diff --git a/AUTHORS b/AUTHORS
index f002fb26..de9f1625 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1382,6 +1382,7 @@
 Zeqin Chen <talonchen@tencent.com>
 Zhang Hao <zhanghao.m@bytedance.com>
 Zhang Hao <15686357310a@gmail.com>
+Zhaoming Jiang <zhaoming.jiang@intel.com>
 Zhaoze Zhou <zhaoze.zhou@partner.samsung.com>
 Zheda Chen <zheda.chen@intel.com>
 Zheng Chuang <zhengchuangscu@gmail.com>
diff --git a/DEPS b/DEPS
index 3c7773d..004b9a6 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': '58e745419425420cd70b87adf89e1bde376b72d6',
+  'skia_revision': 'ccd5d27fd4bee5c9a7bd52d8f81b1ef4913eb4eb',
   # 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': '66d10810dc149686026a90bae9ff1f8089031e32',
+  'v8_revision': '69535c29d7908813c24b0e778b05868b515c7dc8',
   # 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': '45d3155f08ea3f1fa6cb74d4d941ba2d8996fdf3',
+  'angle_revision': '1f8bc491082d1059284183c094e9c7aead4bac81',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -327,7 +327,7 @@
   #
   # Note this revision should be updated with
   # third_party/boringssl/roll_boringssl.py, not roll-dep.
-  'boringssl_revision': '28f96c2686459add7acedcd97cb841030bdda019',
+  'boringssl_revision': '3251ca1f63ff8c9ea760c0046309e93596f6c12b',
   # 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.
@@ -383,7 +383,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': '89d471c2db4f18fefa9531d5fbc94cefc9e92d35',
+  'devtools_frontend_revision': '4b0603be0585b48597644e0767f9767d24695a5c',
   # 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.
@@ -471,7 +471,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'clang_format_revision':    '8b525d2747f2584fc35d8c7e612e66f377858df7',
+  'clang_format_revision':    'f97059df7f8b205064625cdb5f97b56668a125ef',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -770,7 +770,7 @@
 
   'src/clank': {
     'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' +
-    'b45f496bb39114cb1d350e180de027ca1ca45b3d',
+    'ae0ebbba3682d6e0677f277ae9d26370a01f4e9a',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -869,7 +869,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': '411TqVwdqe3Y7GvnGYKByJastAxbED7qxLyyKT_lHN4C',
+          'version': 'ioHALR2SQvuhPr2eGleyEcO42dKdzErjuvRN3VLHqB4C',
         },
       ],
       'dep_type': 'cipd',
@@ -880,7 +880,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': '3FrHUmglDgNjpCOSAQqPQ1QLsA17X5oqLfoYXAMfeMAC',
+          'version': 'L815Hqz0MnnRJ4Wv36tl3QwVcEhlob1-PQudceNK0X0C',
         },
       ],
       'dep_type': 'cipd',
@@ -891,7 +891,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'U_ENwu8oTFufRwc7-fMV_gXjdrbg01_k99gaaFv6TwYC',
+          'version': 'KvsyHw9BY5VU2pxewFOWPcm6xPDf_GnKF5chKsmJoVMC',
         },
       ],
       'dep_type': 'cipd',
@@ -1176,7 +1176,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' + '@' + '7b35a0ad174d809ae704a4165fe7bd7629b3bb75',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '44439a1f8ec90a9be61ac3dc02444f7d55812b35',
       'condition': 'checkout_chromeos',
   },
 
@@ -1204,13 +1204,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '175767738f47a3329b202e3ae63cb572e8ab4304',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '7879da9e9dfc4ee3c44b7a52f446bb52de83a9a1',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + 'd5a6cbbf6830d9db5f3842d17951c4eec1957666',
+      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '36d3a77cc050023459fd304ddf1317058e2675ec',
     'condition': 'checkout_src_internal',
   },
 
@@ -1644,7 +1644,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'e0dd93ef3709f247be7e3ab26a4d3d0aaf8d2898',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '5d0bac17a2cb9779156eaa23f4b019c6bcc3d20c',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1684,7 +1684,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'bs2Q_5MC61CyUsEbpowkt4tABytyCHe7eSbylw4sC3QC',
+              'version': 'jVbxJPYj2eIXMIU3dCVMjSFcpEcwBGbSzKKfgoTM9tIC',
           },
       ],
       'condition': 'checkout_android',
@@ -1789,7 +1789,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@3ff606af6dec38ce5b2fcb5a1f6333497a7e1d08',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@34d970c3d4fc94ce95ac037bdb21caefb62a929f',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1826,10 +1826,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '65fcd99b74d2a432b2b311730e141b09b7f0d153',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '4f3e800b7e1d384622e7d6dfe0ee192b49e0b739',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '6d19e6fefc2b43ce778edb95fdc5af8728101844',
+    Var('webrtc_git') + '/src.git' + '@' + '478f3b786e5d7dacbded7ed4e6346932d32ef0e3',
 
   # 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.
@@ -1899,7 +1899,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b466541105e8ed3b314d16191a71a455a433f326',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@460aec5276861d65d3bb9fc5afb6a472badb49b2',
     'condition': 'checkout_src_internal',
   },
 
@@ -1929,7 +1929,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'pr0vr6pcU_92NTeSDPr7JMbvjlg0dk7XBH5ILatjVNcC',
+        'version': '32N0hJ3-hXAe5cNwsdS_jjqNHrrc5OgaEtUdWJ-M7hsC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1940,7 +1940,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'mNfrWP7HgthhcuJraWA2NQ5QQV8dtPUiwEN3RyV3w2wC',
+        'version': 't23-7MlqzdrMkQ5ppTTXlB3evC46CvwY1gpPtmgCFrsC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index fa4a6b7f..9a9940e 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -779,11 +779,6 @@
              "EnableOobeNetworkScreenSkip",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Enables skipping of network screen.
-BASE_FEATURE(kEnableOobeThemeSelection,
-             "EnableOobeThemeSelection",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Enables showing notification after the password change for SAML users.
 BASE_FEATURE(kEnableSamlNotificationOnPasswordChangeSuccess,
              "EnableSamlNotificationOnPasswordChangeSuccess",
@@ -2893,10 +2888,6 @@
   return base::FeatureList::IsEnabled(kOobeRemoveShutdownButton);
 }
 
-bool IsOobeThemeSelectionEnabled() {
-  return base::FeatureList::IsEnabled(kEnableOobeThemeSelection);
-}
-
 bool IsOobeTouchpadScrollEnabled() {
   return base::FeatureList::IsEnabled(kOobeTouchpadScroll);
 }
diff --git a/ash/frame/caption_buttons/frame_size_button_unittest.cc b/ash/frame/caption_buttons/frame_size_button_unittest.cc
index 9f1a4421..8e95fee 100644
--- a/ash/frame/caption_buttons/frame_size_button_unittest.cc
+++ b/ash/frame/caption_buttons/frame_size_button_unittest.cc
@@ -904,9 +904,16 @@
       chromeos::GetEntryTypeHistogramName(),
       MultitaskMenuEntryType::kFrameSizeButtonLongPress, 1);
 
+  // Check that the accelerator increments the correct bucket.
+  // Create an active window for the toggle menu to work.
+  auto window = CreateTestWindow();
+  PressAndReleaseKey(ui::VKEY_Z, ui::EF_COMMAND_DOWN);
+  histogram_tester.ExpectBucketCount(chromeos::GetEntryTypeHistogramName(),
+                                     MultitaskMenuEntryType::kAccel, 1);
+
   // Check total counts for each histogram to ensure calls aren't counted in
   // multiple buckets.
-  histogram_tester.ExpectTotalCount(chromeos::GetEntryTypeHistogramName(), 2);
+  histogram_tester.ExpectTotalCount(chromeos::GetEntryTypeHistogramName(), 3);
 }
 
 }  // namespace ash
diff --git a/ash/public/cpp/cast_config_controller.h b/ash/public/cpp/cast_config_controller.h
index ee46218..8d4c40e 100644
--- a/ash/public/cpp/cast_config_controller.h
+++ b/ash/public/cpp/cast_config_controller.h
@@ -29,7 +29,6 @@
 
   std::string id;
   std::string name;
-  std::string domain;
 
   // Icon which describes the type of sink media is being routed to.
   SinkIconType sink_icon_type = SinkIconType::kGeneric;
diff --git a/ash/system/cast/tray_cast_unittest.cc b/ash/system/cast/tray_cast_unittest.cc
index 4a85ebc8..78fb9d7a 100644
--- a/ash/system/cast/tray_cast_unittest.cc
+++ b/ash/system/cast/tray_cast_unittest.cc
@@ -100,13 +100,11 @@
     SinkAndRoute device1;
     device1.sink.id = "fake_sink_id_1";
     device1.sink.name = "Sink Name 1";
-    device1.sink.domain = "example.com";
     device1.sink.sink_icon_type = SinkIconType::kCast;
     devices.push_back(device1);
     SinkAndRoute device2;
     device2.sink.id = "fake_sink_id_2";
     device2.sink.name = "Sink Name 2";
-    device2.sink.domain = "example.com";
     device2.sink.sink_icon_type = SinkIconType::kCast;
     devices.push_back(device2);
     detailed_view_->OnDevicesUpdated(devices);
@@ -193,7 +191,6 @@
   SinkAndRoute device;
   device.sink.id = "fake_sink_id_1";
   device.sink.name = "Sink Name 1";
-  device.sink.domain = "example.com";
   device.sink.sink_icon_type = SinkIconType::kCast;
   device.route.id = "fake_route_id_1";
   device.route.title = "Title 1";
@@ -228,7 +225,6 @@
   SinkAndRoute device;
   device.sink.id = "fake_sink_id_1";
   device.sink.name = "Sink Name 1";
-  device.sink.domain = "example.com";
   device.sink.sink_icon_type = SinkIconType::kCast;
   device.route.id = "fake_route_id_1";
   device.route.title = "Title 1";
diff --git a/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.cc b/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.cc
index f81b5d25..ed34a5a 100644
--- a/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.cc
+++ b/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.cc
@@ -45,8 +45,7 @@
       : UnifiedSliderView(views::Button::PressedCallback(),
                           controller,
                           kUnifiedMenuKeyboardBrightnessIcon,
-                          IDS_ASH_STATUS_TRAY_BRIGHTNESS,
-                          true /* readonly*/),
+                          IDS_ASH_STATUS_TRAY_BRIGHTNESS),
         model_(model) {
     if (features::IsRgbKeyboardEnabled() &&
         Shell::Get()->rgb_keyboard_manager()->IsRgbKeyboardSupported()) {
@@ -146,7 +145,18 @@
     float value,
     float old_value,
     views::SliderChangeReason reason) {
-  // This slider is read-only.
+  if (reason != views::SliderChangeReason::kByUser) {
+    return;
+  }
+
+  power_manager::SetBacklightBrightnessRequest request;
+  request.set_percent(value * 100);
+  request.set_transition(
+      power_manager::SetBacklightBrightnessRequest_Transition_FAST);
+  request.set_cause(
+      power_manager::SetBacklightBrightnessRequest_Cause_USER_REQUEST);
+  chromeos::PowerManagerClient::Get()->SetKeyboardBrightness(
+      std::move(request));
 }
 
 }  // namespace ash
diff --git a/ash/wm/multitask_menu_nudge_controller.cc b/ash/wm/multitask_menu_nudge_controller.cc
index a0543f0..41a62eea 100644
--- a/ash/wm/multitask_menu_nudge_controller.cc
+++ b/ash/wm/multitask_menu_nudge_controller.cc
@@ -300,13 +300,10 @@
   const gfx::Rect anchor_bounds_in_screen = anchor_view_->GetBoundsInScreen();
 
   // Reparent the nudge and pulse if necessary.
-  aura::Window* new_root =
-      window_util::GetRootWindowMatching(anchor_bounds_in_screen);
+  aura::Window* new_parent = window_->parent();
   aura::Window* nudge_window = nudge_widget_->GetNativeWindow();
 
-  if (new_root != nudge_window->GetRootWindow()) {
-    const int parent_id = nudge_window->parent()->GetId();
-    aura::Window* new_parent = new_root->GetChildById(parent_id);
+  if (new_parent != nudge_window->parent()) {
     new_parent->AddChild(nudge_window);
     new_parent->layer()->Add(pulse_layer_.get());
   }
diff --git a/ash/wm/multitask_menu_nudge_controller_unittest.cc b/ash/wm/multitask_menu_nudge_controller_unittest.cc
index 535c0b63..24f3eed 100644
--- a/ash/wm/multitask_menu_nudge_controller_unittest.cc
+++ b/ash/wm/multitask_menu_nudge_controller_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/display/display_move_window_util.h"
+#include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -13,9 +14,15 @@
 #include "ash/wm/wm_event.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
+#include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
+#include "chromeos/ui/frame/caption_buttons/frame_size_button.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
+#include "chromeos/ui/frame/multitask_menu/multitask_button.h"
+#include "chromeos/ui/frame/multitask_menu/multitask_menu.h"
 #include "chromeos/ui/wm/features.h"
+#include "ui/views/widget/any_widget_observer.h"
+#include "ui/wm/core/window_util.h"
 
 namespace ash {
 
@@ -108,6 +115,48 @@
   EXPECT_FALSE(GetWidget());
 }
 
+// Tests that there is no crash after floating a window via the multitask menu.
+// Regression test for b/265189622.
+TEST_F(MultitaskMenuNudgeControllerTest,
+       NoCrashAfterFloatingFromMultitaskMenu) {
+  auto window = CreateAppWindow(gfx::Rect(300, 300));
+  ASSERT_FALSE(GetWidget());
+
+  // Maximize the window to show the nudge.
+  const WMEvent maximize_event(WM_EVENT_MAXIMIZE);
+  WindowState::Get(window.get())->OnWMEvent(&maximize_event);
+  ASSERT_TRUE(GetWidget());
+
+  // Float the window from the multitask menu. Floating the window using the
+  // accelerator does not cause the crash mentioned in the bug because the
+  // presence of the multitask menu causes an activation change which leads to
+  // restacking that does not happen otherwise.
+  views::NamedWidgetShownWaiter waiter(
+      views::test::AnyWidgetTestPasskey{},
+      std::string("MultitaskMenuBubbleWidget"));
+  auto* size_button = static_cast<chromeos::FrameSizeButton*>(
+      NonClientFrameViewAsh::Get(window.get())
+          ->GetHeaderView()
+          ->caption_button_container()
+          ->size_button());
+  size_button->ShowMultitaskMenu(
+      chromeos::MultitaskMenuEntryType::kFrameSizeButtonHover);
+  views::WidgetDelegate* delegate =
+      waiter.WaitIfNeededAndGet()->widget_delegate();
+  auto* multitask_menu =
+      static_cast<chromeos::MultitaskMenu*>(delegate->AsDialogDelegate());
+
+  // After floating the window from the multitask menu, there is no crash.
+  GetEventGenerator()->MoveMouseTo(
+      multitask_menu->multitask_menu_view_for_testing()
+          ->float_button_for_testing()
+          ->GetBoundsInScreen()
+          .CenterPoint());
+  GetEventGenerator()->ClickLeftButton();
+  ASSERT_TRUE(WindowState::Get(window.get())->IsFloated());
+  EXPECT_TRUE(GetWidget());
+}
+
 TEST_F(MultitaskMenuNudgeControllerTest, NudgeTimeout) {
   auto window = CreateAppWindow(gfx::Rect(300, 300));
   WindowState::Get(window.get())->Maximize();
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
index a8c0fcf..1f7a1ab 100644
--- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc
+++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -45,15 +45,6 @@
 // Sampling interval parameter, the mean value for intervals between samples.
 std::atomic_size_t g_sampling_interval{kDefaultSamplingIntervalBytes};
 
-void (*g_hooks_install_callback)() = nullptr;
-
-// This will be true if *either* InstallAllocatorHooksOnce or
-// SetHooksInstallerCallback has run. `g_hooks_install_callback` should be
-// invoked when *both* have run, so each of them checks the value and, if it is
-// true, knows that the other function has already run so it's time to invoke
-// the callback.
-std::atomic_bool g_hooks_installed{false};
-
 struct ThreadLocalData {
   // Accumulated bytes towards sample.
   intptr_t accumulated_bytes = 0;
@@ -172,10 +163,6 @@
   DCHECK(!g_mute_hooked_samples);
   g_mute_hooked_samples = true;
 
-  // `g_hooks_install_callback` can't be used with
-  // ScopedMuteHookedSamplesForTesting because there's no way to remove it.
-  DCHECK(!g_hooks_install_callback);
-
   // Make sure hooks have been installed, so that the only order of operations
   // that needs to be handled is Install Hooks -> Remove Hooks For Testing ->
   // Reinstall Hooks.
@@ -226,11 +213,6 @@
   [[maybe_unused]] static bool hook_installed = [] {
     allocator::dispatcher::InstallStandardAllocatorHooks();
 
-    bool expected = false;
-    if (!g_hooks_installed.compare_exchange_strong(expected, true)) {
-      // SetHooksInstallCallback already ran, so run the callback now.
-      g_hooks_install_callback();
-    }
     // The allocator hooks use `g_sampled_address_set` so it had better be
     // initialized.
     DCHECK(g_sampled_addresses_set.load(std::memory_order_acquire));
@@ -239,23 +221,6 @@
 }
 
 // static
-void PoissonAllocationSampler::SetHooksInstallCallback(
-    void (*hooks_install_callback)()) {
-  // `g_hooks_install_callback` can't be used with
-  // ScopedMuteHookedSamplesForTesting because there's no way to remove it.
-  DCHECK(!g_mute_hooked_samples);
-
-  CHECK(!g_hooks_install_callback && hooks_install_callback);
-  g_hooks_install_callback = hooks_install_callback;
-
-  bool expected = false;
-  if (!g_hooks_installed.compare_exchange_strong(expected, true)) {
-    // InstallAllocatorHooksOnce already ran, so run the callback now.
-    g_hooks_install_callback();
-  }
-}
-
-// static
 bool PoissonAllocationSampler::AreHookedSamplesMuted() {
   return g_mute_hooked_samples;
 }
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.h b/base/sampling_heap_profiler/poisson_allocation_sampler.h
index 3a97657..a89591f 100644
--- a/base/sampling_heap_profiler/poisson_allocation_sampler.h
+++ b/base/sampling_heap_profiler/poisson_allocation_sampler.h
@@ -26,7 +26,6 @@
 
 // This singleton class implements Poisson sampling of the incoming allocations
 // stream. It hooks onto base::allocator and base::PartitionAlloc.
-// An extra custom allocator can be hooked via SetHooksInstallCallback method.
 // The only control parameter is sampling interval that controls average value
 // of the sampling intervals. The actual intervals between samples are
 // randomized using Poisson distribution to mitigate patterns in the allocation
@@ -85,16 +84,6 @@
   // reserves a TLS slot.
   static void Init();
 
-  // This is an entry point for plugging in an external allocator.
-  // Profiler will invoke the provided callback upon initialization.
-  // The callback should install hooks onto the corresponding memory allocator
-  // and make them invoke PoissonAllocationSampler::RecordAlloc and
-  // PoissonAllocationSampler::RecordFree upon corresponding allocation events.
-  //
-  // If the method is called after profiler is initialized, the callback
-  // is invoked right away.
-  static void SetHooksInstallCallback(void (*hooks_install_callback)());
-
   void AddSamplesObserver(SamplesObserver*);
 
   // Note: After an observer is removed it is still possible to receive
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index c16e029..63e6089 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-11.20230116.3.1
+11.20230117.0.1
diff --git a/cc/test/paint_op_helper.h b/cc/test/paint_op_helper.h
index 7b35dd75..2339610 100644
--- a/cc/test/paint_op_helper.h
+++ b/cc/test/paint_op_helper.h
@@ -223,11 +223,6 @@
     return str.str();
   }
 
-  template <typename T>
-  static std::string SkiaTypeToString(const T&) {
-    return "<unknown skia type>";
-  }
-
   static std::string SkiaTypeToString(const SkScalar& scalar) {
     return base::StringPrintf("%.3f", scalar);
   }
@@ -262,6 +257,14 @@
         matrix.rc(3, 0), matrix.rc(3, 1), matrix.rc(3, 2), matrix.rc(3, 3));
   }
 
+  static std::string SkiaTypeToString(const SkMatrix& matrix) {
+    return base::StringPrintf(
+        "[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]]",
+        matrix.rc(0, 0), matrix.rc(0, 1), matrix.rc(0, 2), matrix.rc(1, 0),
+        matrix.rc(1, 1), matrix.rc(1, 2), matrix.rc(2, 0), matrix.rc(2, 1),
+        matrix.rc(2, 2));
+  }
+
   static std::string SkiaTypeToString(const SkColor& color) {
     return base::StringPrintf("rgba(%d, %d, %d, %d)", SkColorGetR(color),
                               SkColorGetG(color), SkColorGetB(color),
@@ -426,11 +429,6 @@
     return "SkDrawLooper";
   }
 
-  template <typename T>
-  static std::string EnumToString(T) {
-    return "<unknown enum type>";
-  }
-
   static std::string EnumToString(PaintCanvas::AnnotationType type) {
     switch (type) {
       default:
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 8def7fe9..a94e4bf 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1436,6 +1436,7 @@
       "//components/browser_ui/styles/android:java",
       "//components/browser_ui/util/android:java",
       "//components/browser_ui/widget/android:java",
+      "//components/commerce/core/android:core_java",
       "//components/crash/android:java",
       "//components/embedder_support/android:context_menu_java",
       "//components/embedder_support/android:util_java",
@@ -1510,8 +1511,11 @@
       "$google_play_services_package:google_play_services_gcm_java",
       "$google_play_services_package:google_play_services_iid_java",
       "$google_play_services_package:google_play_services_tasks_java",
+      "//base:base_java",
       "//base:base_java_test_support",
+      "//base:jni_java",
       "//base/test:test_support_java",
+      "//build/android:build_java",
       "//cc:cc_java",
       "//chrome/android:chrome_java",
       "//chrome/android/features/keyboard_accessory/public:public_java",
@@ -1826,8 +1830,6 @@
 
     deps += feed_test_deps
 
-    deps += commerce_subscriptions_java_test_deps
-
     if (enable_printing) {
       deps += [ "//printing:printing_java" ]
     }
@@ -4066,7 +4068,6 @@
     "//chrome/browser/battery/android:jni_headers",
     "//chrome/browser/commerce/merchant_viewer/android:jni_headers",
     "//chrome/browser/commerce/price_tracking/android:jni_headers",
-    "//chrome/browser/commerce/subscriptions/android:jni_headers",
     "//chrome/browser/contextmenu:jni_headers",
     "//chrome/browser/download/android:jni_headers",
     "//chrome/browser/enterprise/util:jni_headers",
diff --git a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
index cf72139..c102f5b 100644
--- a/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
+++ b/chrome/android/features/start_surface/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
@@ -670,7 +670,8 @@
     // two different things, audit the wording usage and see if we can rename this method to
     // setStartSurfaceStateInternal.
     private void setOverviewStateInternal() {
-        if (mStartSurfaceState == StartSurfaceState.SHOWING_HOMEPAGE) {
+        if (mStartSurfaceState == StartSurfaceState.SHOWING_HOMEPAGE
+                || mStartSurfaceState == StartSurfaceState.SHOWING_START) {
             // When entering the Start surface by tapping home button or new tab page, we need to
             // reset the scrolling position.
             resetScrollPosition();
diff --git a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTabSwitcherTest.java b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTabSwitcherTest.java
index 611137f..39fdba4 100644
--- a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTabSwitcherTest.java
+++ b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTabSwitcherTest.java
@@ -22,6 +22,7 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import static org.chromium.chrome.features.start_surface.StartSurfaceTestUtils.INSTANT_START_TEST_BASE_PARAMS;
 import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
@@ -367,7 +368,7 @@
     @CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION,
         INSTANT_START_TEST_BASE_PARAMS,
         FeedPlaceholderLayout.DISABLE_ANIMATION_SWITCH})
-    public void testScrollToSelectedTab() throws IOException {
+    public void testScrollToSelectedTab() throws Exception {
         // clang-format on
         StartSurfaceTestUtils.createTabStateFile(new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, null, 5);
         StartSurfaceTestUtils.startMainActivityFromLauncher(mActivityTestRule);
@@ -388,6 +389,9 @@
                     Assert.assertTrue(v instanceof RecyclerView);
                     LinearLayoutManager layoutManager =
                             (LinearLayoutManager) ((RecyclerView) v).getLayoutManager();
+                    assertEquals(2, layoutManager.findFirstVisibleItemPosition());
+                    assertTrue(layoutManager.isViewPartiallyVisible(
+                            layoutManager.getChildAt(5), false, false));
                     assertEquals(7, layoutManager.findLastVisibleItemPosition());
                 });
 
diff --git a/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java b/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
index 71f48ce..af28bb6f 100644
--- a/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
+++ b/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
@@ -1006,8 +1006,7 @@
 
         assertEquals(StartSurfaceUserData.getInstance().restoreFeedInstanceState(), instanceState);
 
-        mediator.setStartSurfaceState(
-                StartSurfaceState.SHOWING_START, NewTabPageLaunchOrigin.WEB_FEED);
+        mediator.setLaunchOrigin(NewTabPageLaunchOrigin.WEB_FEED);
         assertNull(StartSurfaceUserData.getInstance().restoreFeedInstanceState());
     }
 
@@ -1024,8 +1023,7 @@
 
         assertEquals(StartSurfaceUserData.getInstance().restoreFeedInstanceState(), instanceState);
 
-        mediator.setStartSurfaceState(
-                StartSurfaceState.SHOWING_START, NewTabPageLaunchOrigin.UNKNOWN);
+        mediator.setLaunchOrigin(NewTabPageLaunchOrigin.UNKNOWN);
         assertNotNull(StartSurfaceUserData.getInstance().restoreFeedInstanceState());
     }
 
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 4c508c2..164651b 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -166,6 +166,7 @@
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageService.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java",
+    "java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiMetricsHelper.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/BaselineTabSuggestionProvider.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/StaleTabSuggestionProvider.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContextObserver.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 37fd2c8..3c068fff7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
-import static org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMediator.INITIAL_SCROLL_INDEX_OFFSET_GTS;
-
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -43,6 +41,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorAction.IconPosition;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorAction.ShowMode;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorCoordinator.TabSelectionEditorController;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorOpenMetricGroups;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.tab_ui.R;
@@ -314,11 +313,8 @@
                     mTabSelectionEditorControllerSupplier.get().show(tabs,
                             /*preSelectedTabCount=*/0,
                             v2Enabled ? mRecyclerViewPositionSupplier.get() : null);
-                    if (v2Enabled) {
-                        RecordUserAction.record("TabMultiSelectV2.OpenFromDialog");
-                    } else {
-                        RecordUserAction.record("TabMultiSelect.OpenFromDialog");
-                    }
+                    TabUiMetricsHelper.recordSelectionEditorOpenMetrics(
+                            TabSelectionEditorOpenMetricGroups.OPEN_FROM_DIALOG, mContext);
                 }
             } else if (result == R.id.share_tab_group) {
                 Tab tab = mTabModelSelector.getTabById(mCurrentTabId);
@@ -537,8 +533,7 @@
         }
         List<Tab> relatedTabs = getRelatedTabs(mCurrentTabId);
         Tab currentTab = mTabModelSelector.getTabById(mCurrentTabId);
-        int initialPosition =
-                Math.max(relatedTabs.indexOf(currentTab) - INITIAL_SCROLL_INDEX_OFFSET_GTS, 0);
+        int initialPosition = relatedTabs.indexOf(currentTab);
         mModel.set(TabGridPanelProperties.INITIAL_SCROLL_INDEX, initialPosition);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinder.java
index 2d00bce..07b2d0a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinder.java
@@ -34,9 +34,10 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
+import org.chromium.chrome.browser.tab.TabUtils;
 import org.chromium.ui.base.ViewUtils;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -127,8 +128,13 @@
             }
         } else if (INITIAL_SCROLL_INDEX == propertyKey) {
             int index = (Integer) model.get(INITIAL_SCROLL_INDEX);
-            ((LinearLayoutManager) viewHolder.contentView.getLayoutManager())
-                    .scrollToPositionWithOffset(index, 0);
+            RecyclerView view = viewHolder.contentView;
+            if (view.getWidth() == 0 || view.getHeight() == 0) {
+                // If layout hasn't happened post the scroll index change until layout happens.
+                view.post(() -> setScrollIndex(view, index));
+                return;
+            }
+            setScrollIndex(viewHolder.contentView, index);
         } else if (IS_MAIN_CONTENT_VISIBLE == propertyKey) {
             viewHolder.contentView.setVisibility(View.VISIBLE);
         } else if (MENU_CLICK_LISTENER == propertyKey) {
@@ -165,4 +171,18 @@
             }
         }
     }
+
+    private static void setScrollIndex(RecyclerView view, int index) {
+        GridLayoutManager layoutManager = (GridLayoutManager) view.getLayoutManager();
+        int offset = computeOffset(view, layoutManager);
+        layoutManager.scrollToPositionWithOffset(index, offset);
+    }
+
+    private static int computeOffset(View view, GridLayoutManager layoutManager) {
+        int width = view.getWidth();
+        int height = view.getHeight();
+        int cardWidth = width / layoutManager.getSpanCount();
+        int cardHeight = TabUtils.deriveGridCardHeight(cardWidth, view.getContext());
+        return height / 2 - cardHeight / 2;
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java
index 93e10b58..aa329c5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java
@@ -40,7 +40,13 @@
     public static final PropertyModel.WritableIntPropertyKey BOTTOM_PADDING =
             new PropertyModel.WritableIntPropertyKey();
 
+    /**
+     * Same as {@link TabListCoordinator.TabListMode}.
+     */
+    public static final PropertyModel.WritableIntPropertyKey MODE =
+            new PropertyModel.WritableIntPropertyKey();
+
     public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {IS_VISIBLE, IS_INCOGNITO,
             VISIBILITY_LISTENER, INITIAL_SCROLL_INDEX, ANIMATE_VISIBILITY_CHANGES, TOP_MARGIN,
-            BOTTOM_CONTROLS_HEIGHT, SHADOW_TOP_OFFSET, BOTTOM_PADDING};
+            BOTTOM_CONTROLS_HEIGHT, SHADOW_TOP_OFFSET, BOTTOM_PADDING, MODE};
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
index 63c59408..b8788c9b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
@@ -10,14 +10,20 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.INITIAL_SCROLL_INDEX;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.IS_INCOGNITO;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.IS_VISIBLE;
+import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.MODE;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.SHADOW_TOP_OFFSET;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.TOP_MARGIN;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.VISIBILITY_LISTENER;
 
+import android.app.Activity;
+import android.graphics.Rect;
 import android.widget.FrameLayout;
 
+import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.LinearLayoutManager;
 
+import org.chromium.chrome.browser.tab.TabUtils;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.ui.base.ViewUtils;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -47,9 +53,11 @@
         } else if (VISIBILITY_LISTENER == propertyKey) {
             view.setVisibilityListener(model.get(VISIBILITY_LISTENER));
         } else if (INITIAL_SCROLL_INDEX == propertyKey) {
-            // RecyclerView#scrollToPosition(int) behaves incorrectly first time after cold start.
             int index = (Integer) model.get(INITIAL_SCROLL_INDEX);
-            ((LinearLayoutManager) view.getLayoutManager()).scrollToPositionWithOffset(index, 0);
+            int offset = computeOffset(view, model);
+            // RecyclerView#scrollToPosition(int) behaves incorrectly first time after cold start.
+            ((LinearLayoutManager) view.getLayoutManager())
+                    .scrollToPositionWithOffset(index, offset);
         } else if (TOP_MARGIN == propertyKey) {
             FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams();
             final int newTopMargin = model.get(TOP_MARGIN);
@@ -67,4 +75,52 @@
             view.setBottomPadding(model.get(BOTTOM_PADDING));
         }
     }
+
+    private static int computeOffset(TabListRecyclerView view, PropertyModel model) {
+        int width = view.getWidth();
+        int height = view.getHeight();
+        // If layout hasn't happened yet fallback to dimensions based on visible display frame. This
+        // works for multi-window and different orientations. Don't use View#post() because this
+        // will cause animation jank for expand/shrink animations.
+        if (width == 0 || height == 0) {
+            Rect frame = new Rect();
+            ((Activity) view.getContext())
+                    .getWindow()
+                    .getDecorView()
+                    .getWindowVisibleDisplayFrame(frame);
+            width = frame.width();
+            // Remove toolbar height from height.
+            height = frame.height()
+                    - view.getContext().getResources().getDimensionPixelSize(
+                            R.dimen.toolbar_height_no_shadow);
+        }
+        if (width <= 0 || height <= 0) return 0;
+
+        @TabListCoordinator.TabListMode
+        int mode = model.get(MODE);
+        LinearLayoutManager layoutManager = (LinearLayoutManager) view.getLayoutManager();
+        if (mode == TabListCoordinator.TabListMode.GRID) {
+            GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
+            int cardWidth = width / gridLayoutManager.getSpanCount();
+            int cardHeight = TabUtils.deriveGridCardHeight(cardWidth, view.getContext());
+            return Math.max(0, height / 2 - cardHeight / 2);
+        }
+        if (mode == TabListCoordinator.TabListMode.CAROUSEL) {
+            return Math.max(0,
+                    width / 2
+                            - view.getContext().getResources().getDimensionPixelSize(
+                                      R.dimen.tab_carousel_card_width)
+                                    / 2);
+        }
+        if (mode == TabListCoordinator.TabListMode.LIST) {
+            // Avoid divide by 0 when there are no tabs.
+            if (layoutManager.getItemCount() == 0) return 0;
+
+            return Math.max(0,
+                    height / 2
+                            - view.computeVerticalScrollRange() / layoutManager.getItemCount() / 2);
+        }
+        assert false : "Unexpected MODE when setting INITIAL_SCROLL_INDEX.";
+        return 0;
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index e0b42f7..81c3a54 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -72,6 +72,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider.TabFavicon;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMediator.PriceWelcomeMessageController;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorActionMetricGroups;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
@@ -438,9 +439,11 @@
             if (index == TabModel.INVALID_TAB_INDEX) return;
             boolean selected = mModel.get(index).model.get(TabProperties.IS_SELECTED);
             if (selected) {
-                RecordUserAction.record("TabMultiSelect.TabUnselected");
+                TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                        TabSelectionEditorActionMetricGroups.UNSELECTED);
             } else {
-                RecordUserAction.record("TabMultiSelect.TabSelected");
+                TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                        TabSelectionEditorActionMetricGroups.SELECTED);
             }
             mModel.get(index).model.set(TabProperties.IS_SELECTED, !selected);
             // Reset thumbnail to ensure the color of the blank tab slots is correct.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java
index c82407e..d54b92c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorAction.java
@@ -14,11 +14,11 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ObserverList;
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorExitMetricGroups;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -259,7 +259,9 @@
         };
         if (shouldHideEditorAfterAction()) {
             mActionDelegate.hideByAction();
-            RecordUserAction.record("TabMultiSelectV2.ClosedAutomatically");
+            TabUiMetricsHelper.recordSelectionEditorExitMetrics(
+                    TabSelectionEditorExitMetricGroups.CLOSED_AUTOMATICALLY,
+                    tabs.get(0).getContext());
         }
         return true;
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProvider.java
index 2165d67..98a5310f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionProvider.java
@@ -6,12 +6,12 @@
 
 import androidx.annotation.IntDef;
 
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorActionMetricGroups;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -85,8 +85,8 @@
                         selectedTabs, destinationTab, false, true);
                 mTabSelectionEditorController.hide();
 
-                RecordUserAction.record("TabMultiSelect.Done");
-                RecordUserAction.record("TabGroup.Created.TabMultiSelect");
+                TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                        TabSelectionEditorActionMetricGroups.PROVIDER_GROUP);
                 break;
             case TabSelectionEditorAction.UNGROUP:
                 TabGroupModelFilter filter =
@@ -96,7 +96,8 @@
                     filter.moveTabOutOfGroup(tab.getId());
                 }
                 mTabSelectionEditorController.hide();
-                RecordUserAction.record("TabGridDialog.RemoveFromGroup.TabMultiSelect");
+                TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                        TabSelectionEditorActionMetricGroups.PROVIDER_UNGROUP);
                 break;
             case TabSelectionEditorAction.CLOSE:
                 tabModelSelector.getCurrentModel().closeMultipleTabs(selectedTabs, true);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorBookmarkAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorBookmarkAction.java
index 9b13e96..2160e02 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorBookmarkAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorBookmarkAction.java
@@ -10,11 +10,11 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.content.res.AppCompatResources;
 
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.bookmarks.BookmarkModel;
 import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorActionMetricGroups;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.tab_ui.R;
 
@@ -98,7 +98,8 @@
             assert snackbarManager != null;
             mDelegate.bookmarkTabsAndShowSnackbar(mActivity, tabs, snackbarManager);
         }
-        RecordUserAction.record("TabMultiSelectV2.BookmarkTabs");
+        TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                TabSelectionEditorActionMetricGroups.BOOKMARK);
         return true;
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCloseAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCloseAction.java
index eed654a5..6e96fc68 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCloseAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCloseAction.java
@@ -9,8 +9,8 @@
 
 import androidx.appcompat.content.res.AppCompatResources;
 
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorActionMetricGroups;
 import org.chromium.chrome.tab_ui.R;
 
 import java.util.List;
@@ -57,7 +57,8 @@
         } else {
             getTabModelSelector().getCurrentModel().closeMultipleTabs(tabs, true);
         }
-        RecordUserAction.record("TabMultiSelectV2.CloseTabs");
+        TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                TabSelectionEditorActionMetricGroups.CLOSE);
         return true;
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
index 4fdb64e..76bfda8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
@@ -20,7 +20,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
@@ -28,6 +27,7 @@
 import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
 import org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView.RecyclerViewPosition;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorExitMetricGroups;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
@@ -148,11 +148,8 @@
          * Defines what to do when the navigation button is clicked.
          */
         public void goBack() {
-            if (TabUiFeatureUtilities.isTabSelectionEditorV2Enabled(mContext)) {
-                RecordUserAction.record("TabMultiSelectV2.ClosedByUser");
-            } else {
-                RecordUserAction.record("TabMultiSelect.Cancelled");
-            }
+            TabUiMetricsHelper.recordSelectionEditorExitMetrics(
+                    TabSelectionEditorExitMetricGroups.CLOSED_BY_USER, mContext);
             mTabSelectionEditorController.hide();
         }
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorGroupAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorGroupAction.java
index d9e23af..25a7c01 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorGroupAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorGroupAction.java
@@ -9,11 +9,11 @@
 
 import androidx.appcompat.content.res.AppCompatResources;
 
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorActionMetricGroups;
 import org.chromium.chrome.tab_ui.R;
 
 import java.util.ArrayList;
@@ -86,8 +86,8 @@
         tabGroupModelFilter.mergeListOfTabsToGroup(
                 sortedTabs, destinationTab, /*isSameGroup=*/true, /*notify=*/true);
 
-        RecordUserAction.record("TabMultiSelectV2.GroupTabs");
-        RecordUserAction.record("TabGroup.Created.TabMultiSelect");
+        TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                TabSelectionEditorActionMetricGroups.GROUP);
         return true;
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
index 4a6411fca..0e388a6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMediator.java
@@ -11,8 +11,6 @@
 import androidx.annotation.ColorInt;
 import androidx.annotation.Nullable;
 
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -26,6 +24,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tasks.tab_management.TabListRecyclerView.RecyclerViewPosition;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorCoordinator.TabSelectionEditorNavigationProvider;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorExitMetricGroups;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
@@ -68,12 +67,6 @@
     private SnackbarManager mSnackbarManager;
     private TabSelectionEditorLayout mTabSelectionEditorLayout;
 
-    /**
-     * The last time the Tab Selection Editor was shown across all instances, null if never shown
-     * before within an activity lifespan.
-     */
-    private static Long sLastShownTimestampMillis;
-
     private final View.OnClickListener mNavigationClickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -202,7 +195,9 @@
             @Nullable RecyclerViewPosition recyclerViewPosition) {
         // Reparent the snackbarManager to use the selection editor layout to avoid layering issues.
         mSnackbarManager.setParentView(mTabSelectionEditorLayout);
-        recordTimeSinceLastShown();
+        // Records to a histogram the time since an instance of TabSelectionEditor was last opened
+        // within an activity lifespan.
+        TabUiMetricsHelper.recordEditorTimeSinceLastShownHistogram();
         // We don't call TabListCoordinator#prepareTabSwitcherView, since not all the logic (e.g.
         // requiring one tab to be selected) is applicable here.
         mTabListCoordinator.prepareTabGridView();
@@ -322,10 +317,8 @@
     private void hideInternal(boolean hiddenByAction) {
         if (!isEditorVisible()) return;
         mSnackbarManager.setParentView(null);
-
-        if (TabUiFeatureUtilities.isTabSelectionEditorV2Enabled(mContext)) {
-            RecordUserAction.record("TabMultiSelectV2.Closed");
-        }
+        TabUiMetricsHelper.recordSelectionEditorExitMetrics(
+                TabSelectionEditorExitMetricGroups.CLOSED, mContext);
 
         // When hiding by action it is expected that syncRecyclerViewPosition() is called before the
         // action occurs. This is because an action may remove tabs so sync position must happen
@@ -388,17 +381,4 @@
             mTabModelSelector.removeObserver(mTabModelSelectorObserver);
         }
     }
-
-    /**
-     * Records to a historgam the time since an instance of TabSelectionEditor was last opened
-     * within an activity lifespan.
-     */
-    private void recordTimeSinceLastShown() {
-        long timestampMillis = System.currentTimeMillis();
-        if (sLastShownTimestampMillis != null) {
-            RecordHistogram.recordTimesHistogram("Android.TabMultiSelectV2.TimeSinceLastShown",
-                    timestampMillis - sLastShownTimestampMillis);
-        }
-        sLastShownTimestampMillis = timestampMillis;
-    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java
index 5bf36a2..5e130f3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorSelectionAction.java
@@ -11,8 +11,8 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.content.res.AppCompatResources;
 
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorActionMetricGroups;
 import org.chromium.chrome.tab_ui.R;
 
 import java.lang.annotation.Retention;
@@ -85,10 +85,12 @@
     public boolean performAction(List<Tab> tabs) {
         if (mActionState == ActionState.SELECT_ALL) {
             getActionDelegate().selectAll();
-            RecordUserAction.record("TabMultiSelectV2.SelectAll");
+            TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                    TabSelectionEditorActionMetricGroups.SELECT_ALL);
         } else if (mActionState == ActionState.DESELECT_ALL) {
             getActionDelegate().deselectAll();
-            RecordUserAction.record("TabMultiSelectV2.DeselectAll");
+            TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                    TabSelectionEditorActionMetricGroups.DESELECT_ALL);
         } else {
             assert false : "Invalid selection state";
         }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareAction.java
index 5a08b0e2..78206ff9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareAction.java
@@ -18,12 +18,11 @@
 import androidx.appcompat.content.res.AppCompatResources;
 
 import org.chromium.base.Callback;
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabList;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorActionMetricGroups;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.share.ShareImageFileUtils;
 import org.chromium.components.browser_ui.share.ShareParams;
@@ -109,7 +108,8 @@
         List<Integer> sortedTabIndexList = filterTabs(tabs, tabList);
 
         if (sortedTabIndexList.size() == 0) {
-            logShareActionState(TabSelectionEditorShareActionState.ALL_TABS_FILTERED);
+            TabUiMetricsHelper.recordShareStateHistogram(
+                    TabSelectionEditorShareActionState.ALL_TABS_FILTERED);
             return false;
         }
 
@@ -120,8 +120,9 @@
                 isOnlyOneTab ? tabList.getTabAt(sortedTabIndexList.get(0)).getTitle() : "";
         String tabUrl =
                 isOnlyOneTab ? tabList.getTabAt(sortedTabIndexList.get(0)).getUrl().getSpec() : "";
-        String userAction = isOnlyOneTab ? "TabMultiSelectV2.SharedTabAsTextList"
-                                         : "TabMultiSelectV2.SharedTabsListAsTextList";
+        @TabSelectionEditorActionMetricGroups
+        int actionId = isOnlyOneTab ? TabSelectionEditorActionMetricGroups.SHARE_TAB
+                                    : TabSelectionEditorActionMetricGroups.SHARE_TABS;
 
         ShareParams shareParams =
                 new ShareParams
@@ -131,7 +132,7 @@
                         .setCallback(new ShareParams.TargetChosenCallback() {
                             @Override
                             public void onTargetChosen(ComponentName chosenComponent) {
-                                RecordUserAction.record(userAction);
+                                TabUiMetricsHelper.recordSelectionEditorActionMetrics(actionId);
                             }
 
                             @Override
@@ -179,7 +180,8 @@
                         PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
                             shareIntent.setClipData(ClipData.newRawUri("", uri));
                             mContext.startActivity(Intent.createChooser(shareIntent, null));
-                            logShareActionState(TabSelectionEditorShareActionState.SUCCESS);
+                            TabUiMetricsHelper.recordShareStateHistogram(
+                                    TabSelectionEditorShareActionState.SUCCESS);
                         });
 
                         if (sIntentCallbackForTesting != null) {
@@ -221,11 +223,6 @@
         return sb.toString();
     }
 
-    private static void logShareActionState(@TabSelectionEditorShareActionState int action) {
-        RecordHistogram.recordEnumeratedHistogram("Android.TabMultiSelectV2.SharingState", action,
-                TabSelectionEditorShareActionState.NUM_ENTRIES);
-    }
-
     private boolean shouldFilterUrl(GURL url) {
         if (mSkipUrlCheckForTesting) return false;
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorUngroupAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorUngroupAction.java
index c80e886..d4f1a3e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorUngroupAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorUngroupAction.java
@@ -9,9 +9,9 @@
 
 import androidx.appcompat.content.res.AppCompatResources;
 
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorActionMetricGroups;
 import org.chromium.chrome.tab_ui.R;
 
 import java.util.List;
@@ -63,8 +63,8 @@
         for (Tab tab : tabs) {
             filter.moveTabOutOfGroup(tab.getId());
         }
-        RecordUserAction.record("TabMultiSelectV2.UngroupTabs");
-        RecordUserAction.record("TabGridDialog.RemoveFromGroup.TabMultiSelect");
+        TabUiMetricsHelper.recordSelectionEditorActionMetrics(
+                TabSelectionEditorActionMetricGroups.UNGROUP);
         return true;
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 775b937..9c6aeaf41 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -53,6 +53,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorAction.ShowMode;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorCoordinator.TabSelectionEditorController;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorCoordinator.TabSelectionEditorNavigationProvider;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabSelectionEditorOpenMetricGroups;
 import org.chromium.chrome.browser.tasks.tab_management.suggestions.TabSuggestionsOrchestrator;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
@@ -534,7 +535,8 @@
         }
         mTabSelectionEditorCoordinator.getController().show(
                 tabs, /*preSelectedTabCount=*/0, mTabListCoordinator.getRecyclerViewPosition());
-        RecordUserAction.record("TabMultiSelectV2.OpenFromGrid");
+        TabUiMetricsHelper.recordSelectionEditorOpenMetrics(
+                TabSelectionEditorOpenMetricGroups.OPEN_FROM_GRID, mActivity);
     }
 
     private void setUpPriceTracking(Context context, ModalDialogManager modalDialogManager) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
index 0cc31e4..34bf7db 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
@@ -10,6 +10,7 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.INITIAL_SCROLL_INDEX;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.IS_INCOGNITO;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.IS_VISIBLE;
+import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.MODE;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.SHADOW_TOP_OFFSET;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.TOP_MARGIN;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.VISIBILITY_LISTENER;
@@ -81,11 +82,6 @@
                                      TabSwitcherCustomViewManager.Delegate, BackPressHandler {
     private static final String TAG = "TabSwitcherMediator";
 
-    // This should be the same as TabListCoordinator.GRID_LAYOUT_SPAN_COUNT for the selected tab
-    // to be on the 2nd row.
-    static final int INITIAL_SCROLL_INDEX_OFFSET_GTS = 2;
-    static final int INITIAL_SCROLL_INDEX_OFFSET_CAROUSEL = 1;
-
     private static final int DEFAULT_TOP_PADDING = 0;
 
     // Count histograms for tab counts when showing switcher.
@@ -294,6 +290,7 @@
         mBrowserControlsStateProvider = browserControlsStateProvider;
         mMultiWindowModeStateDispatcher = multiWindowModeStateDispatcher;
         mMode = mode;
+        mContainerViewModel.set(MODE, mode);
         mContext = context;
 
         if (incognitoReauthControllerSupplier != null) {
@@ -761,12 +758,9 @@
     }
 
     private void setInitialScrollIndexOffset() {
-        int offset = mMode == TabListMode.CAROUSEL ? INITIAL_SCROLL_INDEX_OFFSET_CAROUSEL
-                                                   : INITIAL_SCROLL_INDEX_OFFSET_GTS;
-        int initialPosition = Math.max(
-                mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter().index()
-                        - offset,
-                0);
+        int initialPosition =
+                mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter().index();
+
         // In MRU order, selected Tab is always at the first position.
         if (mShowTabsInMruOrder) initialPosition = 0;
         mContainerViewModel.set(INITIAL_SCROLL_INDEX, initialPosition);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiMetricsHelper.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiMetricsHelper.java
new file mode 100644
index 0000000..95b59dd7
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiMetricsHelper.java
@@ -0,0 +1,207 @@
+// 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.chrome.browser.tasks.tab_management;
+
+import android.content.Context;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorShareAction.TabSelectionEditorShareActionState;
+
+/**
+ * Metrics helper class for the Tab UI module.
+ */
+public class TabUiMetricsHelper {
+    /**
+     * The last time the Tab Selection Editor was shown across all instances, null if never shown
+     * before within an activity lifespan.
+     */
+    private static Long sLastShownTimestampMillis;
+
+    // These values are persisted to logs. Entries should not be renumbered and
+    // numeric values should never be reused.
+    @IntDef({TabSelectionEditorActionMetricGroups.BOOKMARK,
+            TabSelectionEditorActionMetricGroups.CLOSE, TabSelectionEditorActionMetricGroups.GROUP,
+            TabSelectionEditorActionMetricGroups.SELECT_ALL,
+            TabSelectionEditorActionMetricGroups.DESELECT_ALL,
+            TabSelectionEditorActionMetricGroups.SHARE_TAB,
+            TabSelectionEditorActionMetricGroups.SHARE_TABS,
+            TabSelectionEditorActionMetricGroups.UNGROUP,
+            TabSelectionEditorActionMetricGroups.PROVIDER_GROUP,
+            TabSelectionEditorActionMetricGroups.PROVIDER_UNGROUP,
+            TabSelectionEditorActionMetricGroups.UNSELECTED,
+            TabSelectionEditorActionMetricGroups.SELECTED})
+    public @interface TabSelectionEditorActionMetricGroups {
+        int BOOKMARK = 0;
+        int CLOSE = 1;
+        int GROUP = 2;
+        int SELECT_ALL = 3;
+        int DESELECT_ALL = 4;
+        int SHARE_TAB = 5;
+        int SHARE_TABS = 6;
+        int UNGROUP = 7;
+        int PROVIDER_GROUP = 8;
+        int PROVIDER_UNGROUP = 9;
+        int UNSELECTED = 10;
+        int SELECTED = 11;
+    }
+
+    // These values are persisted to logs. Entries should not be renumbered and
+    // numeric values should never be reused.
+    @IntDef({TabSelectionEditorExitMetricGroups.CLOSED,
+            TabSelectionEditorExitMetricGroups.CLOSED_AUTOMATICALLY,
+            TabSelectionEditorExitMetricGroups.CLOSED_BY_USER})
+    public @interface TabSelectionEditorExitMetricGroups {
+        int CLOSED = 0;
+        int CLOSED_AUTOMATICALLY = 1;
+        int CLOSED_BY_USER = 2;
+    }
+
+    // These values are persisted to logs. Entries should not be renumbered and
+    // numeric values should never be reused.
+    @IntDef({TabSelectionEditorOpenMetricGroups.OPEN_FROM_GRID,
+            TabSelectionEditorOpenMetricGroups.OPEN_FROM_DIALOG})
+    public @interface TabSelectionEditorOpenMetricGroups {
+        int OPEN_FROM_GRID = 0;
+        int OPEN_FROM_DIALOG = 1;
+    }
+
+    // Histograms
+    public static void recordEditorTimeSinceLastShownHistogram() {
+        long timestampMillis = System.currentTimeMillis();
+        if (sLastShownTimestampMillis != null) {
+            RecordHistogram.recordTimesHistogram("Android.TabMultiSelectV2.TimeSinceLastShown",
+                    timestampMillis - sLastShownTimestampMillis);
+        }
+        sLastShownTimestampMillis = timestampMillis;
+    }
+
+    public static void recordShareStateHistogram(@TabSelectionEditorShareActionState int action) {
+        RecordHistogram.recordEnumeratedHistogram("Android.TabMultiSelectV2.SharingState", action,
+                TabSelectionEditorShareActionState.NUM_ENTRIES);
+    }
+
+    // Metrics
+    public static void recordSelectionEditorActionMetrics(
+            @TabSelectionEditorActionMetricGroups int actionId) {
+        switch (actionId) {
+            case TabSelectionEditorActionMetricGroups.BOOKMARK:
+                RecordUserAction.record("TabMultiSelectV2.BookmarkTabs");
+                break;
+            case TabSelectionEditorActionMetricGroups.CLOSE:
+                RecordUserAction.record("TabMultiSelectV2.CloseTabs");
+                break;
+            case TabSelectionEditorActionMetricGroups.GROUP:
+                RecordUserAction.record("TabMultiSelectV2.GroupTabs");
+                RecordUserAction.record("TabGroup.Created.TabMultiSelect");
+                break;
+            case TabSelectionEditorActionMetricGroups.SELECT_ALL:
+                RecordUserAction.record("TabMultiSelectV2.SelectAll");
+                break;
+            case TabSelectionEditorActionMetricGroups.DESELECT_ALL:
+                RecordUserAction.record("TabMultiSelectV2.DeselectAll");
+                break;
+            case TabSelectionEditorActionMetricGroups.SHARE_TAB:
+                RecordUserAction.record("TabMultiSelectV2.SharedTabAsTextList");
+                break;
+            case TabSelectionEditorActionMetricGroups.SHARE_TABS:
+                RecordUserAction.record("TabMultiSelectV2.SharedTabsListAsTextList");
+                break;
+            case TabSelectionEditorActionMetricGroups.UNGROUP:
+                RecordUserAction.record("TabMultiSelectV2.UngroupTabs");
+                RecordUserAction.record("TabGridDialog.RemoveFromGroup.TabMultiSelect");
+                break;
+            case TabSelectionEditorActionMetricGroups.PROVIDER_GROUP:
+                RecordUserAction.record("TabMultiSelect.Done");
+                RecordUserAction.record("TabGroup.Created.TabMultiSelect");
+                break;
+            case TabSelectionEditorActionMetricGroups.PROVIDER_UNGROUP:
+                RecordUserAction.record("TabGridDialog.RemoveFromGroup.TabMultiSelect");
+                break;
+            case TabSelectionEditorActionMetricGroups.UNSELECTED:
+                RecordUserAction.record("TabMultiSelect.TabUnselected");
+                break;
+            case TabSelectionEditorActionMetricGroups.SELECTED:
+                RecordUserAction.record("TabMultiSelect.TabSelected");
+                break;
+            default:
+                assert false : "Unexpected TabSelectionEditorActionMetricGroups value " + actionId
+                               + " when calling recordSelectionEditorActionMetrics.";
+        }
+    }
+
+    public static void recordSelectionEditorExitMetrics(
+            @TabSelectionEditorExitMetricGroups int actionId, Context context) {
+        if (TabUiFeatureUtilities.isTabSelectionEditorV2Enabled(context)) {
+            switch (actionId) {
+                case TabSelectionEditorExitMetricGroups.CLOSED:
+                    RecordUserAction.record("TabMultiSelectV2.Closed");
+                    break;
+                case TabSelectionEditorExitMetricGroups.CLOSED_AUTOMATICALLY:
+                    RecordUserAction.record("TabMultiSelectV2.ClosedAutomatically");
+                    break;
+                case TabSelectionEditorExitMetricGroups.CLOSED_BY_USER:
+                    RecordUserAction.record("TabMultiSelectV2.ClosedByUser");
+                    break;
+                default:
+                    assert false
+                        : "Unexpected TabSelectionEditorExitMetricGroups value of "
+                          + actionId
+                          + " when calling recordSelectionEditorExitMetrics with V2 enabled.";
+            }
+        } else {
+            switch (actionId) {
+                case TabSelectionEditorExitMetricGroups.CLOSED:
+                    // Since the equivalent metric is not recorded for V1, it will result in a
+                    // no-op.
+                    break;
+                case TabSelectionEditorExitMetricGroups.CLOSED_AUTOMATICALLY:
+                    // Since the equivalent metric is not recorded for V1, it will result in a
+                    // no-op.
+                    break;
+                case TabSelectionEditorExitMetricGroups.CLOSED_BY_USER:
+                    RecordUserAction.record("TabMultiSelect.Cancelled");
+                    break;
+                default:
+                    assert false
+                        : "Unexpected TabSelectionEditorExitMetricGroups value of "
+                          + actionId
+                          + " when calling recordSelectionEditorExitMetrics with V2 disabled.";
+            }
+        }
+    }
+
+    public static void recordSelectionEditorOpenMetrics(
+            @TabSelectionEditorOpenMetricGroups int actionId, Context context) {
+        if (TabUiFeatureUtilities.isTabSelectionEditorV2Enabled(context)) {
+            switch (actionId) {
+                case TabSelectionEditorOpenMetricGroups.OPEN_FROM_GRID:
+                    RecordUserAction.record("TabMultiSelectV2.OpenFromGrid");
+                    break;
+                case TabSelectionEditorOpenMetricGroups.OPEN_FROM_DIALOG:
+                    RecordUserAction.record("TabMultiSelectV2.OpenFromDialog");
+                    break;
+                default:
+                    assert false
+                        : "Unexpected TabSelectionEditorOpenMetricGroups value of "
+                          + actionId
+                          + " when calling recordSelectionEditorOpenMetrics with V2 enabled.";
+            }
+        } else {
+            switch (actionId) {
+                case TabSelectionEditorOpenMetricGroups.OPEN_FROM_DIALOG:
+                    RecordUserAction.record("TabMultiSelect.OpenFromDialog");
+                    break;
+                default:
+                    assert false
+                        : "Unexpected TabSelectionEditorOpenMetricGroups value of "
+                          + actionId
+                          + " when calling recordSelectionEditorOpenMetrics with V2 disabled.";
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinderTest.java
index 0d64711..8ce7dd44b 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridPanelViewBinderTest.java
@@ -4,6 +4,15 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.lessThan;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.hamcrest.MockitoHamcrest.intThat;
+
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.areAnimatorsEnabled;
 
 import android.content.res.ColorStateList;
@@ -31,6 +40,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.UiThreadTest;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.chrome.browser.tasks.tab_management.TabGridDialogView.VisibilityListener;
@@ -50,6 +60,7 @@
  * Tests for {@link TabGridPanelViewBinder}.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.PER_CLASS)
 public class TabGridPanelViewBinderTest extends BlankUiTestActivityTestCase {
     private static final String TAG = "TGPVBT";
     private static final int CONTENT_TOP_MARGIN = 56;
@@ -64,6 +75,7 @@
     private EditText mTitleTextView;
     private View mMainContent;
     private ScrimCoordinator mScrimCoordinator;
+    private GridLayoutManager mLayoutManager;
 
     @Override
     public void setUpTest() throws Exception {
@@ -74,7 +86,8 @@
             mContentView =
                     (TabListRecyclerView) LayoutInflater.from(getActivity())
                             .inflate(R.layout.tab_list_recycler_view_layout, parentView, false);
-            mContentView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
+            mLayoutManager = spy(new GridLayoutManager(getActivity(), 2));
+            mContentView.setLayoutManager(mLayoutManager);
             mToolbarView = (TabGroupUiToolbarView) LayoutInflater.from(getActivity())
                                    .inflate(R.layout.bottom_tab_grid_toolbar, mContentView, false);
             LayoutInflater.from(getActivity())
@@ -468,6 +481,19 @@
         Assert.assertNotNull(mTabGridDialogView.getVisibilityListenerForTesting());
     }
 
+    @Test
+    @SmallTest
+    @UiThreadTest
+    public void testSetInitialScrollIndex() {
+        mContentView.layout(0, 0, 100, 500);
+
+        mModel.set(TabGridPanelProperties.INITIAL_SCROLL_INDEX, 5);
+
+        verify(mLayoutManager, times(1))
+                .scrollToPositionWithOffset(eq(5),
+                        intThat(allOf(lessThan(mContentView.getHeight() / 2), greaterThan(0))));
+    }
+
     @Override
     public void tearDownTest() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(mMCP::destroy);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderTest.java
index d7e42cbc..45b8f67 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderTest.java
@@ -6,8 +6,17 @@
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.lessThan;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.hamcrest.MockitoHamcrest.intThat;
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.areAnimatorsEnabled;
 
@@ -16,6 +25,8 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.test.filters.MediumTest;
 
 import org.hamcrest.Matchers;
@@ -24,10 +35,12 @@
 import org.junit.Test;
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
+import org.mockito.Spy;
 
 import org.chromium.base.CommandLine;
 import org.chromium.base.MathUtils;
 import org.chromium.base.test.UiThreadTest;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
@@ -49,6 +62,7 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Features.EnableFeatures({ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID})
+@Batch(Batch.PER_CLASS)
 public class TabListContainerViewBinderTest extends BlankUiTestActivityTestCase {
     /**
      * BlankUiTestActivityTestCase also needs {@link ChromeFeatureList}'s
@@ -62,7 +76,12 @@
     private static final int INCREASED_CONTAINER_HEIGHT = 76;
     private PropertyModel mContainerModel;
     private PropertyModelChangeProcessor mMCP;
+    @Spy
     private TabListRecyclerView mRecyclerView;
+    @Spy
+    private GridLayoutManager mGridLayoutManager;
+    @Spy
+    private LinearLayoutManager mLinearLayoutManager;
     private CallbackHelper mStartedShowingCallback;
     private CallbackHelper mFinishedShowingCallback;
     private CallbackHelper mStartedHidingCallback;
@@ -104,7 +123,7 @@
         super.setUpTest();
 
         TestThreadUtils.runOnUiThreadBlocking(
-                () -> { mRecyclerView = getActivity().findViewById(R.id.tab_list_view); });
+                () -> { mRecyclerView = spy(getActivity().findViewById(R.id.tab_list_view)); });
 
         mStartedShowingCallback = new CallbackHelper();
         mFinishedShowingCallback = new CallbackHelper();
@@ -119,6 +138,20 @@
         });
     }
 
+    private void setUpGridLayoutManager() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mGridLayoutManager = spy(new GridLayoutManager(getActivity(), 2));
+            mRecyclerView.setLayoutManager(mGridLayoutManager);
+        });
+    }
+
+    private void setUpLinearLayoutManager() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mLinearLayoutManager = spy(new LinearLayoutManager(getActivity()));
+            mRecyclerView.setLayoutManager(mLinearLayoutManager);
+        });
+    }
+
     @Test
     @MediumTest
     // clang-format off
@@ -291,6 +324,71 @@
         assertThat(mRecyclerView.getPaddingBottom(), equalTo(CONTAINER_HEIGHT));
     }
 
+    @Test
+    @MediumTest
+    @UiThreadTest
+    public void testSetInitialScrollIndex_Carousel() {
+        setUpLinearLayoutManager();
+        mRecyclerView.layout(0, 0, 1000, 100);
+
+        mContainerModel.set(
+                TabListContainerProperties.MODE, TabListCoordinator.TabListMode.CAROUSEL);
+        mContainerModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 3);
+
+        // Offset will be view width (1000) / 2 - tab card width calculated from dp dimension / 2.
+        verify(mLinearLayoutManager, times(1))
+                .scrollToPositionWithOffset(eq(3),
+                        intThat(allOf(lessThan(mRecyclerView.getWidth() / 2), greaterThan(0))));
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
+    public void testSetInitialScrollIndex_Grid() {
+        setUpGridLayoutManager();
+        mRecyclerView.layout(0, 0, 100, 500);
+
+        mContainerModel.set(TabListContainerProperties.MODE, TabListCoordinator.TabListMode.GRID);
+        mContainerModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 5);
+
+        // Offset will be view height (500) / 2 - tab card height calculated from TabUtils / 2
+        verify(mGridLayoutManager, times(1))
+                .scrollToPositionWithOffset(eq(5),
+                        intThat(allOf(lessThan(mRecyclerView.getHeight() / 2), greaterThan(0))));
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
+    public void testSetInitialScrollIndex_List_NoTabs() {
+        setUpLinearLayoutManager();
+        mRecyclerView.layout(0, 0, 100, 500);
+
+        mContainerModel.set(TabListContainerProperties.MODE, TabListCoordinator.TabListMode.LIST);
+        mContainerModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 7);
+
+        // Offset will be 0 to avoid divide by 0 with no tabs.
+        verify(mLinearLayoutManager, times(1)).scrollToPositionWithOffset(eq(7), eq(0));
+    }
+
+    @Test
+    @MediumTest
+    @UiThreadTest
+    public void testSetInitialScrollIndex_List_WithTabs() {
+        setUpLinearLayoutManager();
+        mRecyclerView.layout(0, 0, 100, 500);
+
+        doReturn(9).when(mLinearLayoutManager).getItemCount();
+        doReturn(900).when(mRecyclerView).computeVerticalScrollRange();
+
+        mContainerModel.set(TabListContainerProperties.MODE, TabListCoordinator.TabListMode.LIST);
+        mContainerModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 5);
+
+        // 9 Tabs at 900 scroll extent = 100 per tab. With view height of 500 the offset is
+        // 500 / 2 - 900 / 9 / 2 = 200.
+        verify(mLinearLayoutManager, times(1)).scrollToPositionWithOffset(eq(5), eq(200));
+    }
+
     @Override
     public void tearDownTest() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(mMCP::destroy);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
index 49802dc..74cab4c0 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
@@ -1363,6 +1363,7 @@
 
     @Test
     @MediumTest
+    @EnableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
     public void testToolbarActionButtonContentDescription() {
         prepareBlankTab(2, false);
         List<Tab> tabs = getTabsInCurrentTabModel();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
index 73b8ebeb..78ec6d6 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -29,6 +29,7 @@
 import android.view.View;
 import android.widget.EditText;
 
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -1270,6 +1271,26 @@
     }
 
     @Test
+    public void testScrollToTab() {
+        // Mock that tab1, tab2 and newTab are in the same group and newTab is the root tab.
+        TabImpl newTab = prepareTab(TAB3_ID, TAB3_TITLE);
+        List<Tab> tabgroup = new ArrayList<>(Arrays.asList(mTab1, mTab2, newTab));
+        createTabGroup(tabgroup, TAB2_ID);
+
+        // Mock that mTab2 is the current tab for the dialog.
+        doReturn(0).when(mTabGroupModelFilter).indexOf(mTab1);
+        doReturn(mTab2).when(mTabGroupModelFilter).getTabAt(0);
+        doReturn(TAB2_ID).when(mTabModelSelector).getCurrentTabId();
+        doReturn(mTab2).when(mTabModelSelector).getTabById(TAB2_ID);
+        doReturn(tabgroup).when(mTabGroupModelFilter).getRelatedTabList(TAB2_ID);
+
+        // Reset and confirm scroll index.
+        mMediator.onReset(tabgroup);
+
+        Assert.assertEquals(1, mModel.get(TabGridPanelProperties.INITIAL_SCROLL_INDEX).intValue());
+    }
+
+    @Test
     public void destroy() {
         mMediator.destroy();
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCloseActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCloseActionUnitTest.java
index 79d45c4a..8204dfd 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCloseActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCloseActionUnitTest.java
@@ -13,7 +13,9 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -22,6 +24,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -34,6 +37,8 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorActionUnitTestHelper.TabIdGroup;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorActionUnitTestHelper.TabListHolder;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 
@@ -48,7 +53,11 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
+@EnableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
 public class TabSelectionEditorCloseActionUnitTest {
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
     @Mock
     private TabModelSelector mTabModelSelector;
     @Mock
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorGroupActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorGroupActionUnitTest.java
index d2271aa..a6921f56 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorGroupActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorGroupActionUnitTest.java
@@ -13,7 +13,9 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -22,6 +24,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -34,6 +37,8 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorActionUnitTestHelper.TabIdGroup;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorActionUnitTestHelper.TabListHolder;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 
@@ -47,7 +52,11 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
+@EnableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
 public class TabSelectionEditorGroupActionUnitTest {
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
     @Mock
     private TabModelSelector mTabModelSelector;
     @Mock
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java
index 6f0bb99..cc75a98 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorShareActionUnitTest.java
@@ -20,6 +20,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -29,6 +30,7 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
@@ -39,6 +41,8 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorAction.IconPosition;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorAction.ShowMode;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
 import org.chromium.components.browser_ui.share.ShareParams;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
@@ -57,9 +61,12 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
+@EnableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
 public class TabSelectionEditorShareActionUnitTest {
     @Rule
     public JniMocker mJniMocker = new JniMocker();
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
 
     @Mock
     private TabModelSelector mTabModelSelector;
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorUngroupActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorUngroupActionUnitTest.java
index 8b8a818e..030687a0 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorUngroupActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorUngroupActionUnitTest.java
@@ -13,7 +13,9 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -22,6 +24,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -32,6 +35,8 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorAction.IconPosition;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorAction.ShowMode;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 
@@ -45,7 +50,11 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
+@EnableFeatures({ChromeFeatureList.TAB_SELECTION_EDITOR_V2})
 public class TabSelectionEditorUngroupActionUnitTest {
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
     @Mock
     private TabModelSelector mTabModelSelector;
     @Mock
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
index 1089f51..b60250f 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
@@ -476,15 +476,15 @@
 
         doReturn(1).when(mTabModelFilter).index();
         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(incognitoTabModel, mTabModel);
-        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(0));
+        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(1));
 
         doReturn(2).when(mTabModelFilter).index();
         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(incognitoTabModel, mTabModel);
-        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(0));
+        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(2));
 
         doReturn(3).when(mTabModelFilter).index();
         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(incognitoTabModel, mTabModel);
-        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(1));
+        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(3));
     }
 
     @Test
@@ -576,10 +576,11 @@
         mMediator.showTabSwitcherView(true);
         assertThat(mModel.get(TabListContainerProperties.IS_VISIBLE), equalTo(true));
 
-        mModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 1);
+        // Set to an "invalid" value and ensure it gets changed.
+        mModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 1337);
 
         mTabModelObserverCaptor.getValue().restoreCompleted();
-        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(0));
+        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(2));
     }
 
     @Test
@@ -715,10 +716,11 @@
         initAndAssertAllProperties();
         doReturn(true).when(mTabModelSelector).isTabStateInitialized();
 
-        mModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 1);
+        // Set to an "invalid" value and ensure it gets changed.
+        mModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 1337);
 
         mMediator.prepareTabSwitcherView();
-        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(0));
+        assertThat(mModel.get(TabListContainerProperties.INITIAL_SCROLL_INDEX), equalTo(2));
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiMetricsHelperUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiMetricsHelperUnitTest.java
new file mode 100644
index 0000000..22bf8164
--- /dev/null
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiMetricsHelperUnitTest.java
@@ -0,0 +1,28 @@
+// 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.chrome.browser.tasks.tab_management;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorShareAction.TabSelectionEditorShareActionState;
+
+/**
+ * Unit tests for {@link TabUiMetricsHelper}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class TabUiMetricsHelperUnitTest {
+    @Test
+    public void testShareStateHistogram() {
+        String histogramName = "Android.TabMultiSelectV2.SharingState";
+        TabUiMetricsHelper.recordShareStateHistogram(TabSelectionEditorShareActionState.SUCCESS);
+        assertThat(RecordHistogram.getHistogramValueCountForTesting(histogramName, 1), equalTo(1));
+    }
+}
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index 7df1d2c..b1be7fac 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -72,6 +72,7 @@
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCustomViewManagerUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilitiesUnitTest.java",
+  "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiMetricsHelperUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUnitTestUtils.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/StaleTabSuggestionProviderTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabContextObserverTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
index 4a29de3f..9064b9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
@@ -23,16 +23,19 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.commerce.ShoppingFeatures;
+import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
 import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksShim;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.read_later.ReadingListUtils;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsServiceFactory;
-import org.chromium.chrome.browser.subscriptions.SubscriptionsManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkItem;
 import org.chromium.components.bookmarks.BookmarkType;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.IdentifierType;
+import org.chromium.components.commerce.core.ManagementType;
+import org.chromium.components.commerce.core.ShoppingService;
+import org.chromium.components.commerce.core.SubscriptionsObserver;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.components.power_bookmarks.PowerBookmarkType;
 import org.chromium.components.power_bookmarks.ShoppingSpecifics;
@@ -54,8 +57,8 @@
     private boolean mIsNativeBookmarkModelLoaded;
     private final ObserverList<BookmarkModelObserver> mObservers =
             new ObserverList<BookmarkModelObserver>();
-    private SubscriptionsManager mSubscriptionManager;
-    private SubscriptionsManager.SubscriptionObserver mSubscriptionsObserver;
+    private ShoppingService mShoppingService;
+    private SubscriptionsObserver mSubscriptionsObserver;
 
     /**
      * Handler to fetch the bookmarks, titles, urls and folder hierarchy.
@@ -75,25 +78,26 @@
         mNativeBookmarkBridge = nativeBookmarkBridge;
         mIsDoingExtensiveChanges = BookmarkBridgeJni.get().isDoingExtensiveChanges(
                 mNativeBookmarkBridge, BookmarkBridge.this);
-        mSubscriptionsObserver = new SubscriptionsManager.SubscriptionObserver() {
+        // TODO(crbug.com/1382191): Move this observer to native.
+        mSubscriptionsObserver = new SubscriptionsObserver() {
             @Override
-            public void onSubscribe(List<CommerceSubscription> subscriptions) {}
+            public void onSubscribe(List<CommerceSubscription> subscriptions, boolean succeeded) {}
 
             @Override
-            public void onUnsubscribe(List<CommerceSubscription> subscriptions) {
+            public void onUnsubscribe(List<CommerceSubscription> subscriptions, boolean succeeded) {
+                if (!succeeded) return;
                 removeExplicitShoppingSubscriptions(subscriptions);
             }
         };
         if (ShoppingFeatures.isShoppingListEnabled()) {
-            mSubscriptionManager = new CommerceSubscriptionsServiceFactory()
-                                           .getForLastUsedProfile()
-                                           .getSubscriptionsManager();
-            mSubscriptionManager.addObserver(mSubscriptionsObserver);
+            mShoppingService =
+                    ShoppingServiceFactory.getForProfile(Profile.getLastUsedRegularProfile());
+            mShoppingService.addSubscriptionsObserver(mSubscriptionsObserver);
         }
     }
 
     @VisibleForTesting
-    SubscriptionsManager.SubscriptionObserver getSubscriptionObserver() {
+    SubscriptionsObserver getSubscriptionObserver() {
         return mSubscriptionsObserver;
     }
 
@@ -109,8 +113,8 @@
         }
         mObservers.clear();
 
-        if (mSubscriptionManager != null) {
-            mSubscriptionManager.removeObserver(mSubscriptionsObserver);
+        if (mShoppingService != null) {
+            mShoppingService.removeSubscriptionsObserver(mSubscriptionsObserver);
         }
     }
 
@@ -450,17 +454,14 @@
         HashSet<Long> clusterIdMap = new HashSet<>();
         for (CommerceSubscription c : subscriptions) {
             // Ensure the subscription is explicit.
-            if (c == null
-                    || !c.getManagementType().equals(
-                            CommerceSubscription.SubscriptionManagementType.USER_MANAGED)) {
+            if (c == null || c.managementType != ManagementType.USER_MANAGED) {
                 continue;
             }
 
-            if (c.getTrackingIdType().equals(CommerceSubscription.TrackingIdType.OFFER_ID)) {
-                offerIdMap.add(UnsignedLongs.parseUnsignedLong(c.getTrackingId()));
-            } else if (c.getTrackingIdType().equals(
-                               CommerceSubscription.TrackingIdType.PRODUCT_CLUSTER_ID)) {
-                clusterIdMap.add(UnsignedLongs.parseUnsignedLong(c.getTrackingId()));
+            if (c.idType == IdentifierType.OFFER_ID) {
+                offerIdMap.add(UnsignedLongs.parseUnsignedLong(c.id));
+            } else if (c.idType == IdentifierType.PRODUCT_CLUSTER_ID) {
+                clusterIdMap.add(UnsignedLongs.parseUnsignedLong(c.id));
             }
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
index 785fe7b..2c68045 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
@@ -25,7 +25,6 @@
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsServiceFactory;
 import org.chromium.chrome.browser.sync.SyncService;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.signin.PersonalizedSigninPromoView;
@@ -68,7 +67,6 @@
     private String mSearchText;
     private BookmarkId mCurrentFolder;
     private SyncService mSyncService;
-    private CommerceSubscriptionsServiceFactory mCommerceSubscriptionsServiceFactory;
 
     // Keep track of the currently highlighted bookmark - used for "show in folder" action.
     private BookmarkId mHighlightedBookmark;
@@ -128,7 +126,6 @@
                 ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.IN_MEMORY_WITH_DISK_CACHE,
                         Profile.getLastUsedRegularProfile().getProfileKey(),
                         GlobalDiscardableReferencePool.getReferencePool());
-        mCommerceSubscriptionsServiceFactory = new CommerceSubscriptionsServiceFactory();
         mSnackbarManager = snackbarManager;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowCoordinator.java
index 6f7811a7..79480e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowCoordinator.java
@@ -20,13 +20,13 @@
 import org.chromium.chrome.browser.commerce.PriceTrackingUtils;
 import org.chromium.chrome.browser.commerce.ShoppingFeatures;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.subscriptions.SubscriptionsManager;
 import org.chromium.chrome.browser.user_education.IPHCommandBuilder;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.components.commerce.core.ShoppingService;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
@@ -55,13 +55,12 @@
     /**
      * @param context The {@link Context} associated with this cooridnator.
      * @param bottomSheetController Allows displaying content in the bottom sheet.
-     * @param subscriptionsManager Allows un/subscribing for product updates, used for
+     * @param shoppingService Allows un/subscribing for product updates, used for
      *         price-tracking.
      * @param userEducationHelper A means of triggering IPH.
      */
     public BookmarkSaveFlowCoordinator(@NonNull Context context,
-            @NonNull BottomSheetController bottomSheetController,
-            @Nullable SubscriptionsManager subscriptionsManager,
+            @NonNull BottomSheetController bottomSheetController, ShoppingService shoppingService,
             @NonNull UserEducationHelper userEducationHelper) {
         mContext = context;
         mBottomSheetController = bottomSheetController;
@@ -72,7 +71,7 @@
         mBookmarkSaveFlowView = LayoutInflater.from(mContext).inflate(
                 org.chromium.chrome.R.layout.bookmark_save_flow, /*root=*/null);
         mMediator = new BookmarkSaveFlowMediator(
-                mBookmarkModel, mPropertyModel, mContext, this::close, subscriptionsManager);
+                mBookmarkModel, mPropertyModel, mContext, this::close, shoppingService);
         mChangeProcessor = PropertyModelChangeProcessor.create(mPropertyModel,
                 (ViewLookupCachingFrameLayout) mBookmarkSaveFlowView,
                 new BookmarkSaveFlowViewBinder());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediator.java
index 4328b8e..bd16376d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediator.java
@@ -20,10 +20,11 @@
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManagerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
-import org.chromium.chrome.browser.subscriptions.SubscriptionsManager;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkItem;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.ShoppingService;
+import org.chromium.components.commerce.core.SubscriptionsObserver;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -32,7 +33,7 @@
 
 /** Controls the bookmarks save-flow. */
 public class BookmarkSaveFlowMediator
-        extends BookmarkModelObserver implements SubscriptionsManager.SubscriptionObserver {
+        extends BookmarkModelObserver implements SubscriptionsObserver {
     private final Context mContext;
     private final Runnable mCloseRunnable;
 
@@ -42,7 +43,7 @@
     private BookmarkId mBookmarkId;
     private PowerBookmarkMeta mPowerBookmarkMeta;
     private boolean mWasBookmarkMoved;
-    private SubscriptionsManager mSubscriptionsManager;
+    private ShoppingService mShoppingService;
     private CommerceSubscription mSubscription;
     private Callback<Boolean> mSubscriptionsManagerCallback;
     private String mFolderName;
@@ -53,11 +54,10 @@
      *         model.
      * @param context The {@link Context} associated with this mediator.
      * @param closeRunnable A {@link Runnable} which closes the bookmark save flow.
-     * @param subscriptionsManager Used to manage the price-tracking subscriptions.
+     * @param shoppingService Used to manage the price-tracking subscriptions.
      */
     public BookmarkSaveFlowMediator(BookmarkModel bookmarkModel, PropertyModel propertyModel,
-            Context context, Runnable closeRunnable,
-            @Nullable SubscriptionsManager subscriptionsManager) {
+            Context context, Runnable closeRunnable, ShoppingService shoppingService) {
         mBookmarkModel = bookmarkModel;
         mBookmarkModel.addObserver(this);
 
@@ -65,9 +65,9 @@
         mContext = context;
         mCloseRunnable = closeRunnable;
 
-        mSubscriptionsManager = subscriptionsManager;
-        if (mSubscriptionsManager != null) {
-            mSubscriptionsManager.addObserver(this);
+        mShoppingService = shoppingService;
+        if (mShoppingService != null) {
+            mShoppingService.addSubscriptionsObserver(this);
         }
     }
 
@@ -189,8 +189,8 @@
 
     void destroy() {
         mBookmarkModel.removeObserver(this);
-        if (mSubscriptionsManager != null) {
-            mSubscriptionsManager.removeObserver(this);
+        if (mShoppingService != null) {
+            mShoppingService.removeSubscriptionsObserver(this);
         }
 
         mBookmarkModel = null;
@@ -228,14 +228,16 @@
         bindBookmarkProperties(mBookmarkId, mPowerBookmarkMeta, mWasBookmarkMoved);
     }
 
-    // SubscriptionsManager.SubscriptionObserver implementation
+    // SubscriptionsObserver implementation
     @Override
-    public void onSubscribe(List<CommerceSubscription> subscriptions) {
+    public void onSubscribe(List<CommerceSubscription> subscriptions, boolean succeeded) {
+        if (!succeeded) return;
         setPriceTrackingToggleVisualsOnly(subscriptions.contains(mSubscription));
     }
 
     @Override
-    public void onUnsubscribe(List<CommerceSubscription> subscriptions) {
+    public void onUnsubscribe(List<CommerceSubscription> subscriptions, boolean succeeded) {
+        if (!succeeded) return;
         setPriceTrackingToggleVisualsOnly(!subscriptions.contains(mSubscription));
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
index 17a3e20..ba1ac2c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
@@ -40,7 +40,7 @@
 import org.chromium.chrome.browser.app.bookmarks.BookmarkEditActivity;
 import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.CustomTabsUiType;
-import org.chromium.chrome.browser.commerce.ShoppingFeatures;
+import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.IncognitoCustomTabIntentDataProvider;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
@@ -50,8 +50,6 @@
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.read_later.ReadingListUtils;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsServiceFactory;
-import org.chromium.chrome.browser.subscriptions.SubscriptionsManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
@@ -62,6 +60,7 @@
 import org.chromium.components.bookmarks.BookmarkItem;
 import org.chromium.components.bookmarks.BookmarkType;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.commerce.core.ShoppingService;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.profile_metrics.BrowserProfileType;
@@ -142,16 +141,12 @@
             return;
         }
 
-        SubscriptionsManager subscriptionService = null;
-        if (ShoppingFeatures.isShoppingListEnabled()) {
-            subscriptionService = new CommerceSubscriptionsServiceFactory()
-                                          .getForLastUsedProfile()
-                                          .getSubscriptionsManager();
-        }
+        ShoppingService shoppingService =
+                ShoppingServiceFactory.getForProfile(Profile.getLastUsedRegularProfile());
 
         BookmarkSaveFlowCoordinator bookmarkSaveFlowCoordinator =
-                new BookmarkSaveFlowCoordinator(activity, bottomSheetController,
-                        subscriptionService, new UserEducationHelper(activity, new Handler()));
+                new BookmarkSaveFlowCoordinator(activity, bottomSheetController, shoppingService,
+                        new UserEducationHelper(activity, new Handler()));
         bookmarkSaveFlowCoordinator.show(bookmarkId, fromExplicitTrackUi, wasBookmarkMoved);
     }
 
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 9c51496..ad6b1b8 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
@@ -20,12 +20,12 @@
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.bookmarks.PowerBookmarkMetrics.PriceTrackingState;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkItem;
 import org.chromium.components.browser_ui.widget.RoundedCornerOutlineProvider;
 import org.chromium.components.browser_ui.widget.chips.ChipView;
+import org.chromium.components.commerce.core.CommerceSubscription;
 import org.chromium.components.image_fetcher.ImageFetcher;
 import org.chromium.components.payments.CurrencyFormatter;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java
index 1900499..1891d05 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java
@@ -18,16 +18,16 @@
 import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
 import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManagerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.PriceTrackableOffer;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.SubscriptionManagementType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.TrackingIdType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.CommerceSubscription.UserSeenOffer;
+import org.chromium.components.commerce.core.IdentifierType;
+import org.chromium.components.commerce.core.ManagementType;
 import org.chromium.components.commerce.core.ShoppingService;
+import org.chromium.components.commerce.core.SubscriptionType;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.components.power_bookmarks.PowerBookmarkType;
 import org.chromium.components.power_bookmarks.ShoppingSpecifics;
@@ -90,14 +90,14 @@
             @NonNull PowerBookmarkMeta meta) {
         ShoppingSpecifics shoppingSpecifics = meta.getShoppingSpecifics();
         // Use UnsignedLongs to convert ProductClusterId to avoid overflow.
-        PriceTrackableOffer seenOffer =
-                new PriceTrackableOffer(UnsignedLongs.toString(shoppingSpecifics.getOfferId()),
-                        Long.toString(shoppingSpecifics.getCurrentPrice().getAmountMicros()),
+        UserSeenOffer seenOffer =
+                new UserSeenOffer(UnsignedLongs.toString(shoppingSpecifics.getOfferId()),
+                        shoppingSpecifics.getCurrentPrice().getAmountMicros(),
                         shoppingSpecifics.getCountryCode());
-        return new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK,
+        return new CommerceSubscription(SubscriptionType.PRICE_TRACK,
+                IdentifierType.PRODUCT_CLUSTER_ID,
                 UnsignedLongs.toString(shoppingSpecifics.getProductClusterId()),
-                SubscriptionManagementType.USER_MANAGED, TrackingIdType.PRODUCT_CLUSTER_ID,
-                seenOffer);
+                ManagementType.USER_MANAGED, seenOffer);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index 419814bf..c48d1eb2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -804,7 +804,6 @@
 
         CommerceSubscriptionsServiceFactory factory = new CommerceSubscriptionsServiceFactory();
         mCommerceSubscriptionsService = factory.getForLastUsedProfile();
-        mCommerceSubscriptionsService.getSubscriptionsManager().queryAndUpdateWaaEnabled();
         mCommerceSubscriptionsService.initDeferredStartupForActivity(
                 mTabModelSelectorSupplier.get(), mActivityLifecycleDispatcher);
     }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 3c71a40c..d9f9cca6be 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -49,7 +49,6 @@
 chrome_junit_test_java_deps += feed_test_deps
 chrome_junit_test_java_sources += commerce_subscriptions_junit_test_sources
 chrome_junit_test_java_deps += commerce_subscriptions_junit_test_deps
-chrome_test_java_sources += commerce_subscriptions_java_test_sources
 chrome_test_java_sources += commerce_merchant_viewer_java_test_sources
 
 if (enable_arcore) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
index f0aaacf..d740398e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
@@ -21,13 +21,16 @@
 import org.chromium.base.test.util.RequiresRestart;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.util.BookmarkTestUtil;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkItem;
 import org.chromium.components.bookmarks.BookmarkType;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.IdentifierType;
+import org.chromium.components.commerce.core.ManagementType;
+import org.chromium.components.commerce.core.SubscriptionType;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.components.power_bookmarks.ShoppingSpecifics;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -385,11 +388,10 @@
         Assert.assertTrue(originalMeta.getShoppingSpecifics().getIsPriceTracked());
 
         ArrayList<CommerceSubscription> subscriptions = new ArrayList<>();
-        subscriptions.add(new CommerceSubscription(
-                CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK, Long.toString(offerId),
-                CommerceSubscription.SubscriptionManagementType.USER_MANAGED,
-                CommerceSubscription.TrackingIdType.OFFER_ID));
-        mBookmarkBridge.getSubscriptionObserver().onUnsubscribe(subscriptions);
+        subscriptions.add(
+                new CommerceSubscription(SubscriptionType.PRICE_TRACK, IdentifierType.OFFER_ID,
+                        Long.toString(offerId), ManagementType.USER_MANAGED, null));
+        mBookmarkBridge.getSubscriptionObserver().onUnsubscribe(subscriptions, true);
 
         // The product with the unsubscribed ID should no longer be price tracked.
         PowerBookmarkMeta updatedMeta = mBookmarkBridge.getPowerBookmarkMeta(bookmark);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java
index 14a52abe..2a3a73a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java
@@ -41,8 +41,6 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksShim;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
-import org.chromium.chrome.browser.subscriptions.SubscriptionsManager;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -53,6 +51,8 @@
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetTestSupport;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.ShoppingService;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.components.power_bookmarks.ShoppingSpecifics;
 import org.chromium.content_public.browser.test.util.ClickUtils;
@@ -81,7 +81,7 @@
     public JniMocker mJniMocker = new JniMocker();
 
     @Mock
-    SubscriptionsManager mSubscriptionsManager;
+    ShoppingService mShoppingService;
 
     @Mock
     PriceTrackingUtils.Natives mMockPriceTrackingUtilsJni;
@@ -107,7 +107,7 @@
                     cta.getRootUiCoordinatorForTesting().getBottomSheetController();
             mBottomSheetTestSupport = new BottomSheetTestSupport(mBottomSheetController);
             mBookmarkSaveFlowCoordinator = new BookmarkSaveFlowCoordinator(
-                    cta, mBottomSheetController, mSubscriptionsManager, mUserEducationHelper);
+                    cta, mBottomSheetController, mShoppingService, mUserEducationHelper);
             mBookmarkModel = mActivityTestRule.getActivity().getBookmarkModelForTesting();
         });
 
@@ -120,18 +120,16 @@
                 .setPriceTrackingStateForBookmark(
                         any(Profile.class), anyLong(), anyBoolean(), any());
         doAnswer((invocation) -> {
-            ((Callback<Integer>) invocation.getArgument(1))
-                    .onResult(SubscriptionsManager.StatusCode.OK);
+            ((Callback<Boolean>) invocation.getArgument(1)).onResult(true);
             return null;
         })
-                .when(mSubscriptionsManager)
+                .when(mShoppingService)
                 .subscribe(any(CommerceSubscription.class), any());
         doAnswer((invocation) -> {
-            ((Callback<Integer>) invocation.getArgument(1))
-                    .onResult(SubscriptionsManager.StatusCode.OK);
+            ((Callback<Boolean>) invocation.getArgument(1)).onResult(true);
             return null;
         })
-                .when(mSubscriptionsManager)
+                .when(mShoppingService)
                 .unsubscribe(any(CommerceSubscription.class), any());
     }
 
@@ -234,11 +232,10 @@
                 .setPriceTrackingStateForBookmark(
                         any(Profile.class), anyLong(), anyBoolean(), any());
         doAnswer((invocation) -> {
-            ((Callback<Integer>) invocation.getArgument(1))
-                    .onResult(SubscriptionsManager.StatusCode.NETWORK_ERROR);
+            ((Callback<Boolean>) invocation.getArgument(1)).onResult(false);
             return null;
         })
-                .when(mSubscriptionsManager)
+                .when(mShoppingService)
                 .subscribe(any(CommerceSubscription.class), any());
         onView(withId(R.id.notification_switch)).perform(click());
         mRenderTestRule.render(mBookmarkSaveFlowCoordinator.getViewForTesting(),
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtilsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtilsTest.java
index 0192b51..0acc3a7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtilsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtilsTest.java
@@ -19,9 +19,12 @@
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Batch;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkType;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.IdentifierType;
+import org.chromium.components.commerce.core.ManagementType;
+import org.chromium.components.commerce.core.SubscriptionType;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.components.power_bookmarks.ProductPrice;
 import org.chromium.components.power_bookmarks.ShoppingSpecifics;
@@ -55,14 +58,12 @@
         CommerceSubscription subscription =
                 PowerBookmarkUtils.createCommerceSubscriptionForPowerBookmarkMeta(meta);
 
-        Assert.assertEquals(CommerceSubscription.TrackingIdType.PRODUCT_CLUSTER_ID,
-                subscription.getTrackingIdType());
-        Assert.assertEquals(CommerceSubscription.SubscriptionManagementType.USER_MANAGED,
-                subscription.getManagementType());
-        Assert.assertEquals("123", subscription.getTrackingId());
-        Assert.assertEquals("456", subscription.getSeenOffer().offerId);
-        Assert.assertEquals("100", subscription.getSeenOffer().currentPrice);
-        Assert.assertEquals("us", subscription.getSeenOffer().countryCode);
+        Assert.assertEquals(IdentifierType.PRODUCT_CLUSTER_ID, subscription.idType);
+        Assert.assertEquals(ManagementType.USER_MANAGED, subscription.managementType);
+        Assert.assertEquals("123", subscription.id);
+        Assert.assertEquals("456", subscription.userSeenOffer.offerId);
+        Assert.assertEquals(100L, subscription.userSeenOffer.userSeenPrice);
+        Assert.assertEquals("us", subscription.userSeenOffer.countryCode);
     }
 
     /**
@@ -70,9 +71,8 @@
      * @return A user-managed subscription with the specified ID.
      */
     private CommerceSubscription buildSubscription(String clusterId) {
-        return new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                clusterId, CommerceSubscription.SubscriptionManagementType.USER_MANAGED,
-                CommerceSubscription.TrackingIdType.PRODUCT_CLUSTER_ID);
+        return new CommerceSubscription(SubscriptionType.PRICE_TRACK,
+                IdentifierType.PRODUCT_CLUSTER_ID, clusterId, ManagementType.USER_MANAGED, null);
     }
 
     /**
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediatorTest.java
index 7606940..1a885742 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediatorTest.java
@@ -18,8 +18,8 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
-import org.chromium.chrome.browser.subscriptions.SubscriptionsManager;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.ShoppingService;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.shadows.ShadowAppCompatResources;
 
@@ -41,7 +41,7 @@
     @Mock
     BookmarkModel mModel;
     @Mock
-    SubscriptionsManager mSubscriptionsManager;
+    ShoppingService mShoppingService;
     @Mock
     CommerceSubscription mSubscription;
 
@@ -49,7 +49,7 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mMediator = new BookmarkSaveFlowMediator(
-                mModel, mPropertyModel, mContext, mCloseRunnable, mSubscriptionsManager);
+                mModel, mPropertyModel, mContext, mCloseRunnable, mShoppingService);
         mMediator.setSubscriptionForTesting(mSubscription);
     }
 
@@ -62,15 +62,15 @@
                 (int) mPropertyModel.get(
                         BookmarkSaveFlowProperties.NOTIFICATION_SWITCH_START_ICON_RES));
 
-        mMediator.onSubscribe(Arrays.asList(mSubscription));
+        mMediator.onSubscribe(Arrays.asList(mSubscription), true);
         Assert.assertTrue(
                 mPropertyModel.get(BookmarkSaveFlowProperties.NOTIFICATION_SWITCH_TOGGLED));
         Assert.assertEquals(R.drawable.price_tracking_enabled_filled,
                 (int) mPropertyModel.get(
                         BookmarkSaveFlowProperties.NOTIFICATION_SWITCH_START_ICON_RES));
-        Mockito.verify(mSubscriptionsManager, Mockito.never())
+        Mockito.verify(mShoppingService, Mockito.never())
                 .subscribe(Mockito.any(CommerceSubscription.class), Mockito.any());
-        Mockito.verify(mSubscriptionsManager, Mockito.never())
+        Mockito.verify(mShoppingService, Mockito.never())
                 .unsubscribe(Mockito.any(CommerceSubscription.class), Mockito.any());
     }
 
@@ -83,15 +83,15 @@
                 (int) mPropertyModel.get(
                         BookmarkSaveFlowProperties.NOTIFICATION_SWITCH_START_ICON_RES));
 
-        mMediator.onUnsubscribe(Arrays.asList(mSubscription));
+        mMediator.onUnsubscribe(Arrays.asList(mSubscription), true);
         Assert.assertFalse(
                 mPropertyModel.get(BookmarkSaveFlowProperties.NOTIFICATION_SWITCH_TOGGLED));
         Assert.assertEquals(R.drawable.price_tracking_disabled,
                 (int) mPropertyModel.get(
                         BookmarkSaveFlowProperties.NOTIFICATION_SWITCH_START_ICON_RES));
-        Mockito.verify(mSubscriptionsManager, Mockito.never())
+        Mockito.verify(mShoppingService, Mockito.never())
                 .subscribe(Mockito.any(CommerceSubscription.class), Mockito.any());
-        Mockito.verify(mSubscriptionsManager, Mockito.never())
+        Mockito.verify(mShoppingService, Mockito.never())
                 .unsubscribe(Mockito.any(CommerceSubscription.class), Mockito.any());
     }
 }
diff --git a/chrome/android/monochrome/scripts/monochrome_python_tests.py b/chrome/android/monochrome/scripts/monochrome_python_tests.py
index 73c9259..ceaf82ec 100755
--- a/chrome/android/monochrome/scripts/monochrome_python_tests.py
+++ b/chrome/android/monochrome/scripts/monochrome_python_tests.py
@@ -47,9 +47,11 @@
       '--system-webview-pathmap',
       help='The system webview APK resources pathmap path.')
 
-  # --avd-config parameter is unused. Add it to the parser because typ.Runner
-  # checks that all arguments are known. crbug.com/1084351
+  # --avd-config and use-persistent-shell parameters are unused.
+  # Add them to the parser because typ.Runner checks that all arguments
+  # are known. crbug.com/1084351
   parser.add_argument('--avd-config', help='Unused')
+  parser.add_argument('--use-persistent-shell', help='Unused')
   return parser
 
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1cf82a41..d38c722b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3284,8 +3284,6 @@
       "//chrome/browser/commerce/merchant_viewer:merchant_signal_db",
       "//chrome/browser/commerce/merchant_viewer:merchant_viewer_data_manager",
       "//chrome/browser/commerce/merchant_viewer/android:jni_headers",
-      "//chrome/browser/commerce/subscriptions:commerce_subscription_db",
-      "//chrome/browser/commerce/subscriptions/android:jni_headers",
       "//chrome/browser/consent_auditor/android:jni_headers",
       "//chrome/browser/content_creation/notes/internal/android:jni_headers",
       "//chrome/browser/creator/android:jni_headers",
@@ -5062,7 +5060,6 @@
       "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
       "//chrome/browser/ui/webui/ash/vm:mojo_bindings",
       "//chrome/browser/ui/webui/nearby_share:mojom",
-      "//chrome/browser/ui/webui/nearby_share/public/mojom",
       "//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom",
       "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings",
       "//chrome/services/sharing/public/cpp",
@@ -5121,6 +5118,7 @@
       "//chromeos/ash/services/nearby/public/cpp",
       "//chromeos/ash/services/nearby/public/cpp:tcp_server_socket_port",
       "//chromeos/ash/services/nearby/public/mojom",
+      "//chromeos/ash/services/nearby/public/mojom:nearby_share_settings",
       "//chromeos/ash/services/network_health/public/cpp",
       "//chromeos/ash/services/secure_channel/public/mojom",
       "//chromeos/components/onc",
@@ -6038,15 +6036,6 @@
     ]
   }
 
-  if (is_mac) {
-    sources += [
-      "enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.cc",
-      "enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.h",
-    ]
-
-    deps += [ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/mac" ]
-  }
-
   if (is_linux || is_win || is_mac || is_android) {
     sources += [
       "enterprise/idle/action.cc",
@@ -6121,7 +6110,10 @@
     }
 
     if (is_mac) {
-      deps += [ "//components/device_signals/core/browser/mac" ]
+      deps += [
+        "//chrome/browser/enterprise/connectors/device_trust/key_management/core/mac",
+        "//components/device_signals/core/browser/mac",
+      ]
     }
   }
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 34dccec..c3fa702 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4347,6 +4347,11 @@
      flag_descriptions::kGlobalMediaControlsCastStartStopDescription,
      kOsDesktop,
      FEATURE_VALUE_TYPE(media_router::kGlobalMediaControlsCastStartStop)},
+    {"media-remoting-without-fullscreen",
+     flag_descriptions::kMediaRemotingWithoutFullscreenName,
+     flag_descriptions::kMediaRemotingWithoutFullscreenDescription,
+     kOsWin | kOsLinux | kOsMac,
+     FEATURE_VALUE_TYPE(media::kMediaRemotingWithoutFullscreen)},
     {"allow-all-sites-to-initiate-mirroring",
      flag_descriptions::kAllowAllSitesToInitiateMirroringName,
      flag_descriptions::kAllowAllSitesToInitiateMirroringDescription,
@@ -9131,11 +9136,6 @@
                                     "BindingManagerConnectionLimit")},
 #endif
 
-    {"sync-access-handle-all-sync-surface",
-     flag_descriptions::kSyncAccessHandleAllSyncSurfaceName,
-     flag_descriptions::kSyncAccessHandleAllSyncSurfaceDescription, kOsAll,
-     FEATURE_VALUE_TYPE(blink::features::kSyncAccessHandleAllSyncSurface)},
-
     {"webauthn-new-desktop-ui", flag_descriptions::kWebAuthnNewDesktopUIName,
      flag_descriptions::kWebAuthnNewDesktopUIDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(device::kWebAuthnNewDiscoverableCredentialsUi)},
diff --git a/chrome/browser/android/customtabs/tab_interaction_recorder_android.cc b/chrome/browser/android/customtabs/tab_interaction_recorder_android.cc
index 862026f..7795ffe21 100644
--- a/chrome/browser/android/customtabs/tab_interaction_recorder_android.cc
+++ b/chrome/browser/android/customtabs/tab_interaction_recorder_android.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/core/browser/autofill_manager.h"
-#include "content/public/browser/document_user_data.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -26,7 +25,6 @@
 using autofill::AutofillManager;
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
-using content::GlobalRenderFrameHostId;
 using content::RenderFrameHost;
 
 namespace {
@@ -38,14 +36,13 @@
     return nullptr;
   return autofill_driver->autofill_manager();
 }
+
 }  // namespace
 
 AutofillObserverImpl::AutofillObserverImpl(
-    GlobalRenderFrameHostId id,
     autofill::AutofillManager* autofill_manager,
     OnFormInteractionCallback form_interaction_callback)
-    : global_id_(id),
-      autofill_manager_(autofill_manager),
+    : autofill_manager_(autofill_manager),
       form_interaction_callback_(std::move(form_interaction_callback)) {
   autofill_manager->AddObserver(this);
 }
@@ -80,22 +77,7 @@
 
 void AutofillObserverImpl::OnFormInteraction() {
   Invalidate();
-  std::move(form_interaction_callback_).Run(global_id_);
-}
-
-DOCUMENT_USER_DATA_KEY_IMPL(FormInteractionData);
-
-FormInteractionData::~FormInteractionData() = default;
-
-FormInteractionData::FormInteractionData(RenderFrameHost* rfh)
-    : DocumentUserData<FormInteractionData>(rfh) {}
-
-void FormInteractionData::SetHasFormInteractionData() {
-  had_form_interaction_data_ = true;
-}
-
-bool FormInteractionData::GetHasFormInteractionData() {
-  return static_cast<jboolean>(had_form_interaction_data_);
+  std::move(form_interaction_callback_).Run();
 }
 
 TabInteractionRecorderAndroid::~TabInteractionRecorderAndroid() = default;
@@ -151,10 +133,7 @@
       .OnUserInteraction();
 }
 
-void TabInteractionRecorderAndroid::SetHasFormInteractions(
-    GlobalRenderFrameHostId id) {
-  FormInteractionData::GetForCurrentDocument(RenderFrameHost::FromID(id))
-      ->SetHasFormInteractionData();
+void TabInteractionRecorderAndroid::SetHasFormInteractions() {
   has_form_interactions_ = true;
   rfh_observer_map_.clear();
 }
@@ -171,11 +150,9 @@
   if (!autofill_manager)
     return;
 
-  FormInteractionData::CreateForCurrentDocument(render_frame_host);
-
   rfh_observer_map_[render_frame_host->GetGlobalId()] =
       std::make_unique<AutofillObserverImpl>(
-          render_frame_host->GetGlobalId(), autofill_manager,
+          autofill_manager,
           base::BindOnce(&TabInteractionRecorderAndroid::SetHasFormInteractions,
                          weak_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/android/customtabs/tab_interaction_recorder_android.h b/chrome/browser/android/customtabs/tab_interaction_recorder_android.h
index 56ad49a..633bb9e 100644
--- a/chrome/browser/android/customtabs/tab_interaction_recorder_android.h
+++ b/chrome/browser/android/customtabs/tab_interaction_recorder_android.h
@@ -11,7 +11,6 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/autofill_manager.h"
-#include "content/public/browser/document_user_data.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "url/gurl.h"
@@ -21,11 +20,9 @@
 // Autofill observer impl for the TabInteractionRecorderAndroid to use.
 class AutofillObserverImpl : public autofill::AutofillManager::Observer {
  public:
-  using OnFormInteractionCallback =
-      base::OnceCallback<void(content::GlobalRenderFrameHostId)>;
+  using OnFormInteractionCallback = base::OnceCallback<void()>;
 
   explicit AutofillObserverImpl(
-      content::GlobalRenderFrameHostId id,
       autofill::AutofillManager* autofill_manager,
       OnFormInteractionCallback form_interaction_callback);
 
@@ -43,29 +40,10 @@
   void OnFormInteraction();
   void Invalidate();
 
-  content::GlobalRenderFrameHostId global_id_;
   raw_ptr<autofill::AutofillManager, DanglingUntriaged> autofill_manager_;
   OnFormInteractionCallback form_interaction_callback_;
 };
 
-// DocumentUserData stored inside each RenderFrameHost indicating whether
-// the hosting RFH experienced a form interaction.
-class FormInteractionData
-    : public content::DocumentUserData<FormInteractionData> {
- public:
-  explicit FormInteractionData(content::RenderFrameHost* rfh);
-
-  ~FormInteractionData() override;
-  void SetHasFormInteractionData();
-  bool GetHasFormInteractionData();
-
- private:
-  bool had_form_interaction_data_;
-
-  friend DocumentUserData;
-  DOCUMENT_USER_DATA_KEY_DECL();
-};
-
 // Class that record interaction from the web contents. The definition
 // of an "interaction" includes user's engagement with text inputs or selection
 // inputs, or changes in navigation stacks.
@@ -121,7 +99,7 @@
 
   friend class AutofillObserverImpl;
   void StartObservingFrame(content::RenderFrameHost* render_frame_host);
-  void SetHasFormInteractions(content::GlobalRenderFrameHostId id);
+  void SetHasFormInteractions();
 
   void ResetImpl();
 
diff --git a/chrome/browser/android/customtabs/tab_interaction_recorder_android_unittest.cc b/chrome/browser/android/customtabs/tab_interaction_recorder_android_unittest.cc
index 6760065d9..ed810ab 100644
--- a/chrome/browser/android/customtabs/tab_interaction_recorder_android_unittest.cc
+++ b/chrome/browser/android/customtabs/tab_interaction_recorder_android_unittest.cc
@@ -23,7 +23,6 @@
 #include "components/autofill/core/common/autofill_tick_clock.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -98,37 +97,32 @@
 };
 
 TEST_F(AutofillObserverImplTest, TestFormInteraction) {
-  base::MockOnceCallback<void(content::GlobalRenderFrameHostId)> callback;
-  content::GlobalRenderFrameHostId id = content::GlobalRenderFrameHostId();
-  AutofillObserverImpl obsever(id, autofill_manager(), callback.Get());
+  base::MockOnceCallback<void()> callback;
+  AutofillObserverImpl obsever(autofill_manager(), callback.Get());
 
-  EXPECT_CALL(callback, Run(id)).Times(1);
+  EXPECT_CALL(callback, Run()).Times(1);
   OnTextFieldDidChangeForAutofillManager(autofill_manager());
 
   // Observer should no longer get notified after the first interaction.
-  EXPECT_CALL(callback, Run(id)).Times(0);
+  EXPECT_CALL(callback, Run()).Times(0);
   OnTextFieldDidChangeForAutofillManager(autofill_manager());
 }
 
 TEST_F(AutofillObserverImplTest, TestNoFormInteraction) {
-  content::GlobalRenderFrameHostId id = content::GlobalRenderFrameHostId();
-  base::MockOnceCallback<void(content::GlobalRenderFrameHostId)> callback;
-  auto* observer =
-      new AutofillObserverImpl(id, autofill_manager(), callback.Get());
+  base::MockOnceCallback<void()> callback;
+  auto* observer = new AutofillObserverImpl(autofill_manager(), callback.Get());
 
-  EXPECT_CALL(callback, Run(id)).Times(0);
+  EXPECT_CALL(callback, Run()).Times(0);
   delete observer;
 }
 
 TEST_F(AutofillObserverImplTest, TestAutofillManagerDestroy) {
-  content::GlobalRenderFrameHostId id = content::GlobalRenderFrameHostId();
-  base::MockOnceCallback<void(content::GlobalRenderFrameHostId)> callback;
-  auto* observer =
-      new AutofillObserverImpl(id, autofill_manager(), callback.Get());
+  base::MockOnceCallback<void()> callback;
+  auto* observer = new AutofillObserverImpl(autofill_manager(), callback.Get());
 
   DestroyManager();
 
-  EXPECT_CALL(callback, Run(id)).Times(0);
+  EXPECT_CALL(callback, Run()).Times(0);
   delete observer;
 }
 
@@ -186,9 +180,6 @@
   EXPECT_FALSE(helper->has_form_interactions());
   OnTextFieldDidChangeForAutofillManager(autofill_manager());
   EXPECT_TRUE(helper->has_form_interactions());
-  EXPECT_TRUE(FormInteractionData::GetForCurrentDocument(
-                  contents->GetPrimaryMainFrame())
-                  ->FormInteractionData::GetHasFormInteractionData());
 
   JNIEnv* env = base::android::AttachCurrentThread();
   EXPECT_TRUE(helper->HadFormInteraction(env));
@@ -249,10 +240,6 @@
   // Simulate touch, text input, and navigation events.
   helper->DidGetUserInteraction(blink::WebTouchEvent());
   OnTextFieldDidChangeForAutofillManager(autofill_manager());
-  EXPECT_TRUE(FormInteractionData::GetForCurrentDocument(
-                  contents->GetPrimaryMainFrame())
-                  ->FormInteractionData::GetHasFormInteractionData());
-
   content::WebContentsTester::For(contents.get())
       ->NavigateAndCommit(GURL("https://bar.com"));
   task_environment()->RunUntilIdle();
diff --git a/chrome/browser/apps/app_service/app_service_proxy_ash.cc b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
index 25221c6cc..bcb26022 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
@@ -43,6 +43,7 @@
 #include "components/services/app_service/public/cpp/preferred_apps_list.h"
 #include "components/services/app_service/public/cpp/types_util.h"
 #include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "extensions/grit/extensions_browser_resources.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
index 132ca89..7775ed7 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
@@ -33,6 +33,8 @@
 #include "components/sync/base/model_type.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_service_utils.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
diff --git a/chrome/browser/ash/accessibility/accessibility_extension_loader.cc b/chrome/browser/ash/accessibility/accessibility_extension_loader.cc
index e69885ed..d973286 100644
--- a/chrome/browser/ash/accessibility/accessibility_extension_loader.cc
+++ b/chrome/browser/ash/accessibility/accessibility_extension_loader.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/user_manager/user.h"
 #include "extensions/browser/extension_system.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc
index 3ed0f57..7323203 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc
@@ -13,6 +13,7 @@
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "components/account_manager_core/account_manager_facade.h"
 #include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
+#include "components/user_manager/user.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_view.cc b/chrome/browser/ash/app_restore/arc_ghost_window_view.cc
index 8de125f..5e7eb4c1 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_view.cc
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_view.cc
@@ -16,6 +16,8 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/chromeos/styles/cros_styles.h"
diff --git a/chrome/browser/ash/app_restore/full_restore_service.cc b/chrome/browser/ash/app_restore/full_restore_service.cc
index 0da354b..aea761b 100644
--- a/chrome/browser/ash/app_restore/full_restore_service.cc
+++ b/chrome/browser/ash/app_restore/full_restore_service.cc
@@ -36,6 +36,8 @@
 #include "components/app_restore/full_restore_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/chromeos/devicetype_utils.h"
 #include "ui/message_center/public/cpp/notification.h"
diff --git a/chrome/browser/ash/arc/policy/managed_configuration_variables.cc b/chrome/browser/ash/arc/policy/managed_configuration_variables.cc
index 5c09c4bb..62450b0 100644
--- a/chrome/browser/ash/arc/policy/managed_configuration_variables.cc
+++ b/chrome/browser/ash/arc/policy/managed_configuration_variables.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/user_manager/user.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "third_party/re2/src/re2/stringpiece.h"
 
diff --git a/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.cc b/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.cc
index 89e853c..d6fb5b2 100644
--- a/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.cc
+++ b/chrome/browser/ash/authpolicy/authpolicy_credentials_manager.cc
@@ -34,6 +34,8 @@
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/chromeos/account_manager.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "components/vector_icons/vector_icons.h"
 #include "dbus/message.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ash/bluetooth/debug_logs_manager_factory.cc b/chrome/browser/ash/bluetooth/debug_logs_manager_factory.cc
index c49d90e..c9441343 100644
--- a/chrome/browser/ash/bluetooth/debug_logs_manager_factory.cc
+++ b/chrome/browser/ash/bluetooth/debug_logs_manager_factory.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/user_manager/user.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/cert_provisioning/cert_provisioning_common.cc b/chrome/browser/ash/cert_provisioning/cert_provisioning_common.cc
index 1befc878..181292b 100644
--- a/chrome/browser/ash/cert_provisioning/cert_provisioning_common.cc
+++ b/chrome/browser/ash/cert_provisioning/cert_provisioning_common.cc
@@ -24,6 +24,7 @@
 #include "chromeos/ash/components/dbus/attestation/interface.pb.h"
 #include "components/account_id/account_id.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/user_manager/user.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index 552abd5..40181bd5 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -884,8 +884,10 @@
   // on ChromeKeyboardControllerClient.
   chrome_keyboard_controller_client_ = ChromeKeyboardControllerClient::Create();
 
-  // ProfileHelper has to be initialized after UserManager instance is created.
-  ProfileHelper::Get()->Initialize();
+  // Instantiate ProfileHelper as some following code depending on this
+  // behavior.
+  // TODO(crbug.com/1325210): Switch to explicit initialization.
+  ProfileHelper::Get();
   signin_profile_handler_ = std::make_unique<SigninProfileHandler>();
 
   // If kLoginUser is passed this indicates that user has already
@@ -1597,6 +1599,11 @@
   // vc_app_service_client_ has to be destructed before PostMainMessageLoopRun.
   vc_app_service_client_.reset();
 
+  // Has a dependency on Profile, so it needs to be destroyed before Profile
+  // gets destroyed during ProfileManager destruction, which happens inside
+  // PostMainMessageLoop below.
+  web_kiosk_app_manager_.reset();
+
   // NOTE: Closes ash and destroys `Shell`.
   ChromeBrowserMainPartsLinux::PostMainMessageLoopRun();
 
@@ -1607,7 +1614,6 @@
 
   // Destroy classes that may have ash observers or dependencies.
   arc_kiosk_app_manager_.reset();
-  web_kiosk_app_manager_.reset();
   chrome_keyboard_controller_client_.reset();
 
   // All ARC related modules should have been shut down by this point, so
diff --git a/chrome/browser/ash/crosapi/browser_manager.cc b/chrome/browser/ash/crosapi/browser_manager.cc
index 7be2aa5..d1ba339 100644
--- a/chrome/browser/ash/crosapi/browser_manager.cc
+++ b/chrome/browser/ash/crosapi/browser_manager.cc
@@ -91,6 +91,8 @@
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_type.h"
 #include "components/version_info/version_info.h"
 #include "media/capture/capture_switches.h"
diff --git a/chrome/browser/ash/crosapi/cert_database_ash.cc b/chrome/browser/ash/crosapi/cert_database_ash.cc
index 9441f9d..1f29bc1 100644
--- a/chrome/browser/ash/crosapi/cert_database_ash.cc
+++ b/chrome/browser/ash/crosapi/cert_database_ash.cc
@@ -20,6 +20,7 @@
 #include "chromeos/crosapi/mojom/cert_database.mojom.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "crypto/nss_util_internal.h"
diff --git a/chrome/browser/ash/crosapi/identity_manager_ash.cc b/chrome/browser/ash/crosapi/identity_manager_ash.cc
index ece3fc4..2dbd3cf 100644
--- a/chrome/browser/ash/crosapi/identity_manager_ash.cc
+++ b/chrome/browser/ash/crosapi/identity_manager_ash.cc
@@ -11,6 +11,8 @@
 #include "chromeos/crosapi/mojom/identity_manager.mojom.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "ui/gfx/image/image.h"
 
diff --git a/chrome/browser/ash/crosapi/test_controller_ash.cc b/chrome/browser/ash/crosapi/test_controller_ash.cc
index adcc27be..bfb50b6 100644
--- a/chrome/browser/ash/crosapi/test_controller_ash.cc
+++ b/chrome/browser/ash/crosapi/test_controller_ash.cc
@@ -46,6 +46,8 @@
 #include "chromeos/ash/components/dbus/shill/shill_third_party_vpn_driver_client.h"
 #include "chromeos/ash/components/dbus/userdataauth/cryptohome_misc_client.h"
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/tts_utterance.h"
 #include "crypto/sha2.h"
diff --git a/chrome/browser/ash/crostini/crostini_features.cc b/chrome/browser/ash/crostini/crostini_features.cc
index 5efdad2a..fdded61b 100644
--- a/chrome/browser/ash/crostini/crostini_features.cc
+++ b/chrome/browser/ash/crostini/crostini_features.cc
@@ -19,6 +19,7 @@
 #include "chrome/common/chrome_features.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user.h"
 
 namespace {
 
diff --git a/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl_unittest.cc b/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl_unittest.cc
index 7e49da0..00c8596 100644
--- a/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl_unittest.cc
+++ b/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl_unittest.cc
@@ -44,7 +44,6 @@
   void SetUp() override {
     user_manager_ = std::make_unique<FakeChromeUserManager>();
     user_manager_->Initialize();
-    ProfileHelper::Get()->Initialize();
     LoginState::Initialize();
 
     task_env_.RunUntilIdle();
diff --git a/chrome/browser/ash/drive/drivefs_test_support.cc b/chrome/browser/ash/drive/drivefs_test_support.cc
index 733a2896..970601b 100644
--- a/chrome/browser/ash/drive/drivefs_test_support.cc
+++ b/chrome/browser/ash/drive/drivefs_test_support.cc
@@ -19,6 +19,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "components/drive/drive_pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user.h"
 
 namespace drive {
 
diff --git a/chrome/browser/ash/fileapi/file_system_backend.cc b/chrome/browser/ash/fileapi/file_system_backend.cc
index b799333c..e8614624 100644
--- a/chrome/browser/ash/fileapi/file_system_backend.cc
+++ b/chrome/browser/ash/fileapi/file_system_backend.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
 #include "chrome/common/url_constants.h"
 #include "chromeos/ash/components/dbus/cros_disks/cros_disks_client.h"
+#include "components/user_manager/user.h"
 #include "storage/browser/file_system/async_file_util.h"
 #include "storage/browser/file_system/external_mount_points.h"
 #include "storage/browser/file_system/file_stream_reader.h"
diff --git a/chrome/browser/ash/kerberos/kerberos_credentials_manager_factory.cc b/chrome/browser/ash/kerberos/kerberos_credentials_manager_factory.cc
index 1cd331f..ca1f896 100644
--- a/chrome/browser/ash/kerberos/kerberos_credentials_manager_factory.cc
+++ b/chrome/browser/ash/kerberos/kerberos_credentials_manager_factory.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/user_manager/user_manager.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/login/oobe_interactive_ui_test.cc b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
index 508222a..fcf121c 100644
--- a/chrome/browser/ash/login/oobe_interactive_ui_test.cc
+++ b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
@@ -800,8 +800,7 @@
     HandleGestureNavigationScreen();
   }
 
-  if (features::IsDarkLightModeEnabled() &&
-      features::IsOobeThemeSelectionEnabled()) {
+  if (features::IsDarkLightModeEnabled()) {
     HandleThemeSelectionScreen();
   }
 
@@ -1152,8 +1151,7 @@
       test_setup()->hide_shelf_controls_in_tablet_mode()) {
     HandleGestureNavigationScreen();
 
-    if (features::IsDarkLightModeEnabled() &&
-        features::IsOobeThemeSelectionEnabled()) {
+    if (features::IsDarkLightModeEnabled()) {
       HandleThemeSelectionScreen();
     }
 
diff --git a/chrome/browser/ash/login/quick_unlock/pin_backend.cc b/chrome/browser/ash/login/quick_unlock/pin_backend.cc
index 5a8b8ea..1bf7397 100644
--- a/chrome/browser/ash/login/quick_unlock/pin_backend.cc
+++ b/chrome/browser/ash/login/quick_unlock/pin_backend.cc
@@ -26,6 +26,8 @@
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "crypto/random.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc
index a8b37c3e..0a3426e 100644
--- a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc
+++ b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc
@@ -27,6 +27,7 @@
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
 #include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
+#include "components/user_manager/user_manager.h"
 
 namespace ash::quick_unlock {
 
diff --git a/chrome/browser/ash/login/saml/password_sync_token_verifier.cc b/chrome/browser/ash/login/saml/password_sync_token_verifier.cc
index 487929e..5734068 100644
--- a/chrome/browser/ash/login/saml/password_sync_token_verifier.cc
+++ b/chrome/browser/ash/login/saml/password_sync_token_verifier.cc
@@ -12,6 +12,8 @@
 #include "chrome/browser/browser_process.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/browser/storage_partition.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/screens/theme_selection_screen.cc b/chrome/browser/ash/login/screens/theme_selection_screen.cc
index 14b134b..097dfdb 100644
--- a/chrome/browser/ash/login/screens/theme_selection_screen.cc
+++ b/chrome/browser/ash/login/screens/theme_selection_screen.cc
@@ -65,7 +65,6 @@
       ProfileManager::GetActiveUserProfile()->GetPrefs()->FindPreference(
           prefs::kDarkModeScheduleType);
   if (pref->IsManaged() || pref->IsRecommended() ||
-      !features::IsOobeThemeSelectionEnabled() ||
       !features::IsDarkLightModeEnabled()) {
     return true;
   }
diff --git a/chrome/browser/ash/login/screens/theme_selection_screen_browsertest.cc b/chrome/browser/ash/login/screens/theme_selection_screen_browsertest.cc
index 495b0840..f9d6bfc 100644
--- a/chrome/browser/ash/login/screens/theme_selection_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/theme_selection_screen_browsertest.cc
@@ -53,9 +53,7 @@
       public ::testing::WithParamInterface<test::UIPath> {
  public:
   ThemeSelectionScreenTest() {
-    feature_list_.InitWithFeatures({features::kEnableOobeThemeSelection,
-                                    chromeos::features::kDarkLightMode},
-                                   {});
+    feature_list_.InitWithFeatures({chromeos::features::kDarkLightMode}, {});
   }
 
   void SetUpOnMainThread() override {
diff --git a/chrome/browser/ash/login/security_token_session_controller_factory.cc b/chrome/browser/ash/login/security_token_session_controller_factory.cc
index c0b6328..dd940938 100644
--- a/chrome/browser/ash/login/security_token_session_controller_factory.cc
+++ b/chrome/browser/ash/login/security_token_session_controller_factory.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 
 namespace ash {
 namespace login {
diff --git a/chrome/browser/ash/login/session/user_session_initializer.cc b/chrome/browser/ash/login/session/user_session_initializer.cc
index 0d85972..2f4c54a 100644
--- a/chrome/browser/ash/login/session/user_session_initializer.cc
+++ b/chrome/browser/ash/login/session/user_session_initializer.cc
@@ -53,6 +53,7 @@
 #include "chromeos/ash/components/peripheral_notification/peripheral_notification_manager.h"
 #include "components/live_caption/caption_util.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 
diff --git a/chrome/browser/ash/login/signin/offline_signin_limiter_unittest.cc b/chrome/browser/ash/login/signin/offline_signin_limiter_unittest.cc
index a68fd453..d918787 100644
--- a/chrome/browser/ash/login/signin/offline_signin_limiter_unittest.cc
+++ b/chrome/browser/ash/login/signin/offline_signin_limiter_unittest.cc
@@ -96,7 +96,6 @@
   DestroyLimiter();
   Mock::VerifyAndClearExpectations(user_manager_);
   EXPECT_CALL(*user_manager_, Shutdown()).Times(1);
-  EXPECT_CALL(*user_manager_, RemoveSessionStateObserver(_)).Times(1);
   profile_ = nullptr;
   // Finish any pending tasks before deleting the TestingBrowserProcess.
   task_environment_.RunUntilIdle();
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index bfb4dc9..ad0f33e1 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -76,6 +76,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/components/cryptohome/userdataauth_util.h"
 #include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
 #include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h"
@@ -100,6 +101,7 @@
 #include "components/user_manager/remove_user_delegate.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_image/user_image.h"
+#include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_names.h"
 #include "components/user_manager/user_type.h"
 #include "content/public/browser/browser_thread.h"
@@ -242,9 +244,14 @@
       base::BindOnce(&CheckCryptohomeIsMounted));
 
   // Confirm that we hadn't loaded the new profile previously.
+  const auto* user = user_manager::UserManager::Get()->GetActiveUser();
+  if (!user) {
+    // No active user means there's no new profile for the active user.
+    return;
+  }
   base::FilePath user_profile_dir =
-      g_browser_process->profile_manager()->user_data_dir().Append(
-          ProfileHelper::Get()->GetActiveUserProfileDir());
+      ash::BrowserContextHelper::Get()->GetBrowserContextPathByUserIdHash(
+          user->username_hash());
   CHECK(
       !g_browser_process->profile_manager()->GetProfileByPath(user_profile_dir))
       << "The user profile was loaded before we mounted the cryptohome.";
diff --git a/chrome/browser/ash/login/users/fake_chrome_user_manager.cc b/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
index 17a8677..a3f31f22 100644
--- a/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
@@ -265,8 +265,6 @@
 
 void FakeChromeUserManager::SwitchActiveUser(const AccountId& account_id) {
   active_account_id_ = account_id;
-  ProfileHelper::Get()->ActiveUserHashChanged(
-      user_manager::FakeUserManager::GetFakeUsernameHash(account_id));
   active_user_ = nullptr;
   if (!users_.empty() && active_account_id_.is_valid()) {
     for (user_manager::User* const user : users_) {
diff --git a/chrome/browser/ash/login/users/scoped_test_user_manager.cc b/chrome/browser/ash/login/users/scoped_test_user_manager.cc
index 4456d65..e68a49c 100644
--- a/chrome/browser/ash/login/users/scoped_test_user_manager.cc
+++ b/chrome/browser/ash/login/users/scoped_test_user_manager.cc
@@ -5,16 +5,12 @@
 #include "chrome/browser/ash/login/users/scoped_test_user_manager.h"
 
 #include "chrome/browser/ash/login/users/chrome_user_manager_impl.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
 
 namespace ash {
 
 ScopedTestUserManager::ScopedTestUserManager() {
   chrome_user_manager_ = ChromeUserManagerImpl::CreateChromeUserManager();
   chrome_user_manager_->Initialize();
-
-  // ProfileHelper has to be initialized after UserManager instance is created.
-  ProfileHelper::Get()->Initialize();
 }
 
 ScopedTestUserManager::~ScopedTestUserManager() {
diff --git a/chrome/browser/ash/login/wizard_controller_browsertest.cc b/chrome/browser/ash/login/wizard_controller_browsertest.cc
index b2bd31f..800e2cb 100644
--- a/chrome/browser/ash/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/ash/login/wizard_controller_browsertest.cc
@@ -3216,8 +3216,7 @@
   WizardControllerThemeSelectionDefaultSettingsTest() {
     feature_list_.InitWithFeatures(
         /*enabled_features=*/{},
-        /*disabled_features=*/{features::kEnableOobeThemeSelection,
-                               chromeos::features::kDarkLightMode});
+        /*disabled_features=*/{chromeos::features::kDarkLightMode});
   }
 
  protected:
@@ -3246,8 +3245,7 @@
  public:
   WizardControllerThemeSelectionEnabledTest() {
     feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kEnableOobeThemeSelection,
-                              chromeos::features::kDarkLightMode},
+        /*enabled_features=*/{chromeos::features::kDarkLightMode},
         /*disabled_features=*/{});
   }
 
diff --git a/chrome/browser/ash/net/network_portal_detector_impl.cc b/chrome/browser/ash/net/network_portal_detector_impl.cc
index e41e067..d111b082 100644
--- a/chrome/browser/ash/net/network_portal_detector_impl.cc
+++ b/chrome/browser/ash/net/network_portal_detector_impl.cc
@@ -53,6 +53,40 @@
   return NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
 }
 
+  // traffic annotation tag.
+constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+  net::DefineNetworkTrafficAnnotation("network_portal_detector", R"(
+    semantics {
+      sender: "Network Portal Detector"
+      description:
+        "Checks if the system is behind a captive portal. To do so, makes "
+        "an unlogged, dataless connection to a Google server and checks "
+        "the response."
+      trigger:
+        "Portal detection by the OS is initiated when a new WiFi service "
+        "is connected to in order to determine whether the network has "
+        "internet access or is behind a captive portal."
+      data: "None."
+      destination: GOOGLE_OWNED_SERVICE
+      internal {
+        contacts {
+          email: "cros-network-health-team@google.com"
+        }
+      }
+      user_data {
+        type: NONE
+      }
+      last_reviewed: "2023-01-13"
+    }
+    policy {
+      cookies_allowed: NO
+      setting:
+        "This feature cannot be disabled by settings."
+      policy_exception_justification:
+        "This feature is required to deliver core user experiences and "
+        "cannot be disabled by policy."
+    })");
+
 void SetNetworkPortalState(const NetworkState* network,
                            NetworkState::PortalState portal_state) {
   NetworkHandler::Get()->network_state_handler()->SetNetworkChromePortalState(
@@ -261,7 +295,7 @@
       url,
       base::BindOnce(&NetworkPortalDetectorImpl::OnAttemptCompleted,
                      weak_factory_.GetWeakPtr()),
-      NO_TRAFFIC_ANNOTATION_YET);
+      kTrafficAnnotation);
   attempt_timeout_task_.Reset(
       base::BindOnce(&NetworkPortalDetectorImpl::OnAttemptTimeout,
                      weak_factory_.GetWeakPtr()));
diff --git a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
index 2b03ffc..cebe0f67 100644
--- a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
+++ b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
@@ -34,6 +34,7 @@
 #include "chromeos/ash/components/phonehub/user_action_recorder_impl.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "components/pref_registry/pref_registry_syncable.h"
+#include "components/user_manager/user_manager.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/chrome/browser/ash/policy/enrollment/psm/fake_rlwe_client.cc b/chrome/browser/ash/policy/enrollment/psm/fake_rlwe_client.cc
index 6f6a6739..6b84395 100644
--- a/chrome/browser/ash/policy/enrollment/psm/fake_rlwe_client.cc
+++ b/chrome/browser/ash/policy/enrollment/psm/fake_rlwe_client.cc
@@ -102,6 +102,10 @@
   return server_membership_response == kHasMembership;
 }
 
+std::string FakeRlweClient::GetPsmIdentifierDebugString() {
+  return plaintext_id_.sensitive_id();
+}
+
 FakeRlweClient::FakeRlweClient(psm_rlwe::RlweUseCase use_case,
                                const psm_rlwe::RlwePlaintextId& plaintext_id)
     : use_case_(use_case), plaintext_id_(plaintext_id) {}
diff --git a/chrome/browser/ash/policy/enrollment/psm/fake_rlwe_client.h b/chrome/browser/ash/policy/enrollment/psm/fake_rlwe_client.h
index 0549b64..4e3cba9 100644
--- a/chrome/browser/ash/policy/enrollment/psm/fake_rlwe_client.h
+++ b/chrome/browser/ash/policy/enrollment/psm/fake_rlwe_client.h
@@ -31,6 +31,7 @@
       const OprfResponse& oprf_response) override;
   ::rlwe::StatusOr<bool> ProcessQueryResponse(
       const QueryResponse& query_response) override;
+  std::string GetPsmIdentifierDebugString() override;
 
  private:
   const UseCase use_case_;
diff --git a/chrome/browser/ash/policy/enrollment/psm/rlwe_client.h b/chrome/browser/ash/policy/enrollment/psm/rlwe_client.h
index ece295f483..4e37db8 100644
--- a/chrome/browser/ash/policy/enrollment/psm/rlwe_client.h
+++ b/chrome/browser/ash/policy/enrollment/psm/rlwe_client.h
@@ -44,6 +44,9 @@
   // the set, or why the determination failed.
   virtual ::rlwe::StatusOr<bool> ProcessQueryResponse(
       const QueryResponse& query_response) = 0;
+
+  // Gets the sensitive id from `plaintext_id_` for debugging purposes.
+  virtual std::string GetPsmIdentifierDebugString() = 0;
 };
 
 }  // namespace policy::psm
diff --git a/chrome/browser/ash/policy/enrollment/psm/rlwe_client_impl.cc b/chrome/browser/ash/policy/enrollment/psm/rlwe_client_impl.cc
index e8df46cf..f8e0b6c 100644
--- a/chrome/browser/ash/policy/enrollment/psm/rlwe_client_impl.cc
+++ b/chrome/browser/ash/policy/enrollment/psm/rlwe_client_impl.cc
@@ -68,6 +68,10 @@
   return responses->membership_responses(0).membership_response().is_member();
 }
 
+std::string RlweClientImpl::GetPsmIdentifierDebugString() {
+  return plaintext_id_.sensitive_id();
+}
+
 RlweClientImpl::RlweClientImpl(
     std::unique_ptr<psm_rlwe::PrivateMembershipRlweClient> psm_rlwe_client,
     const psm_rlwe::RlwePlaintextId& plaintext_id)
diff --git a/chrome/browser/ash/policy/enrollment/psm/rlwe_client_impl.h b/chrome/browser/ash/policy/enrollment/psm/rlwe_client_impl.h
index 370a24d..993a2fafe 100644
--- a/chrome/browser/ash/policy/enrollment/psm/rlwe_client_impl.h
+++ b/chrome/browser/ash/policy/enrollment/psm/rlwe_client_impl.h
@@ -51,6 +51,7 @@
       const OprfResponse& oprf_response) override;
   ::rlwe::StatusOr<bool> ProcessQueryResponse(
       const QueryResponse& query_response) override;
+  std::string GetPsmIdentifierDebugString() override;
 
  private:
   const std::unique_ptr<private_membership::rlwe::PrivateMembershipRlweClient>
diff --git a/chrome/browser/ash/policy/enrollment/psm/rlwe_dmserver_client_impl.cc b/chrome/browser/ash/policy/enrollment/psm/rlwe_dmserver_client_impl.cc
index 1ca40ce..76ebdc6 100644
--- a/chrome/browser/ash/policy/enrollment/psm/rlwe_dmserver_client_impl.cc
+++ b/chrome/browser/ash/policy/enrollment/psm/rlwe_dmserver_client_impl.cc
@@ -228,7 +228,8 @@
       RecordPsmSuccessTimeHistogram();
 
       LOG(WARNING) << "PSM determination successful. Identifier "
-                   << (*is_member ? "" : "not ") << "present on the server";
+                   << psm_rlwe_client_->GetPsmIdentifierDebugString()
+                   << (*is_member ? "" : " not") << " present on the server";
 
       // Reset the |psm_request_job_| to allow another call to
       // CheckMembership.
diff --git a/chrome/browser/ash/policy/handlers/powerwash_requirements_checker.cc b/chrome/browser/ash/policy/handlers/powerwash_requirements_checker.cc
index d6f48b0..fd37e33 100644
--- a/chrome/browser/ash/policy/handlers/powerwash_requirements_checker.cc
+++ b/chrome/browser/ash/policy/handlers/powerwash_requirements_checker.cc
@@ -23,6 +23,7 @@
 #include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h"
 #include "chromeos/ash/components/dbus/userdataauth/cryptohome_misc_client.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
+#include "components/user_manager/user.h"
 #include "components/vector_icons/vector_icons.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ash/profiles/profile_helper.cc b/chrome/browser/ash/profiles/profile_helper.cc
index 33148df..bf3e09b 100644
--- a/chrome/browser/ash/profiles/profile_helper.cc
+++ b/chrome/browser/ash/profiles/profile_helper.cc
@@ -46,10 +46,6 @@
       std::unique_ptr<BrowserContextHelper::Delegate> delegate);
   ~ProfileHelperImpl() override;
 
-  // ProfileHelper overrides
-  base::FilePath GetActiveUserProfileDir() override;
-  void Initialize() override;
-
   Profile* GetProfileByAccountId(const AccountId& account_id) override;
   Profile* GetProfileByUser(const user_manager::User* user) override;
 
@@ -65,14 +61,8 @@
   void RemoveUserFromListForTesting(const AccountId& account_id) override;
 
  private:
-  // user_manager::UserManager::UserSessionStateObserver implementation:
-  void ActiveUserHashChanged(const std::string& hash) override;
-
   std::unique_ptr<BrowserContextHelper> browser_context_helper_;
 
-  // Identifies path to active user profile on Chrome OS.
-  std::string active_user_id_hash_;
-
   // Used for testing by unit tests and FakeUserManager/MockUserManager.
   std::map<const user_manager::User*, Profile*> user_to_profile_for_testing_;
 
@@ -86,14 +76,9 @@
 ////////////////////////////////////////////////////////////////////////////////
 // ProfileHelper, public
 
-ProfileHelper::ProfileHelper() {}
+ProfileHelper::ProfileHelper() = default;
 
-ProfileHelper::~ProfileHelper() {
-  // Checking whether UserManager is initialized covers case
-  // when ScopedTestUserManager is used.
-  if (user_manager::UserManager::IsInitialized())
-    user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
-}
+ProfileHelper::~ProfileHelper() = default;
 
 // static
 std::unique_ptr<ProfileHelper> ProfileHelper::CreateInstance() {
@@ -249,14 +234,6 @@
 
 ProfileHelperImpl::~ProfileHelperImpl() = default;
 
-base::FilePath ProfileHelperImpl::GetActiveUserProfileDir() {
-  return ProfileHelper::GetUserProfileDir(active_user_id_hash_);
-}
-
-void ProfileHelperImpl::Initialize() {
-  user_manager::UserManager::Get()->AddSessionStateObserver(this);
-}
-
 Profile* ProfileHelperImpl::GetProfileByAccountId(const AccountId& account_id) {
   // TODO(crbug.com/1325210): Remove test injection from here.
   if (!user_to_profile_for_testing_.empty()) {
@@ -346,13 +323,6 @@
       GetUserByProfile(static_cast<const Profile*>(profile)));
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// ProfileHelper, UserManager::UserSessionStateObserver implementation:
-
-void ProfileHelperImpl::ActiveUserHashChanged(const std::string& hash) {
-  active_user_id_hash_ = hash;
-}
-
 void ProfileHelperImpl::SetProfileToUserMappingForTesting(
     user_manager::User* user) {
   user_list_for_testing_.push_back(user);
diff --git a/chrome/browser/ash/profiles/profile_helper.h b/chrome/browser/ash/profiles/profile_helper.h
index 12fccce8..c788a72 100644
--- a/chrome/browser/ash/profiles/profile_helper.h
+++ b/chrome/browser/ash/profiles/profile_helper.h
@@ -12,37 +12,31 @@
 
 #include "base/files/file_path.h"
 #include "base/functional/callback_forward.h"
-#include "components/user_manager/user_manager.h"
 
+class AccountId;
 class IndependentOTRProfileManagerTest;
 class Profile;
 
-namespace base {
-class FilePath;
+namespace user_manager {
+class User;
 }
 
 namespace ash {
 
 // This helper class is used on Chrome OS to keep track of currently
 // active user profile.
-// Whenever active user is changed (either add another user into session or
-// switch between users), ActiveUserHashChanged() will be called thus
-// internal state |active_user_id_hash_| will be updated.
 // Typical use cases for using this class:
 // 1. Get "signin profile" which is a special type of profile that is only used
 //    during signin flow: GetSigninProfile()
-// 2. Get profile dir of an active user, used by ProfileManager:
-//    GetActiveUserProfileDir()
-// 3. Get mapping from user_id_hash to Profile instance/profile path etc.
-class ProfileHelper
-    : public user_manager::UserManager::UserSessionStateObserver {
+// 2. Get mapping from user_id_hash to Profile instance/profile path etc.
+class ProfileHelper {
  public:
   ProfileHelper();
 
   ProfileHelper(const ProfileHelper&) = delete;
   ProfileHelper& operator=(const ProfileHelper&) = delete;
 
-  ~ProfileHelper() override;
+  virtual ~ProfileHelper();
 
   // Creates and returns ProfileHelper implementation instance to
   // BrowserProcess/BrowserProcessPlatformPart.
@@ -140,12 +134,6 @@
   // DEPRECATED. Please use ash::IsUserBrowserContextBaseName() instead.
   static bool IsUserProfilePath(const base::FilePath& profile_path);
 
-  // Returns active user profile dir in a format [u-$hash].
-  virtual base::FilePath GetActiveUserProfileDir() = 0;
-
-  // Should called once after UserManager instance has been created.
-  virtual void Initialize() = 0;
-
   // DEPRECATED: Please use
   // BrowserContextHelper::GetBrowserContextByAccountId() instead.
   // Returns profile of the user associated with |account_id| if it is created
diff --git a/chrome/browser/ash/profiles/profile_helper_browsertest.cc b/chrome/browser/ash/profiles/profile_helper_browsertest.cc
deleted file mode 100644
index c1a2de6..0000000
--- a/chrome/browser/ash/profiles/profile_helper_browsertest.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
-#include "content/public/test/browser_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash {
-
-namespace {
-constexpr char kActiveUserHash[] = "01234567890";
-} // namespace
-
-// The boolean parameter, retrieved by GetParam(), is true if testing with
-// multi-profiles enabled.
-class ProfileHelperTest : public InProcessBrowserTest {
- public:
-  ProfileHelperTest() {
-  }
-
- protected:
-  void ActiveUserChanged(ProfileHelper* profile_helper,
-                         const std::string& hash) {
-    profile_helper->ActiveUserHashChanged(hash);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(ProfileHelperTest, ActiveUserProfileDir) {
-  ProfileHelper* profile_helper = ProfileHelper::Get();
-  ActiveUserChanged(profile_helper, kActiveUserHash);
-  base::FilePath profile_dir = profile_helper->GetActiveUserProfileDir();
-  std::string expected_dir =
-      BrowserContextHelper::GetUserBrowserContextDirName(kActiveUserHash);
-  EXPECT_EQ(expected_dir, profile_dir.BaseName().value());
-}
-
-}  // namespace ash
diff --git a/chrome/browser/ash/smb_client/smb_service.cc b/chrome/browser/ash/smb_client/smb_service.cc
index bac855c..b630810 100644
--- a/chrome/browser/ash/smb_client/smb_service.cc
+++ b/chrome/browser/ash/smb_client/smb_service.cc
@@ -39,6 +39,7 @@
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "crypto/random.h"
diff --git a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
index 8376f40..92cc5a0 100644
--- a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
@@ -57,6 +57,7 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "components/crash/content/browser/error_reporting/mock_crash_endpoint.h"
 #include "components/services/app_service/public/cpp/intent.h"
+#include "components/user_manager/user.h"
 #include "content/public/browser/media_session_service.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_navigation_observer.h"
diff --git a/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc b/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc
index dc55a07..5471ed4d 100644
--- a/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc
+++ b/chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/grit/generated_resources.h"
@@ -75,9 +74,8 @@
         std::make_unique<FakeChromeUserManager>());
 
     auto account = AccountId::FromUserEmail(kProfileName);
-    auto* user = GetFakeUserManager()->AddUser(account);
+    GetFakeUserManager()->AddUser(account);
     GetFakeUserManager()->LoginUser(account);
-    ProfileHelper::Get()->ActiveUserHashChanged(user->username_hash());
 
     service_tester_ =
         std::make_unique<NotificationDisplayServiceTester>(profile);
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index c4e5abd2..59fdc240 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -290,7 +290,6 @@
 #include "chrome/browser/ui/webui/ash/vm/vm_ui.h"
 #include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h"
 #include "chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"  // nogncheck crbug.com/1125897
 #include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom.h"
 #include "chrome/browser/ui/webui/settings/ash/os_settings_ui.h"
 #include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
@@ -304,6 +303,7 @@
 #include "chromeos/ash/services/hotspot_config/public/mojom/cros_hotspot_config.mojom.h"
 #include "chromeos/ash/services/multidevice_setup/multidevice_setup_service.h"
 #include "chromeos/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"  // nogncheck crbug.com/1125897
 #include "chromeos/components/print_management/mojom/printing_manager.mojom.h"  // nogncheck
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"  // nogncheck
 #include "chromeos/services/network_health/public/mojom/network_diagnostics.mojom.h"  // nogncheck
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index fd0b8a1..fac6bb0 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2707,14 +2707,6 @@
             blink::switches::kPrefixedStorageInfoEnabled);
       }
 
-      // Enabled async interface for FileSystemSyncAccessHandle if enabled by
-      // enterprise policy.
-      if (prefs->GetBoolean(
-              storage::kFileSystemSyncAccessHandleAsyncInterfaceEnabled)) {
-        command_line->AppendSwitch(
-            switches::kFileSystemSyncAccessHandleAsyncInterfaceEnabled);
-      }
-
 #if !BUILDFLAG(IS_ANDROID)
       InstantService* instant_service =
           InstantServiceFactory::GetForProfile(profile);
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index 2d18c6a..2d7600c1 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -1057,31 +1057,6 @@
       result.HasSwitch(blink::switches::kWebSQLNonSecureContextEnabled));
 }
 
-TEST_F(ChromeContentBrowserClientSwitchTest,
-       FileSystemSyncAccessHandleAsyncInterfaceEnabledDefault) {
-  base::CommandLine result = FetchCommandLineSwitchesForRendererProcess();
-  EXPECT_FALSE(result.HasSwitch(
-      switches::kFileSystemSyncAccessHandleAsyncInterfaceEnabled));
-}
-
-TEST_F(ChromeContentBrowserClientSwitchTest,
-       FileSystemSyncAccessHandleAsyncInterfaceEnabledDisabled) {
-  profile()->GetPrefs()->SetBoolean(
-      storage::kFileSystemSyncAccessHandleAsyncInterfaceEnabled, false);
-  base::CommandLine result = FetchCommandLineSwitchesForRendererProcess();
-  EXPECT_FALSE(result.HasSwitch(
-      switches::kFileSystemSyncAccessHandleAsyncInterfaceEnabled));
-}
-
-TEST_F(ChromeContentBrowserClientSwitchTest,
-       FileSystemSyncAccessHandleAsyncInterfaceEnabledEnabled) {
-  profile()->GetPrefs()->SetBoolean(
-      storage::kFileSystemSyncAccessHandleAsyncInterfaceEnabled, true);
-  base::CommandLine result = FetchCommandLineSwitchesForRendererProcess();
-  EXPECT_TRUE(result.HasSwitch(
-      switches::kFileSystemSyncAccessHandleAsyncInterfaceEnabled));
-}
-
 #if BUILDFLAG(IS_CHROMEOS)
 TEST_F(ChromeContentBrowserClientSwitchTest,
        ShouldSetForceAppModeSwitchInRendererProcessIfItIsSetInCurrentProcess) {
diff --git a/chrome/browser/chromeos/app_mode/app_session.cc b/chrome/browser/chromeos/app_mode/app_session.cc
index ecbe5da..36bfb33 100644
--- a/chrome/browser/chromeos/app_mode/app_session.cc
+++ b/chrome/browser/chromeos/app_mode/app_session.cc
@@ -215,6 +215,7 @@
 void AppSession::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterBooleanPref(prefs::kNewWindowsInKioskAllowed, false);
+  registry->RegisterBooleanPref(prefs::kKioskTroubleshootingToolsEnabled, false);
 }
 
 void AppSession::Init(const std::string& app_id) {
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManagerImpl.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManagerImpl.java
index 37d2c2f..f22537c 100644
--- a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManagerImpl.java
+++ b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManagerImpl.java
@@ -32,6 +32,7 @@
 import org.chromium.chrome.browser.bookmarks.BookmarkModel;
 import org.chromium.chrome.browser.bookmarks.BookmarkModelObserver;
 import org.chromium.chrome.browser.browserservices.intents.WebappConstants;
+import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.notifications.NotificationIntentInterceptor;
@@ -41,16 +42,14 @@
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.SubscriptionManagementType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.TrackingIdType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsServiceFactory;
-import org.chromium.chrome.browser.subscriptions.SubscriptionsManager;
-import org.chromium.chrome.browser.subscriptions.SubscriptionsManagerImpl;
 import org.chromium.components.browser_ui.notifications.NotificationManagerProxy;
 import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
 import org.chromium.components.browser_ui.notifications.channels.ChannelsInitializer;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.IdentifierType;
+import org.chromium.components.commerce.core.ManagementType;
+import org.chromium.components.commerce.core.ShoppingService;
+import org.chromium.components.commerce.core.SubscriptionType;
 
 import java.util.Locale;
 
@@ -233,16 +232,11 @@
                     NotificationIntentInterceptor.INVALID_CREATE_TIME);
         } else if (actionId.equals(ACTION_ID_TURN_OFF_ALERT)) {
             if (offerId == null && clusterId == null) return;
-            SubscriptionsManagerImpl subscriptionsManager =
-                    (new CommerceSubscriptionsServiceFactory())
-                            .getForLastUsedProfile()
-                            .getSubscriptionsManager();
-            Callback<Integer> callback = (status) -> {
-                assert status
-                        == SubscriptionsManager.StatusCode.OK : "Failed to remove subscriptions.";
-                Log.e(TAG,
-                        String.format(
-                                Locale.US, "Failed to remove subscriptions. Status: %d", status));
+            ShoppingService shoppingService =
+                    ShoppingServiceFactory.getForProfile(Profile.getLastUsedRegularProfile());
+            Callback<Boolean> callback = (status) -> {
+                assert status : "Failed to remove subscriptions.";
+                Log.e(TAG, "Failed to remove subscriptions.");
             };
             final BookmarkModel bookmarkModel;
             if (sBookmarkModelForTesting != null) {
@@ -253,17 +247,17 @@
 
             Runnable unsubscribeRunnable = () -> {
                 if (offerId != null) {
-                    subscriptionsManager.unsubscribe(
-                            new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK, offerId,
-                                    SubscriptionManagementType.CHROME_MANAGED,
-                                    TrackingIdType.OFFER_ID),
+                    shoppingService.unsubscribe(
+                            new CommerceSubscription(SubscriptionType.PRICE_TRACK,
+                                    IdentifierType.OFFER_ID, offerId, ManagementType.CHROME_MANAGED,
+                                    null),
                             callback);
                 }
                 if (clusterId != null) {
-                    subscriptionsManager.unsubscribe(
-                            new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK,
-                                    clusterId, SubscriptionManagementType.USER_MANAGED,
-                                    TrackingIdType.PRODUCT_CLUSTER_ID),
+                    shoppingService.unsubscribe(
+                            new CommerceSubscription(SubscriptionType.PRICE_TRACK,
+                                    IdentifierType.PRODUCT_CLUSTER_ID, clusterId,
+                                    ManagementType.USER_MANAGED, null),
                             callback);
                 }
             };
diff --git a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManagerTest.java b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManagerTest.java
index 4c8e133e..01c45f0 100644
--- a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManagerTest.java
+++ b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManagerTest.java
@@ -10,8 +10,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -32,6 +30,8 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -42,24 +42,23 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.bookmarks.BookmarkModel;
 import org.chromium.chrome.browser.browserservices.intents.WebappConstants;
+import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker.SystemNotificationType;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManagerImpl.DismissNotificationChromeActivity;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.SubscriptionManagementType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.TrackingIdType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsService;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsServiceFactory;
-import org.chromium.chrome.browser.subscriptions.SubscriptionsManagerImpl;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.browser_ui.notifications.MockNotificationManagerProxy;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.IdentifierType;
+import org.chromium.components.commerce.core.ManagementType;
+import org.chromium.components.commerce.core.ShoppingService;
 
 /**
  * Tests for  {@link PriceDropNotificationManager}.
@@ -99,13 +98,13 @@
             new BlankCTATabInitialStateRule(sActivityTestRule, false);
 
     @Mock
-    private CommerceSubscriptionsService mMockSubscriptionsService;
-
-    @Mock
-    private SubscriptionsManagerImpl mMockSubscriptionsManager;
-
+    private ShoppingService mMockShoppingService;
     @Mock
     private BookmarkModel mMockBookmarkModel;
+    @Mock
+    private Profile mMockProfile;
+    @Captor
+    private ArgumentCaptor<CommerceSubscription> mSubscriptionCaptor;
 
     @Before
     public void setUp() {
@@ -114,6 +113,8 @@
         mPriceDropNotificationManager = PriceDropNotificationManagerFactory.create();
         when(mMockBookmarkModel.isBookmarkModelLoaded()).thenReturn(true);
         PriceDropNotificationManagerImpl.setBookmarkModelForTesting(mMockBookmarkModel);
+        ShoppingServiceFactory.setShoppingServiceForTesting(mMockShoppingService);
+        Profile.setLastUsedProfileForTesting(mMockProfile);
     }
 
     @After
@@ -262,26 +263,20 @@
     @Test
     @MediumTest
     public void testOnNotificationActionClicked_TurnOffAlert() {
-        doReturn(mMockSubscriptionsManager)
-                .when(mMockSubscriptionsService)
-                .getSubscriptionsManager();
-        CommerceSubscriptionsServiceFactory.setSubscriptionsServiceForTesting(
-                mMockSubscriptionsService);
-
         String offerId = "offer_id";
-        CommerceSubscription commerceSubscription =
-                new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK, offerId,
-                        SubscriptionManagementType.CHROME_MANAGED, TrackingIdType.OFFER_ID);
 
         mPriceDropNotificationManager.onNotificationActionClicked(
                 ACTION_ID_TURN_OFF_ALERT, TEST_URL, null, null, false);
-        verify(mMockSubscriptionsManager, times(0))
-                .unsubscribe(eq(commerceSubscription), any(Callback.class));
+        verify(mMockShoppingService, times(0)).unsubscribe(any(), any(Callback.class));
 
         mPriceDropNotificationManager.onNotificationActionClicked(
                 ACTION_ID_TURN_OFF_ALERT, TEST_URL, offerId, null, false);
-        verify(mMockSubscriptionsManager, times(1))
-                .unsubscribe(eq(commerceSubscription), any(Callback.class));
+        verify(mMockShoppingService, times(1))
+                .unsubscribe(mSubscriptionCaptor.capture(), any(Callback.class));
+        assertEquals(IdentifierType.OFFER_ID, mSubscriptionCaptor.getValue().idType);
+        assertEquals(offerId, mSubscriptionCaptor.getValue().id);
+        assertEquals(ManagementType.CHROME_MANAGED, mSubscriptionCaptor.getValue().managementType);
+        assertEquals(null, mSubscriptionCaptor.getValue().userSeenOffer);
     }
 
     @Test
diff --git a/chrome/browser/commerce/subscriptions/BUILD.gn b/chrome/browser/commerce/subscriptions/BUILD.gn
deleted file mode 100644
index a20ccd2..0000000
--- a/chrome/browser/commerce/subscriptions/BUILD.gn
+++ /dev/null
@@ -1,25 +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("//testing/test.gni")
-import("//third_party/protobuf/proto_library.gni")
-
-source_set("commerce_subscription_db") {
-  sources = [
-    "commerce_subscription_db.cc",
-    "commerce_subscription_db.h",
-  ]
-
-  deps = [
-    "//base:base",
-    "//chrome/browser/commerce/subscriptions/android:jni_headers",
-    "//chrome/browser/persisted_state_db:persisted_state_db",
-    "//components/commerce/core:commerce_subscription_db_content_proto",
-    "//components/commerce/core:persisted_state_db_content_proto",
-    "//components/keyed_service/content",
-    "//components/leveldb_proto",
-    "//content/public/browser:browser",
-    "//third_party/leveldatabase",
-  ]
-}
diff --git a/chrome/browser/commerce/subscriptions/android/BUILD.gn b/chrome/browser/commerce/subscriptions/android/BUILD.gn
index c89c056..a7e5931c 100644
--- a/chrome/browser/commerce/subscriptions/android/BUILD.gn
+++ b/chrome/browser/commerce/subscriptions/android/BUILD.gn
@@ -4,25 +4,11 @@
 
 import("//build/config/android/rules.gni")
 
-generate_jni("jni_headers") {
-  sources = [
-    "java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscription.java",
-    "java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsStorage.java",
-  ]
-}
-
 android_library("subscriptions_java") {
   sources = [
-    "java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscription.java",
-    "java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionJsonSerializer.java",
-    "java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsMetrics.java",
     "java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsService.java",
     "java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceConfig.java",
-    "java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceProxy.java",
-    "java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsStorage.java",
     "java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManager.java",
-    "java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManager.java",
-    "java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManagerImpl.java",
   ]
 
   deps = [
@@ -38,6 +24,7 @@
     "//chrome/browser/signin/services/android:java",
     "//chrome/browser/tab:java",
     "//chrome/browser/tabmodel:java",
+    "//components/commerce/core/android:core_java",
     "//components/prefs/android:java",
     "//components/signin/public/android:java",
     "//components/user_prefs/android:java",
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscription.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscription.java
deleted file mode 100644
index 3608dd1..0000000
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscription.java
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringDef;
-
-import org.chromium.base.annotations.CalledByNative;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents the information for one commerce subscription entry.
- *
- * To add a new SubscriptionType / ManagementType / TrackingIdType:
- * 1. Add the type in this class.
- * 2. Add the corresponding entry in {@link commerce_subscription_db_content.proto} to ensure the
- * storage works correctly.
- */
-public class CommerceSubscription {
-    @StringDef({CommerceSubscriptionType.TYPE_UNSPECIFIED, CommerceSubscriptionType.PRICE_TRACK})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CommerceSubscriptionType {
-        String TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED";
-        String PRICE_TRACK = "PRICE_TRACK";
-    }
-
-    @StringDef({SubscriptionManagementType.TYPE_UNSPECIFIED,
-            SubscriptionManagementType.CHROME_MANAGED, SubscriptionManagementType.USER_MANAGED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SubscriptionManagementType {
-        String TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED";
-        String CHROME_MANAGED = "CHROME_MANAGED";
-        String USER_MANAGED = "USER_MANAGED";
-    }
-
-    @StringDef({TrackingIdType.IDENTIFIER_TYPE_UNSPECIFIED, TrackingIdType.OFFER_ID,
-            TrackingIdType.PRODUCT_CLUSTER_ID})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TrackingIdType {
-        String IDENTIFIER_TYPE_UNSPECIFIED = "IDENTIFIER_TYPE_UNSPECIFIED";
-        String OFFER_ID = "OFFER_ID";
-        String PRODUCT_CLUSTER_ID = "PRODUCT_CLUSTER_ID";
-    }
-
-    /** The price track offer data specific to price track subscriptions. */
-    public static class PriceTrackableOffer {
-        public PriceTrackableOffer(@Nullable String offerId, @Nullable String currentPrice,
-                @Nullable String countryCode) {
-            this.offerId = offerId;
-            this.currentPrice = currentPrice;
-            this.countryCode = countryCode;
-        }
-        /** Associated offer id */
-        public final String offerId;
-        /** Current price upon subscribing */
-        public final String currentPrice;
-        /** Country code of the offer */
-        public final String countryCode;
-    }
-
-    public static final long UNSAVED_SUBSCRIPTION = -1L;
-
-    private final long mTimestamp;
-    @NonNull
-    private final @CommerceSubscriptionType String mType;
-    @NonNull
-    private final String mTrackingId;
-    @NonNull
-    private final @SubscriptionManagementType String mManagementType;
-    @NonNull
-    private final @TrackingIdType String mTrackingIdType;
-    @Nullable
-    private final PriceTrackableOffer mSeenOffer;
-
-    // TODO(crbug.com/1311754): Clean up this api.
-    @Deprecated
-    public CommerceSubscription(@NonNull @CommerceSubscriptionType String type,
-            @NonNull String trackingId, @NonNull @SubscriptionManagementType String managementType,
-            @NonNull @TrackingIdType String trackingIdType) {
-        this(type, trackingId, managementType, trackingIdType, UNSAVED_SUBSCRIPTION);
-    }
-
-    @CalledByNative
-    CommerceSubscription(@NonNull @CommerceSubscriptionType String type, @NonNull String trackingId,
-            @NonNull @SubscriptionManagementType String managementType,
-            @NonNull @TrackingIdType String trackingIdType, long timestamp) {
-        this(type, trackingId, managementType, trackingIdType, timestamp, null);
-    }
-
-    public CommerceSubscription(@NonNull @CommerceSubscriptionType String type,
-            @NonNull String trackingId, @NonNull @SubscriptionManagementType String managementType,
-            @NonNull @TrackingIdType String trackingIdType,
-            @Nullable PriceTrackableOffer seenOffer) {
-        this(type, trackingId, managementType, trackingIdType, UNSAVED_SUBSCRIPTION, seenOffer);
-    }
-
-    private CommerceSubscription(@NonNull @CommerceSubscriptionType String type,
-            @NonNull String trackingId, @NonNull @SubscriptionManagementType String managementType,
-            @NonNull @TrackingIdType String trackingIdType, long timestamp,
-            @Nullable PriceTrackableOffer seenOffer) {
-        mTrackingId = trackingId;
-        mType = type;
-        mManagementType = managementType;
-        mTrackingIdType = trackingIdType;
-        mTimestamp = timestamp;
-        mSeenOffer = seenOffer;
-    }
-
-    long getTimestamp() {
-        return mTimestamp;
-    }
-
-    @CommerceSubscriptionType
-    String getType() {
-        return mType;
-    }
-
-    @TrackingIdType
-    public String getTrackingIdType() {
-        return mTrackingIdType;
-    }
-
-    public String getTrackingId() {
-        return mTrackingId;
-    }
-
-    @SubscriptionManagementType
-    public String getManagementType() {
-        return mManagementType;
-    }
-
-    public PriceTrackableOffer getSeenOffer() {
-        return mSeenOffer;
-    }
-
-    @CalledByNative
-    static List<CommerceSubscription> createSubscriptionList() {
-        return new ArrayList<>();
-    }
-
-    @CalledByNative
-    static CommerceSubscription createSubscriptionAndAddToList(List<CommerceSubscription> list,
-            @NonNull @CommerceSubscriptionType String type, @NonNull String trackingId,
-            @NonNull @SubscriptionManagementType String managementType,
-            @NonNull @TrackingIdType String trackingIdType, long timestamp) {
-        CommerceSubscription subscription = new CommerceSubscription(
-                type, trackingId, managementType, trackingIdType, timestamp);
-        list.add(subscription);
-        return subscription;
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (!(other instanceof CommerceSubscription)) {
-            return false;
-        }
-        CommerceSubscription otherSubscription = (CommerceSubscription) other;
-        return mManagementType.equals(otherSubscription.getManagementType())
-                && mType.equals(otherSubscription.getType())
-                && mTrackingId.equals(otherSubscription.getTrackingId())
-                && mTrackingIdType.equals(otherSubscription.getTrackingIdType())
-                && mTimestamp == otherSubscription.getTimestamp();
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionJsonSerializer.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionJsonSerializer.java
deleted file mode 100644
index 4ba9d3f..0000000
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionJsonSerializer.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import org.chromium.base.Log;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.PriceTrackableOffer;
-
-import java.util.Locale;
-
-/**
- * Helpers for serializing and deserializing {@link CommerceSubscription} objects.
- */
-class CommerceSubscriptionJsonSerializer {
-    private static final String TAG = "CSJS";
-    private static final String SUBSCRIPTION_TYPE_KEY = "type";
-    private static final String SUBSCRIPTION_IDENTIFIER_KEY = "identifier";
-    private static final String SUBSCRIPTION_IDENTIFIER_TYPE_KEY = "identifierType";
-    private static final String SUBSCRIPTION_MANAGEMENT_TYPE_KEY = "managementType";
-    private static final String SUBSCRIPTION_TIMESTAMP_KEY = "eventTimestampMicros";
-    private static final String SUBSCRIPTION_SEEN_OFFER_KEY = "userSeenOffer";
-    private static final String SEEN_OFFER_ID_KEY = "offerId";
-    private static final String SEEN_OFFER_PRICE_KEY = "seenPriceMicros";
-    private static final String SEEN_OFFER_COUNTRY_KEY = "countryCode";
-
-    /** Creates a {@link CommerceSubscription} from a {@link JSONObject}. */
-    public static CommerceSubscription deserialize(JSONObject json) {
-        try {
-            return new CommerceSubscription(json.getString(SUBSCRIPTION_TYPE_KEY),
-                    json.getString(SUBSCRIPTION_IDENTIFIER_KEY),
-                    json.getString(SUBSCRIPTION_MANAGEMENT_TYPE_KEY),
-                    json.getString(SUBSCRIPTION_IDENTIFIER_TYPE_KEY),
-                    Long.parseLong(json.getString(SUBSCRIPTION_TIMESTAMP_KEY)));
-
-        } catch (JSONException e) {
-            Log.e(TAG,
-                    String.format(Locale.US,
-                            "Failed to deserialize CommerceSubscription. Details: %s",
-                            e.getMessage()));
-        }
-        return null;
-    }
-
-    /** Creates a {@link JSONObject}from a  {@link CommerceSubscription}. */
-    public static JSONObject serialize(CommerceSubscription subscription) {
-        try {
-            JSONObject subscriptionJson = new JSONObject();
-            subscriptionJson.put(SUBSCRIPTION_TYPE_KEY, subscription.getType());
-            subscriptionJson.put(
-                    SUBSCRIPTION_MANAGEMENT_TYPE_KEY, subscription.getManagementType());
-            subscriptionJson.put(
-                    SUBSCRIPTION_IDENTIFIER_TYPE_KEY, subscription.getTrackingIdType());
-            subscriptionJson.put(SUBSCRIPTION_IDENTIFIER_KEY, subscription.getTrackingId());
-
-            PriceTrackableOffer seenOffer = subscription.getSeenOffer();
-            if (CommerceSubscriptionsServiceConfig.shouldParseSeenOfferToServer()
-                    && seenOffer != null) {
-                JSONObject seenOfferJson = new JSONObject();
-                seenOfferJson.put(SEEN_OFFER_ID_KEY, seenOffer.offerId);
-                seenOfferJson.put(SEEN_OFFER_PRICE_KEY, seenOffer.currentPrice);
-                seenOfferJson.put(SEEN_OFFER_COUNTRY_KEY, seenOffer.countryCode);
-                subscriptionJson.put(SUBSCRIPTION_SEEN_OFFER_KEY, seenOfferJson);
-            }
-
-            return subscriptionJson;
-        } catch (JSONException e) {
-            Log.e(TAG,
-                    String.format(Locale.US,
-                            "Failed to serialize CommerceSubscription. Details: %s",
-                            e.getMessage()));
-        }
-
-        return null;
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsMetrics.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsMetrics.java
deleted file mode 100644
index 6494924c..0000000
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsMetrics.java
+++ /dev/null
@@ -1,103 +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.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.chrome.browser.preferences.Pref;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.SubscriptionManagementType;
-import org.chromium.components.prefs.PrefService;
-import org.chromium.components.signin.identitymanager.ConsentLevel;
-import org.chromium.components.user_prefs.UserPrefs;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * Commerce Subscriptions Metrics.
- */
-public class CommerceSubscriptionsMetrics {
-    @VisibleForTesting
-    public static final String SUBSCRIPTION_CHROME_MANAGED_COUNT_HISTOGRAM =
-            "Commerce.Subscriptions.ChromeManaged.Count";
-    @VisibleForTesting
-    public static final String SUBSCRIPTION_USER_MANAGED_COUNT_HISTOGRAM =
-            "Commerce.Subscriptions.UserManaged.Count";
-    @VisibleForTesting
-    public static final String ACCOUNT_WAA_STATUS_HISTOGRAM = "Commerce.SignIn.AccountWaaStatus";
-
-    /**
-     * The account web and app activity enabled status.
-     *
-     * Needs to stay in sync with AccountWaaStatusForCommerce in enums.xml. These values are
-     * persisted to logs. Entries should not be renumbered and numeric values should never be
-     * reused.
-     */
-    @IntDef({AccountWaaStatus.SIGN_OUT, AccountWaaStatus.SIGN_IN_WAA_DISABLED,
-            AccountWaaStatus.SIGN_IN_WAA_ENABLED, AccountWaaStatus.NUM_ENTRIES})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AccountWaaStatus {
-        int SIGN_OUT = 0;
-        int SIGN_IN_WAA_DISABLED = 1;
-        int SIGN_IN_WAA_ENABLED = 2;
-
-        // Must be the last one.
-        int NUM_ENTRIES = 3;
-    }
-
-    /**
-     * Record the number of subscriptions per management type.
-     */
-    void recordSubscriptionCounts(List<CommerceSubscription> subscriptions) {
-        int chromeManaged = 0;
-        int userManaged = 0;
-        for (CommerceSubscription subscription : subscriptions) {
-            @SubscriptionManagementType
-            String type = subscription.getManagementType();
-            if (SubscriptionManagementType.CHROME_MANAGED.equals(type)) {
-                chromeManaged++;
-            } else if (SubscriptionManagementType.USER_MANAGED.equals(type)) {
-                userManaged++;
-            }
-        }
-        RecordHistogram.recordCount1000Histogram(
-                SUBSCRIPTION_CHROME_MANAGED_COUNT_HISTOGRAM, chromeManaged);
-        RecordHistogram.recordCount1000Histogram(
-                SUBSCRIPTION_USER_MANAGED_COUNT_HISTOGRAM, userManaged);
-    }
-
-    void recordAccountWaaStatus() {
-        RecordHistogram.recordEnumeratedHistogram(
-                ACCOUNT_WAA_STATUS_HISTOGRAM, getAccountWaaStatus(), AccountWaaStatus.NUM_ENTRIES);
-    }
-
-    @AccountWaaStatus
-    private int getAccountWaaStatus() {
-        if (!isSignedIn()) {
-            return AccountWaaStatus.SIGN_OUT;
-        } else if (isWebAndAppActivityEnabled()) {
-            return AccountWaaStatus.SIGN_IN_WAA_ENABLED;
-        } else {
-            return AccountWaaStatus.SIGN_IN_WAA_DISABLED;
-        }
-    }
-
-    private boolean isSignedIn() {
-        return IdentityServicesProvider.get()
-                .getIdentityManager(Profile.getLastUsedRegularProfile())
-                .hasPrimaryAccount(ConsentLevel.SYNC);
-    }
-
-    private boolean isWebAndAppActivityEnabled() {
-        PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile());
-        return prefService != null
-                && prefService.getBoolean(Pref.WEB_AND_APP_ACTIVITY_ENABLED_FOR_SHOPPING);
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsService.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsService.java
index b0e750f6..dd1edf18 100644
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsService.java
+++ b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsService.java
@@ -12,47 +12,37 @@
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
 import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.components.signin.identitymanager.IdentityManager;
-import org.chromium.components.signin.identitymanager.PrimaryAccountChangeEvent;
+import org.chromium.components.commerce.core.ShoppingService;
 
 import java.util.concurrent.TimeUnit;
 
 /**
  * Commerce Subscriptions Service.
+ * TODO(crbug.com/1382191): This service is now only used to manage implicit tracking and to record
+ * notification metrics, both of which are Android-specific. The
+ * ImplicitPriceDropSubscriptionsManager should be profile-independent and we should decouple
+ * subscriptions and notifications. Some logic here like observing Android activity lifecycle can be
+ * moved to ShoppingServiceFactory.
  */
 public class CommerceSubscriptionsService {
     @VisibleForTesting
     public static final String CHROME_MANAGED_SUBSCRIPTIONS_TIMESTAMP =
             ChromePreferenceKeys.COMMERCE_SUBSCRIPTIONS_CHROME_MANAGED_TIMESTAMP;
 
-    private final SubscriptionsManagerImpl mSubscriptionManager;
-    private final IdentityManager mIdentityManager;
-    private final IdentityManager.Observer mIdentityManagerObserver;
     private final SharedPreferencesManager mSharedPreferencesManager;
     private final PriceDropNotificationManager mPriceDropNotificationManager;
-    private final CommerceSubscriptionsMetrics mMetrics;
     private ImplicitPriceDropSubscriptionsManager mImplicitPriceDropSubscriptionsManager;
     private ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
     private PauseResumeWithNativeObserver mPauseResumeWithNativeObserver;
+    private ShoppingService mShoppingService;
 
     /** Creates a new instance. */
-    CommerceSubscriptionsService(SubscriptionsManagerImpl subscriptionsManager,
-            IdentityManager identityManager,
+    CommerceSubscriptionsService(ShoppingService shoppingService,
             PriceDropNotificationManager priceDropNotificationManager) {
-        mSubscriptionManager = subscriptionsManager;
-        mIdentityManager = identityManager;
-        mIdentityManagerObserver = new IdentityManager.Observer() {
-            @Override
-            public void onPrimaryAccountChanged(PrimaryAccountChangeEvent eventDetails) {
-                mSubscriptionManager.onIdentityChanged();
-            }
-        };
-        mIdentityManager.addObserver(mIdentityManagerObserver);
+        mShoppingService = shoppingService;
         mSharedPreferencesManager = SharedPreferencesManager.getInstance();
         mPriceDropNotificationManager = priceDropNotificationManager;
-        mMetrics = new CommerceSubscriptionsMetrics();
     }
 
     /** Performs any deferred startup tasks required by {@link Subscriptions}. */
@@ -72,21 +62,15 @@
 
         if (CommerceSubscriptionsServiceConfig.isImplicitSubscriptionsEnabled()
                 && mImplicitPriceDropSubscriptionsManager == null) {
-            mImplicitPriceDropSubscriptionsManager = new ImplicitPriceDropSubscriptionsManager(
-                    tabModelSelector, mSubscriptionManager);
+            mImplicitPriceDropSubscriptionsManager =
+                    new ImplicitPriceDropSubscriptionsManager(tabModelSelector, mShoppingService);
         }
     }
 
-    /** Returns the subscriptionsManager. */
-    public SubscriptionsManagerImpl getSubscriptionsManager() {
-        return mSubscriptionManager;
-    }
-
     /**
      * Cleans up internal resources. Currently this method calls SubscriptionsManagerImpl#destroy.
      */
     public void destroy() {
-        mIdentityManager.removeObserver(mIdentityManagerObserver);
         if (mActivityLifecycleDispatcher != null) {
             mActivityLifecycleDispatcher.unregister(mPauseResumeWithNativeObserver);
         }
@@ -106,7 +90,6 @@
         }
         mSharedPreferencesManager.writeLong(
                 CHROME_MANAGED_SUBSCRIPTIONS_TIMESTAMP, System.currentTimeMillis());
-        mMetrics.recordAccountWaaStatus();
         if (!PriceTrackingFeatures.isPriceDropNotificationEligible()) return;
         recordMetricsForEligibleAccount();
         if (mImplicitPriceDropSubscriptionsManager != null) {
@@ -118,8 +101,6 @@
         // Record notification opt-in metrics.
         mPriceDropNotificationManager.canPostNotificationWithMetricsRecorded();
         mPriceDropNotificationManager.recordMetricsForNotificationCounts();
-        mSubscriptionManager.getSubscriptions(
-                CommerceSubscriptionType.PRICE_TRACK, false, mMetrics::recordSubscriptionCounts);
     }
 
     @VisibleForTesting
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceConfig.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceConfig.java
index 870ebac..5b99d73a 100644
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceConfig.java
+++ b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceConfig.java
@@ -4,22 +4,15 @@
 
 package org.chromium.chrome.browser.subscriptions;
 
-import android.text.TextUtils;
-
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.FeatureList;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 
 import java.util.concurrent.TimeUnit;
+
 /** Flag configuration for Commerce Subscriptions Service. */
 public class CommerceSubscriptionsServiceConfig {
-    private static final String sServiceBaseUrl =
-            "https://memex-pa.googleapis.com/v1/shopping/subscriptions";
-
-    @VisibleForTesting
-    private static final String sBaseUrlParam = "subscriptions_service_base_url";
-
     @VisibleForTesting
     private static final String STALE_TAB_LOWER_BOUND_SECONDS_PARAM =
             "price_tracking_stale_tab_lower_bound_seconds";
@@ -28,21 +21,8 @@
     public static final String IMPLICIT_SUBSCRIPTIONS_ENABLED_PARAM =
             "implicit_subscriptions_enabled";
 
-    private static final String PARSE_SEEN_OFFER_TO_SERVER_PARAM =
-            "price_tracking_parse_seen_offer_to_server";
-
     private static final int DEFAULT_STALE_TAB_LOWER_BOUND_DAYS = 1;
 
-    public static String getDefaultServiceUrl() {
-        String defaultValue = sServiceBaseUrl;
-        if (FeatureList.isInitialized()) {
-            defaultValue = ChromeFeatureList.getFieldTrialParamByFeature(
-                    ChromeFeatureList.COMMERCE_PRICE_TRACKING, sBaseUrlParam);
-        }
-
-        return TextUtils.isEmpty(defaultValue) ? sServiceBaseUrl : defaultValue;
-    }
-
     public static int getStaleTabLowerBoundSeconds() {
         int defaultValue = (int) TimeUnit.DAYS.toSeconds(DEFAULT_STALE_TAB_LOWER_BOUND_DAYS);
         if (FeatureList.isInitialized()) {
@@ -61,13 +41,4 @@
         }
         return false;
     }
-
-    public static boolean shouldParseSeenOfferToServer() {
-        if (FeatureList.isInitialized()) {
-            return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                    ChromeFeatureList.COMMERCE_PRICE_TRACKING, PARSE_SEEN_OFFER_TO_SERVER_PARAM,
-                    true);
-        }
-        return true;
-    }
 }
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceFactory.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceFactory.java
index 3fc2d89..cf59608 100644
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceFactory.java
+++ b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceFactory.java
@@ -6,11 +6,11 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
 import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
 import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManagerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -67,9 +67,7 @@
             PriceDropNotificationManager priceDropNotificationManager =
                     PriceDropNotificationManagerFactory.create();
             service = new CommerceSubscriptionsService(
-                    new SubscriptionsManagerImpl(profile, priceDropNotificationManager),
-                    IdentityServicesProvider.get().getIdentityManager(profile),
-                    priceDropNotificationManager);
+                    ShoppingServiceFactory.getForProfile(profile), priceDropNotificationManager);
             sProfileToSubscriptionsService.put(profile, service);
         }
         return service;
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceProxy.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceProxy.java
deleted file mode 100644
index 4d5f3a6..0000000
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceProxy.java
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import org.chromium.base.Callback;
-import org.chromium.base.Log;
-import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
-import org.chromium.chrome.browser.preferences.Pref;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.components.prefs.PrefService;
-import org.chromium.components.user_prefs.UserPrefs;
-import org.chromium.net.NetworkTrafficAnnotationTag;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-/**
- * Wrapper around CommerceSubscriptions Web APIs.
- */
-public class CommerceSubscriptionsServiceProxy {
-    private static final String TAG = "CSSP";
-    private static final long HTTPS_REQUEST_TIMEOUT_MS = 1000L;
-    private static final String GET_HTTPS_METHOD = "GET";
-    private static final String POST_HTTPS_METHOD = "POST";
-    private static final String CONTENT_TYPE = "application/json; charset=UTF-8";
-    private static final String EMPTY_POST_DATA = "";
-    private static final String[] OAUTH_SCOPE =
-            new String[] {"https://www.googleapis.com/auth/chromememex"};
-    private static final String OAUTH_NAME = "susbcriptions_svc";
-    private static final String STATUS_KEY = "status";
-    private static final String STATUS_CODE_KEY = "code";
-    private static final String REMOVE_SUBSCRIPTIONS_REQUEST_PARAMS_KEY =
-            "removeShoppingSubscriptionsParams";
-    private static final String CREATE_SUBSCRIPTIONS_REQUEST_PARAMS_KEY =
-            "createShoppingSubscriptionsParams";
-    private static final String EVENT_TIMESTAMP_MICROS_KEY = "eventTimestampMicros";
-    private static final String SUBSCRIPTIONS_KEY = "subscriptions";
-    private static final String GET_SUBSCRIPTIONS_QUERY_PARAMS_TEMPLATE =
-            "?requestParams.subscriptionType=%s";
-    private static final int BACKEND_CANONICAL_CODE_SUCCESS = 0;
-
-    // TODO(crbug.com/1311754): These parameters (url, OAUTH_SCOPE, etc.) are copied from
-    // web_history_service.cc directly, it works now but we should figure out a better way to
-    // keep these parameters in sync.
-    private static final String WAA_QUERY_URL =
-            "https://history.google.com/history/api/lookup?client=web_app";
-    private static final String[] WAA_OAUTH_SCOPE =
-            new String[] {"https://www.googleapis.com/auth/chromesync"};
-    private static final String WAA_RESPONSE_KEY = "history_recording_enabled";
-    private static final String WAA_OAUTH_NAME = "web_history";
-
-    private final Profile mProfile;
-
-    /**
-     * Creates a new instance.
-     * @param profile the {@link Profile} to use when making the calls.
-     */
-    public CommerceSubscriptionsServiceProxy(Profile profile) {
-        mProfile = profile;
-    }
-
-    /**
-     * Makes an HTTPS call to the backend in order to create the provided subscriptions.
-     * @param subscriptions list of {@link CommerceSubscription} to create.
-     * @param callback indicates whether or not the operation succeeded on the backend.
-     */
-    public void create(List<CommerceSubscription> subscriptions, Callback<Boolean> callback) {
-        if (subscriptions.isEmpty()) {
-            callback.onResult(true);
-            return;
-        }
-
-        manageSubscriptions(getCreateSubscriptionsRequestParams(subscriptions), callback);
-    }
-
-    /**
-     * Makes an HTTPS call to the backend to delete the provided list of subscriptions.
-     * @param subscriptions list of {@link CommerceSubscription} to delete.
-     * @param callback indicates whether or not the operation succeeded on the backend.
-     */
-    public void delete(List<CommerceSubscription> subscriptions, Callback<Boolean> callback) {
-        if (subscriptions.isEmpty()) {
-            callback.onResult(true);
-            return;
-        }
-
-        manageSubscriptions(getRemoveSubscriptionsRequestParams(subscriptions), callback);
-    }
-
-    /**
-     * Fetches all subscriptions that match the provided type from the backend.
-     * @param type the type of subscriptions to fetch.
-     * @param callback contains the list of subscriptions returned from the server.
-     */
-    public void get(@CommerceSubscription.CommerceSubscriptionType String type,
-            Callback<List<CommerceSubscription>> callback) {
-        // TODO(crbug.com/995852): Replace MISSING_TRAFFIC_ANNOTATION with a real traffic
-        // annotation.
-        EndpointFetcher.fetchUsingOAuth(
-                (response)
-                        -> {
-                    callback.onResult(createCommerceSubscriptions(response.getResponseString()));
-                },
-                mProfile, OAUTH_NAME,
-                CommerceSubscriptionsServiceConfig.getDefaultServiceUrl()
-                        + String.format(GET_SUBSCRIPTIONS_QUERY_PARAMS_TEMPLATE, type),
-                GET_HTTPS_METHOD, CONTENT_TYPE, OAUTH_SCOPE, EMPTY_POST_DATA,
-                HTTPS_REQUEST_TIMEOUT_MS, NetworkTrafficAnnotationTag.MISSING_TRAFFIC_ANNOTATION);
-    }
-
-    void queryAndUpdateWaaEnabled() {
-        // TODO(crbug.com/1311754): Move the endpoint fetch to components/ and merge this query to
-        // shopping service. For NetworkTrafficAnnotationTag, we need to replace
-        // MISSING_TRAFFIC_ANNOTATION with the correct NetworkTrafficAnnotation.
-        EndpointFetcher.fetchUsingOAuth(
-                (response)
-                        -> {
-                    try {
-                        JSONObject object = new JSONObject(response.getResponseString());
-                        boolean isWaaEnabled = object.getBoolean(WAA_RESPONSE_KEY);
-                        PrefService prefService = UserPrefs.get(mProfile);
-                        if (prefService != null) {
-                            prefService.setBoolean(
-                                    Pref.WEB_AND_APP_ACTIVITY_ENABLED_FOR_SHOPPING, isWaaEnabled);
-                        }
-                    } catch (JSONException e) {
-                        Log.e(TAG,
-                                String.format(Locale.US, "Failed to get waa status. Details: %s",
-                                        e.getMessage()));
-                    }
-                },
-                mProfile, WAA_OAUTH_NAME, WAA_QUERY_URL, GET_HTTPS_METHOD, CONTENT_TYPE,
-                WAA_OAUTH_SCOPE, EMPTY_POST_DATA, 30000L,
-                NetworkTrafficAnnotationTag.MISSING_TRAFFIC_ANNOTATION);
-    }
-
-    private void manageSubscriptions(JSONObject requestPayload, Callback<Boolean> callback) {
-        // TODO(crbug.com/995852): Replace MISSING_TRAFFIC_ANNOTATION with a real traffic
-        // annotation.
-        EndpointFetcher.fetchUsingOAuth(
-                (response)
-                        -> {
-                    callback.onResult(
-                            didManageSubscriptionCallSucceed(response.getResponseString()));
-                },
-                mProfile, OAUTH_NAME, CommerceSubscriptionsServiceConfig.getDefaultServiceUrl(),
-                POST_HTTPS_METHOD, CONTENT_TYPE, OAUTH_SCOPE, requestPayload.toString(),
-                HTTPS_REQUEST_TIMEOUT_MS, NetworkTrafficAnnotationTag.MISSING_TRAFFIC_ANNOTATION);
-    }
-
-    private boolean didManageSubscriptionCallSucceed(String responseString) {
-        try {
-            JSONObject response = new JSONObject(responseString);
-            JSONObject statusJson = response.getJSONObject(STATUS_KEY);
-            int statusCode = statusJson.getInt(STATUS_CODE_KEY);
-            return statusCode == BACKEND_CANONICAL_CODE_SUCCESS;
-        } catch (JSONException e) {
-            Log.e(TAG,
-                    String.format(Locale.US,
-                            "Failed to create CreateSubscriptionRequestParams. Details: %s",
-                            e.getMessage()));
-        }
-
-        return false;
-    }
-
-    private JSONObject getCreateSubscriptionsRequestParams(
-            List<CommerceSubscription> subscriptions) {
-        JSONObject container = new JSONObject();
-        JSONArray subscriptionsJsonArray = new JSONArray();
-        try {
-            for (CommerceSubscription subscription : subscriptions) {
-                subscriptionsJsonArray.put(
-                        CommerceSubscriptionJsonSerializer.serialize(subscription));
-            }
-
-            JSONObject subscriptionsObject = new JSONObject();
-            subscriptionsObject.put(SUBSCRIPTIONS_KEY, subscriptionsJsonArray);
-
-            container.put(CREATE_SUBSCRIPTIONS_REQUEST_PARAMS_KEY, subscriptionsObject);
-        } catch (JSONException e) {
-            Log.e(TAG,
-                    String.format(Locale.US,
-                            "Failed to create CreateSubscriptionRequestParams. Details: %s",
-                            e.getMessage()));
-        }
-
-        return container;
-    }
-
-    private JSONObject getRemoveSubscriptionsRequestParams(
-            List<CommerceSubscription> subscriptions) {
-        JSONObject container = new JSONObject();
-        try {
-            JSONObject removeSubscriptionsParamsJson = new JSONObject();
-            JSONArray subscriptionsTimestamps = new JSONArray();
-            for (CommerceSubscription subscription : subscriptions) {
-                if (subscription.getTimestamp() == CommerceSubscription.UNSAVED_SUBSCRIPTION) {
-                    continue;
-                }
-                subscriptionsTimestamps.put(subscription.getTimestamp());
-            }
-            removeSubscriptionsParamsJson.put(EVENT_TIMESTAMP_MICROS_KEY, subscriptionsTimestamps);
-            container.put(REMOVE_SUBSCRIPTIONS_REQUEST_PARAMS_KEY, removeSubscriptionsParamsJson);
-        } catch (JSONException e) {
-            Log.e(TAG,
-                    String.format(Locale.US,
-                            "Failed to create RemoveSubscriptionsRequestParams. Details: %s",
-                            e.getMessage()));
-        }
-
-        return container;
-    }
-
-    private List<CommerceSubscription> createCommerceSubscriptions(String responseString) {
-        List<CommerceSubscription> subscriptions = new ArrayList<>();
-        try {
-            JSONObject response = new JSONObject(responseString);
-            JSONArray subscriptionsJsonArray = response.getJSONArray(SUBSCRIPTIONS_KEY);
-
-            for (int i = 0; i < subscriptionsJsonArray.length(); i++) {
-                JSONObject subscriptionJson = subscriptionsJsonArray.getJSONObject(i);
-                CommerceSubscription subscription =
-                        CommerceSubscriptionJsonSerializer.deserialize(subscriptionJson);
-                if (subscription != null) {
-                    subscriptions.add(subscription);
-                }
-            }
-        } catch (JSONException e) {
-            Log.e(TAG,
-                    String.format(Locale.US,
-                            "Failed to deserialize Subscriptions list. Details: %s",
-                            e.getMessage()));
-        }
-
-        return subscriptions;
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsStorage.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsStorage.java
deleted file mode 100644
index a899c88..0000000
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsStorage.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.Callback;
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.content_public.browser.BrowserContextHandle;
-
-import java.util.List;
-
-/**
- * Provides storage for commerce subscription data.
- */
-public class CommerceSubscriptionsStorage {
-    private long mNativeCommerceSubscriptionDB;
-
-    CommerceSubscriptionsStorage(Profile profile) {
-        assert !profile.isOffTheRecord()
-            : "CommerceSubscriptionsStorage is not supported for incognito profiles";
-        CommerceSubscriptionsStorageJni.get().init(this, profile);
-        assert mNativeCommerceSubscriptionDB != 0;
-    }
-
-    /**
-     * Save one subscription to the database.
-     * @param subscription The {@link CommerceSubscription} to store.
-     */
-    public void save(CommerceSubscription subscription) {
-        assert mNativeCommerceSubscriptionDB != 0;
-        saveWithCallback(subscription, null);
-    }
-
-    @MainThread
-    @VisibleForTesting
-    public void saveWithCallback(CommerceSubscription subscription, Runnable onComplete) {
-        assert mNativeCommerceSubscriptionDB != 0;
-        CommerceSubscriptionsStorageJni.get().save(mNativeCommerceSubscriptionDB,
-                getKey(subscription), subscription.getType(), subscription.getTrackingId(),
-                subscription.getManagementType(), subscription.getTrackingIdType(),
-                subscription.getTimestamp(), onComplete);
-    }
-
-    /**
-     * Load one subscription from the database.
-     * @param key The key used to identify a subscription.
-     * @param callback A callback with loaded result.
-     */
-    public void load(String key, Callback<CommerceSubscription> callback) {
-        assert mNativeCommerceSubscriptionDB != 0;
-        CommerceSubscriptionsStorageJni.get().load(mNativeCommerceSubscriptionDB, key, callback);
-    }
-
-    /**
-     * Load all subscriptions whose keys have specific prefix.
-     * @param prefix The prefix used to identify subscriptions.
-     * @param callback A callback with loaded results.
-     */
-    public void loadWithPrefix(String prefix, Callback<List<CommerceSubscription>> callback) {
-        assert mNativeCommerceSubscriptionDB != 0;
-        CommerceSubscriptionsStorageJni.get().loadWithPrefix(
-                mNativeCommerceSubscriptionDB, prefix, callback);
-    }
-
-    /**
-     * Delete one subscription from the database.
-     * @param subscription The {@link CommerceSubscription} to delete.
-     */
-    public void delete(CommerceSubscription subscription) {
-        assert mNativeCommerceSubscriptionDB != 0;
-        CommerceSubscriptionsStorageJni.get().delete(
-                mNativeCommerceSubscriptionDB, getKey(subscription), null);
-    }
-
-    @MainThread
-    @VisibleForTesting
-    public void deleteForTesting(CommerceSubscription subscription, Runnable onComplete) {
-        assert mNativeCommerceSubscriptionDB != 0;
-        CommerceSubscriptionsStorageJni.get().delete(
-                mNativeCommerceSubscriptionDB, getKey(subscription), onComplete);
-    }
-
-    /**
-     * Delete all subscriptions from the database.
-     */
-    public void deleteAll() {
-        assert mNativeCommerceSubscriptionDB != 0;
-        CommerceSubscriptionsStorageJni.get().deleteAll(mNativeCommerceSubscriptionDB, null);
-    }
-
-    @MainThread
-    @VisibleForTesting
-    public void deleteAllForTesting(Runnable onComplete) {
-        assert mNativeCommerceSubscriptionDB != 0;
-        CommerceSubscriptionsStorageJni.get().deleteAll(mNativeCommerceSubscriptionDB, onComplete);
-    }
-
-    /**
-     * Destroy the database.
-     */
-    public void destroy() {
-        assert mNativeCommerceSubscriptionDB != 0;
-        CommerceSubscriptionsStorageJni.get().destroy(mNativeCommerceSubscriptionDB);
-    }
-
-    @CalledByNative
-    private void setNativePtr(long nativePtr) {
-        assert nativePtr != 0;
-        assert mNativeCommerceSubscriptionDB == 0;
-        mNativeCommerceSubscriptionDB = nativePtr;
-    }
-
-    @VisibleForTesting
-    public void setNativeCommerceSubscriptionDBForTesting(long nativeCommerceSubscriptionDB) {
-        mNativeCommerceSubscriptionDB = nativeCommerceSubscriptionDB;
-    }
-
-    /**
-     * Generate the key for a {@link CommerceSubscription} used to store it in database.
-     * @param subscription The {@link CommerceSubscription} whose key we want to generate.
-     */
-    public static String getKey(CommerceSubscription subscription) {
-        return String.format("%s_%s_%s", subscription.getType(), subscription.getTrackingIdType(),
-                subscription.getTrackingId());
-    }
-
-    @NativeMethods
-    interface Natives {
-        void init(CommerceSubscriptionsStorage caller, BrowserContextHandle handle);
-        void destroy(long nativeCommerceSubscriptionDB);
-        void save(long nativeCommerceSubscriptionDB, String key, String type, String trackingId,
-                String managementType, String trackingIdType, long timestamp, Runnable onComplete);
-        void load(long nativeCommerceSubscriptionDB, String key,
-                Callback<CommerceSubscription> callback);
-        void loadWithPrefix(long nativeCommerceSubscriptionDB, String key,
-                Callback<List<CommerceSubscription>> callback);
-        void delete(long nativeCommerceSubscriptionDB, String key, Runnable onComplete);
-        void deleteAll(long nativeCommerceSubscriptionDB, Runnable onComplete);
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManager.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManager.java
index 9720761a..1e63dd5 100644
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManager.java
+++ b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManager.java
@@ -10,15 +10,17 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.SubscriptionManagementType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.TrackingIdType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData;
 import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.IdentifierType;
+import org.chromium.components.commerce.core.ManagementType;
+import org.chromium.components.commerce.core.ShoppingService;
+import org.chromium.components.commerce.core.SubscriptionType;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -30,11 +32,11 @@
 public class ImplicitPriceDropSubscriptionsManager {
     private final TabModelSelector mTabModelSelector;
     private final TabModelObserver mTabModelObserver;
-    private final SubscriptionsManagerImpl mSubscriptionManager;
+    private final ShoppingService mShoppingService;
 
     public ImplicitPriceDropSubscriptionsManager(
-            TabModelSelector tabModelSelector, SubscriptionsManagerImpl subscriptionsManager) {
-        mSubscriptionManager = subscriptionsManager;
+            TabModelSelector tabModelSelector, ShoppingService shoppingService) {
+        mShoppingService = shoppingService;
         mTabModelSelector = tabModelSelector;
         mTabModelObserver = new TabModelObserver() {
             @Override
@@ -88,12 +90,12 @@
                 String url = tab.getOriginalUrl().getSpec();
                 if (urlSet.contains(url)) return;
                 urlSet.add(url);
-                CommerceSubscription subscription =
-                        new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK, offerId,
-                                SubscriptionManagementType.CHROME_MANAGED, TrackingIdType.OFFER_ID);
-                mSubscriptionManager.subscribe(subscription, (status) -> {
+                CommerceSubscription subscription = new CommerceSubscription(
+                        SubscriptionType.PRICE_TRACK, IdentifierType.OFFER_ID, offerId,
+                        ManagementType.CHROME_MANAGED, null);
+                mShoppingService.subscribe(subscription, (status) -> {
                     // TODO: Add histograms for implicit tabs creation.
-                    assert status == SubscriptionsManager.StatusCode.OK;
+                    assert status;
                 });
             });
         }
@@ -105,10 +107,9 @@
         fetchOfferId(tab, (offerId) -> {
             if (offerId == null) return;
             CommerceSubscription subscription =
-                    new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK, offerId,
-                            SubscriptionManagementType.CHROME_MANAGED, TrackingIdType.OFFER_ID);
-            mSubscriptionManager.unsubscribe(subscription,
-                    (status) -> { assert status == SubscriptionsManager.StatusCode.OK; });
+                    new CommerceSubscription(SubscriptionType.PRICE_TRACK, IdentifierType.OFFER_ID,
+                            offerId, ManagementType.CHROME_MANAGED, null);
+            mShoppingService.unsubscribe(subscription, (status) -> { assert status; });
         });
     }
 
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManager.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManager.java
deleted file mode 100644
index 2aa27c28..0000000
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManager.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import androidx.annotation.IntDef;
-
-import org.chromium.base.Callback;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * Interface for exposing {@link CommerceSubscription} management.
- */
-public interface SubscriptionsManager {
-    @IntDef({StatusCode.OK, StatusCode.NETWORK_ERROR, StatusCode.INTERNAL_ERROR,
-            StatusCode.INVALID_ARGUMENT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface StatusCode {
-        int OK = 0;
-        int NETWORK_ERROR = 2;
-        int INTERNAL_ERROR = 3;
-        int INVALID_ARGUMENT = 4;
-    }
-
-    /** An observer for notification about a product be tracked or untracked. */
-    interface SubscriptionObserver {
-        /**
-         * A notification that a user has subscribed to product updated.
-         *
-         * @param subscriptions The list of subscriptions being added.
-         */
-        void onSubscribe(List<CommerceSubscription> subscriptions);
-
-        /**
-         * A notification that a user has unsubscribed to product updated.
-         *
-         * @param subscriptions The list of subscriptions being removed.
-         */
-        void onUnsubscribe(List<CommerceSubscription> subscriptions);
-    }
-
-    /**
-     * Creates a new subscription on the server if needed.
-     * @param subscription The {@link CommerceSubscription} to add.
-     * @param callback indicates whether or not the operation was successful.
-     */
-    void subscribe(CommerceSubscription subscription, Callback<Integer> callback);
-
-    /**
-     * Creates new subscriptions in batch if needed.
-     * @param subscriptions The list of {@link CommerceSubscription} to add.
-     * @param callback indicates whether or not the operation was successful.
-     */
-    void subscribe(List<CommerceSubscription> subscriptions, Callback<Integer> callback);
-
-    /**
-     * Destroys a subscription on the server if needed.
-     * @param subscription The {@link CommerceSubscription} to destroy.
-     * @param callback indicates whether or not the operation was successful.
-     */
-    void unsubscribe(CommerceSubscription subscription, Callback<Integer> callback);
-
-    /**
-     * This is batched version of {@link #unsubscribe(CommerceSubscription, Callback)}.
-     * @param subscriptions The list of subscription objects to unsubscribe from.
-     * @param callback A callback for whether the operation completed successfully.
-     */
-    void unsubscribe(List<CommerceSubscription> subscriptions, Callback<Integer> callback);
-
-    /**
-     * Returns all subscriptions that match the provided type.
-     * @param type The {@link CommerceSubscription.CommerceSubscriptionType} to query.
-     * @param forceFetch Whether to fetch from server.
-     * @param callback returns the list of subscriptions.
-     */
-    void getSubscriptions(@CommerceSubscription.CommerceSubscriptionType String type,
-            boolean forceFetch, Callback<List<CommerceSubscription>> callback);
-
-    /**
-     * Checks if the given subscription matches any subscriptions in local storage.
-     *
-     * @param subscription The subscription to check.
-     * @param callback The callback to receive the result.
-     */
-    void isSubscribed(CommerceSubscription subscription, Callback<Boolean> callback);
-
-    /**
-     * Add an observer of subscribe and unsubscribe events.
-     *
-     * @param observer The observer to add.
-     */
-    void addObserver(SubscriptionObserver observer);
-
-    /**
-     * Remove an observer of subscribe and unsubscribe events.
-     *
-     * @param observer The observer to remove.
-     */
-    void removeObserver(SubscriptionObserver observer);
-}
diff --git a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManagerImpl.java b/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManagerImpl.java
deleted file mode 100644
index da5055a..0000000
--- a/chrome/browser/commerce/subscriptions/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManagerImpl.java
+++ /dev/null
@@ -1,439 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import android.os.Build;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.Callback;
-import org.chromium.base.ObserverList;
-import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
-import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
-import org.chromium.chrome.browser.profiles.Profile;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-
-/**
- * Implementation of {@link SubscriptionsManager} to manage price drop related subscriptions.
- * TODO(crbug.com/1186450): Pull subscription type specific code into respective handlers to
- * simplify this class.
- */
-public class SubscriptionsManagerImpl implements SubscriptionsManager {
-    @IntDef({Operation.SUBSCRIBE, Operation.UNSUBSCRIBE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Operation {
-        int SUBSCRIBE = 0;
-        int UNSUBSCRIBE = 1;
-    }
-
-    private final CommerceSubscriptionsStorage mStorage;
-    private final CommerceSubscriptionsServiceProxy mServiceProxy;
-    private static List<CommerceSubscription> sRemoteSubscriptionsForTesting;
-    private boolean mCanHandleRequests;
-    private Queue<DeferredSubscriptionOperation> mDeferredTasks;
-    private final ObserverList<SubscriptionObserver> mObservers;
-    private final PriceDropNotificationManager mPriceDropNotificationManager;
-
-    private static class DeferredSubscriptionOperation {
-        private final @Operation int mOperation;
-        private final List<CommerceSubscription> mSubscriptions;
-        private final Callback<Integer> mCallback;
-
-        public DeferredSubscriptionOperation(@Operation int operation,
-                List<CommerceSubscription> subscriptions, Callback<Integer> callback) {
-            mOperation = operation;
-            mSubscriptions = subscriptions;
-            mCallback = callback;
-        }
-
-        public @Operation int getOperation() {
-            return mOperation;
-        }
-
-        public List<CommerceSubscription> getSubscriptions() {
-            return mSubscriptions;
-        }
-
-        public Callback<Integer> getCallback() {
-            return mCallback;
-        }
-    }
-
-    public SubscriptionsManagerImpl(
-            Profile profile, PriceDropNotificationManager priceDropNotificationManager) {
-        this(profile, new CommerceSubscriptionsStorage(profile),
-                new CommerceSubscriptionsServiceProxy(profile), priceDropNotificationManager);
-    }
-
-    @VisibleForTesting
-    SubscriptionsManagerImpl(Profile profile, CommerceSubscriptionsStorage storage,
-            CommerceSubscriptionsServiceProxy proxy,
-            PriceDropNotificationManager priceDropNotificationManager) {
-        mStorage = storage;
-        mServiceProxy = proxy;
-        mPriceDropNotificationManager = priceDropNotificationManager;
-        mDeferredTasks = new LinkedList<>();
-        mCanHandleRequests = false;
-        initTypes(this::onInitComplete);
-        mObservers = new ObserverList<>();
-    }
-
-    @Override
-    public void addObserver(SubscriptionObserver observer) {
-        mObservers.addObserver(observer);
-    }
-
-    @Override
-    public void removeObserver(SubscriptionObserver observer) {
-        mObservers.removeObserver(observer);
-    }
-
-    /**
-     * Creates a new subscription on the server-side and refreshes the local storage of
-     * subscriptions.
-     * @param subscription The {@link CommerceSubscription} to add.
-     * @param callback indicates whether or not the operation was successful.
-     */
-    @Override
-    public void subscribe(CommerceSubscription subscription, Callback<Integer> callback) {
-        if (subscription == null || !isSubscriptionTypeSupported(subscription.getType())) {
-            callback.onResult(SubscriptionsManager.StatusCode.INVALID_ARGUMENT);
-            return;
-        }
-
-        subscribe(new ArrayList<CommerceSubscription>() {
-            { add(subscription); };
-        }, callback);
-    }
-
-    /**
-     * Creates new subscriptions in batch if needed.
-     * @param subscriptions The list of {@link CommerceSubscription} to add.
-     * @param callback indicates whether or not the operation was successful.
-     */
-    @Override
-    public void subscribe(List<CommerceSubscription> subscriptions, Callback<Integer> callback) {
-        if (subscriptions.size() == 0) {
-            callback.onResult(SubscriptionsManager.StatusCode.OK);
-            return;
-        }
-
-        // Wrap the callback in one that allows us to trigger the observers.
-        Callback<Integer> wrappedCallback = (status) -> {
-            if (status == StatusCode.OK) {
-                for (SubscriptionObserver o : mObservers) {
-                    o.onSubscribe(subscriptions);
-                }
-            }
-            callback.onResult(status);
-        };
-
-        String type = subscriptions.get(0).getType();
-        if (!isSubscriptionTypeSupported(type)) {
-            wrappedCallback.onResult(SubscriptionsManager.StatusCode.INVALID_ARGUMENT);
-            return;
-        }
-
-        // Make sure the notification channel is initialized if there is a user-managed PRICE_TRACK
-        // subscription. For chrome-managed subscriptions, channel will be initialized via message
-        // card in tab switcher.
-        if (CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK.equals(type)
-                && CommerceSubscription.SubscriptionManagementType.USER_MANAGED.equals(
-                        subscriptions.get(0).getManagementType())
-                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            mPriceDropNotificationManager.createNotificationChannel();
-        }
-
-        if (!mCanHandleRequests) {
-            mDeferredTasks.add(new DeferredSubscriptionOperation(
-                    Operation.SUBSCRIBE, subscriptions, wrappedCallback));
-            return;
-        }
-
-        getUniqueSubscriptions(subscriptions, (list) -> {
-            if (list.size() == 0) {
-                wrappedCallback.onResult(SubscriptionsManager.StatusCode.OK);
-            } else {
-                mServiceProxy.create(list,
-                        (didSucceed)
-                                -> handleUpdateSubscriptionsResponse(
-                                        didSucceed, type, wrappedCallback));
-            }
-        });
-    }
-
-    /**
-     * Destroys a subscription on the server-side and refreshes the local storage of subscriptions.
-     * @param subscription The {@link CommerceSubscription} to destroy.
-     * @param callback indicates whether or not the operation was successful.
-     */
-    @Override
-    public void unsubscribe(CommerceSubscription subscription, Callback<Integer> callback) {
-        String type = subscription.getType();
-        if (subscription == null || !isSubscriptionTypeSupported(type)) {
-            callback.onResult(SubscriptionsManager.StatusCode.INVALID_ARGUMENT);
-            return;
-        }
-        unsubscribe(new ArrayList<CommerceSubscription>() {
-            { add(subscription); };
-        }, callback);
-    }
-
-    /**
-     * Returns all subscriptions that match the provided type.
-     * @param type The {@link CommerceSubscription.CommerceSubscriptionType} to query.
-     * @param forceFetch Whether to fetch from server. If no, fetch from local storage.
-     * @param callback returns the list of subscriptions.
-     */
-    @Override
-    public void getSubscriptions(@CommerceSubscription.CommerceSubscriptionType String type,
-            boolean forceFetch, Callback<List<CommerceSubscription>> callback) {
-        if (sRemoteSubscriptionsForTesting != null) {
-            callback.onResult(sRemoteSubscriptionsForTesting);
-            return;
-        }
-        if (forceFetch) {
-            mServiceProxy.get(type, callback);
-        } else {
-            mStorage.loadWithPrefix(String.valueOf(type),
-                    localSubscriptions -> callback.onResult(localSubscriptions));
-        }
-    }
-
-    /**
-     * Checks if the given subscription matches any subscriptions in local storage.
-     *
-     * @param subscription The subscription to check.
-     * @param callback The callback to receive the result.
-     */
-    @Override
-    public void isSubscribed(CommerceSubscription subscription, Callback<Boolean> callback) {
-        if (subscription == null) {
-            callback.onResult(false);
-            return;
-        }
-
-        // Searching by prefix instead of loading by key to handle cases of duplicates.
-        String targetKey = CommerceSubscriptionsStorage.getKey(subscription);
-        mStorage.loadWithPrefix(targetKey, localSubscriptions -> {
-            // TODO: (crbug/1279519) CommerceSubscriptionsStorage should support full key matching
-            // and we shouldn't need to perform this additional check.
-            for (CommerceSubscription current : localSubscriptions) {
-                if (targetKey.equals(CommerceSubscriptionsStorage.getKey(current))) {
-                    callback.onResult(true);
-                    return;
-                }
-            }
-            callback.onResult(false);
-        });
-    }
-
-    /**
-     * Called when user account is cleared or updated.
-     */
-    void onIdentityChanged() {
-        mStorage.deleteAll();
-        // If the feature is still eligible to work, we should re-init and fetch the fresh data.
-        if (PriceTrackingFeatures.isPriceDropNotificationEligible()) {
-            initTypes((status) -> { assert status == SubscriptionsManager.StatusCode.OK; });
-            queryAndUpdateWaaEnabled();
-        }
-    }
-
-    /**
-     * Query whether web and app activity is enabled on the server and update the local pref value.
-     */
-    public void queryAndUpdateWaaEnabled() {
-        mServiceProxy.queryAndUpdateWaaEnabled();
-    }
-
-    @Override
-    public void unsubscribe(List<CommerceSubscription> subscriptions, Callback<Integer> callback) {
-        String type = subscriptions.get(0).getType();
-        if (subscriptions == null || !isSubscriptionTypeSupported(type)) {
-            callback.onResult(SubscriptionsManager.StatusCode.INVALID_ARGUMENT);
-            return;
-        }
-
-        if (subscriptions.size() == 0) {
-            callback.onResult(SubscriptionsManager.StatusCode.OK);
-            return;
-        }
-
-        // Wrap the callback in one that allows us to trigger the observers.
-        Callback<Integer> wrappedCallback = (status) -> {
-            if (status == StatusCode.OK) {
-                for (SubscriptionObserver o : mObservers) {
-                    o.onUnsubscribe(subscriptions);
-                }
-            }
-            callback.onResult(status);
-        };
-
-        if (!mCanHandleRequests) {
-            mDeferredTasks.add(new DeferredSubscriptionOperation(
-                    Operation.UNSUBSCRIBE, subscriptions, wrappedCallback));
-            return;
-        }
-
-        Map<String, CommerceSubscription> subscriptionsMap = getSubscriptionsMap(subscriptions);
-        mStorage.loadWithPrefix(String.valueOf(type), localSubscriptions -> {
-            if (localSubscriptions.size() == 0) {
-                wrappedCallback.onResult(SubscriptionsManager.StatusCode.OK);
-                return;
-            }
-
-            List<CommerceSubscription> subscriptionsToDelete =
-                    new ArrayList<CommerceSubscription>();
-
-            for (CommerceSubscription current : localSubscriptions) {
-                String key = CommerceSubscriptionsStorage.getKey(current);
-                if (subscriptionsMap.containsKey(key)) {
-                    subscriptionsToDelete.add(current);
-                }
-            }
-
-            if (subscriptionsToDelete.size() == 0) {
-                wrappedCallback.onResult(SubscriptionsManager.StatusCode.OK);
-                return;
-            }
-
-            mServiceProxy.delete(subscriptionsToDelete,
-                    (didSucceed)
-                            -> handleUpdateSubscriptionsResponse(
-                                    didSucceed, type, wrappedCallback));
-        });
-    }
-
-    // Calls the backend for known types and updates the local cache.
-    private void initTypes(Callback<Integer> callback) {
-        mStorage.deleteAll();
-        String type = CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK;
-        getSubscriptions(type, true,
-                remoteSubscriptions
-                -> updateStorageWithSubscriptions(type, remoteSubscriptions, callback));
-    }
-
-    // Updates the local cache + the state of whether or not the object can start handling requests
-    // based on the initial response form the server.
-    private void onInitComplete(@SubscriptionsManager.StatusCode Integer result) {
-        mCanHandleRequests = true;
-
-        if (result == SubscriptionsManager.StatusCode.OK) {
-            for (DeferredSubscriptionOperation item : mDeferredTasks) {
-                if (Operation.SUBSCRIBE == item.getOperation()) {
-                    subscribe(item.getSubscriptions(), item.getCallback());
-                } else if (Operation.UNSUBSCRIBE == item.getOperation()) {
-                    unsubscribe(item.getSubscriptions(), item.getCallback());
-                }
-            }
-        } else {
-            // Resolve all pending callbacks with an internal error and clear the queue.
-            // TODO: add a retry in case of a network failure.
-            for (DeferredSubscriptionOperation item : mDeferredTasks) {
-                item.getCallback().onResult(SubscriptionsManager.StatusCode.INTERNAL_ERROR);
-            }
-        }
-
-        mDeferredTasks.clear();
-    }
-
-    private boolean isSubscriptionTypeSupported(
-            @CommerceSubscription.CommerceSubscriptionType String type) {
-        return CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK.equals(type);
-    }
-
-    private void updateStorageWithSubscriptions(
-            @CommerceSubscription.CommerceSubscriptionType String type,
-            List<CommerceSubscription> remoteSubscriptions, Callback<Integer> callback) {
-        mStorage.loadWithPrefix(String.valueOf(type), localSubscriptions -> {
-            for (CommerceSubscription subscription : localSubscriptions) {
-                if (!remoteSubscriptions.contains(subscription)) {
-                    mStorage.delete(subscription);
-                }
-            }
-            for (CommerceSubscription subscription : remoteSubscriptions) {
-                if (!localSubscriptions.contains(subscription)) {
-                    mStorage.save(subscription);
-                }
-            }
-
-            callback.onResult(SubscriptionsManager.StatusCode.OK);
-        });
-    }
-
-    private void handleUpdateSubscriptionsResponse(Boolean didSucceed,
-            @CommerceSubscription.CommerceSubscriptionType String type,
-            Callback<Integer> callback) {
-        if (!didSucceed) {
-            callback.onResult(SubscriptionsManager.StatusCode.NETWORK_ERROR);
-            return;
-        } else {
-            getSubscriptions(type, true,
-                    remoteSubscriptions
-                    -> updateStorageWithSubscriptions(type, remoteSubscriptions, callback));
-        }
-    }
-
-    // Creates a Key-Subscription map where key is generated using {@link
-    // CommerceSubscriptionsStorage#getKey}.
-    private Map<String, CommerceSubscription> getSubscriptionsMap(
-            List<CommerceSubscription> subscriptions) {
-        Map<String, CommerceSubscription> subscriptionsMap =
-                new HashMap<String, CommerceSubscription>();
-        for (CommerceSubscription current : subscriptions) {
-            subscriptionsMap.put(CommerceSubscriptionsStorage.getKey(current), current);
-        }
-
-        return subscriptionsMap;
-    }
-
-    // Compares the provided subscriptions list against the local cache and only returns the ones
-    // that are not in the local cache.
-    private void getUniqueSubscriptions(List<CommerceSubscription> subscriptions,
-            Callback<List<CommerceSubscription>> callback) {
-        String type = subscriptions.get(0).getType();
-
-        mStorage.loadWithPrefix(String.valueOf(type), localSubscriptions -> {
-            if (localSubscriptions.size() == 0) {
-                callback.onResult(subscriptions);
-                return;
-            }
-
-            List<CommerceSubscription> result = new ArrayList<CommerceSubscription>();
-
-            Map<String, CommerceSubscription> localSubscriptionsMap =
-                    getSubscriptionsMap(localSubscriptions);
-
-            for (CommerceSubscription subscription : subscriptions) {
-                String key = CommerceSubscriptionsStorage.getKey(subscription);
-                if (!localSubscriptionsMap.containsKey(key)) {
-                    result.add(subscription);
-                }
-            }
-
-            callback.onResult(result);
-        });
-    }
-
-    @VisibleForTesting
-    public void setRemoteSubscriptionsForTesting(List<CommerceSubscription> subscriptions) {
-        sRemoteSubscriptionsForTesting = subscriptions;
-    }
-
-    @VisibleForTesting
-    public void setCanHandlerequests(boolean value) {
-        mCanHandleRequests = value;
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/commerce_subscription_db.cc b/chrome/browser/commerce/subscriptions/commerce_subscription_db.cc
deleted file mode 100644
index c03987a..0000000
--- a/chrome/browser/commerce/subscriptions/commerce_subscription_db.cc
+++ /dev/null
@@ -1,237 +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.
-
-#include "chrome/browser/commerce/subscriptions/commerce_subscription_db.h"
-
-#include "base/android/callback_android.h"
-#include "base/android/jni_android.h"
-#include "base/android/jni_array.h"
-#include "base/android/jni_string.h"
-#include "base/containers/fixed_flat_map.h"
-#include "base/functional/bind.h"
-#include "base/functional/callback_helpers.h"
-#include "chrome/browser/commerce/subscriptions/android/jni_headers/CommerceSubscription_jni.h"
-#include "chrome/browser/commerce/subscriptions/android/jni_headers/CommerceSubscriptionsStorage_jni.h"
-#include "chrome/browser/persisted_state_db/session_proto_db_factory.h"
-#include "components/commerce/core/proto/commerce_subscription_db_content.pb.h"
-#include "content/public/browser/android/browser_context_handle.h"
-
-namespace {
-
-using CommerceSubscriptionProto =
-    commerce_subscription_db::CommerceSubscriptionContentProto;
-using CommerceSubscriptions =
-    std::vector<SessionProtoDB<CommerceSubscriptionProto>::KeyAndValue>;
-using SubscriptionManagementTypeProto = commerce_subscription_db::
-    CommerceSubscriptionContentProto_SubscriptionManagementType;
-using SubscriptionTypeProto =
-    commerce_subscription_db::CommerceSubscriptionContentProto_SubscriptionType;
-using TrackingIdTypeProto =
-    commerce_subscription_db::CommerceSubscriptionContentProto_TrackingIdType;
-
-SubscriptionManagementTypeProto getManagementTypeForString(
-    const std::string& management_type_string) {
-  SubscriptionManagementTypeProto management_type = commerce_subscription_db::
-      CommerceSubscriptionContentProto_SubscriptionManagementType_MANAGE_TYPE_UNSPECIFIED;
-  bool success = commerce_subscription_db::
-      CommerceSubscriptionContentProto_SubscriptionManagementType_Parse(
-          management_type_string, &management_type);
-  DCHECK(success)
-      << "There was an error getting the management type from given string";
-  return management_type;
-}
-
-const std::string& getStringForManagementType(
-    SubscriptionManagementTypeProto management_type) {
-  return commerce_subscription_db::
-      CommerceSubscriptionContentProto_SubscriptionManagementType_Name(
-          management_type);
-}
-
-SubscriptionTypeProto getSubscriptionTypeForString(
-    const std::string& subscription_type_string) {
-  SubscriptionTypeProto subscription_type = commerce_subscription_db::
-      CommerceSubscriptionContentProto_SubscriptionType_TYPE_UNSPECIFIED;
-  bool success = commerce_subscription_db::
-      CommerceSubscriptionContentProto_SubscriptionType_Parse(
-          subscription_type_string, &subscription_type);
-  DCHECK(success)
-      << "There was an error getting the subscription type from given string";
-  return subscription_type;
-}
-
-const std::string& getStringForSubscriptionType(
-    SubscriptionTypeProto subscription_type) {
-  return commerce_subscription_db::
-      CommerceSubscriptionContentProto_SubscriptionType_Name(subscription_type);
-}
-
-TrackingIdTypeProto getTrackingIdTypeForString(
-    const std::string& tracking_id_type_string) {
-  TrackingIdTypeProto tracking_id_type = commerce_subscription_db::
-      CommerceSubscriptionContentProto_TrackingIdType_IDENTIFIER_TYPE_UNSPECIFIED;
-  bool success = commerce_subscription_db::
-      CommerceSubscriptionContentProto_TrackingIdType_Parse(
-          tracking_id_type_string, &tracking_id_type);
-  DCHECK(success)
-      << "There was an error getting the tracking id type from given string";
-  return tracking_id_type;
-}
-
-const std::string& getStringForTrackingIdType(
-    TrackingIdTypeProto tracking_id_type) {
-  return commerce_subscription_db::
-      CommerceSubscriptionContentProto_TrackingIdType_Name(tracking_id_type);
-}
-
-void OnLoadCallbackSingleEntry(const base::android::JavaRef<jobject>& jcallback,
-                               bool success,
-                               CommerceSubscriptions data) {
-  DCHECK(success) << "There was an error loading from CommerceSubscriptionDB";
-  if (data.size() == 0) {
-    base::android::RunObjectCallbackAndroid(jcallback, nullptr);
-    return;
-  }
-  DCHECK(data.size() == 1);
-  CommerceSubscriptionProto proto = std::move(data.at(0).second);
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::ScopedJavaLocalRef<jobject> subscription =
-      Java_CommerceSubscription_Constructor(
-          env,
-          base::android::ConvertUTF8ToJavaString(
-              env, getStringForSubscriptionType(
-                       proto.subscription_type())) /*subscription_type*/,
-          base::android::ConvertUTF8ToJavaString(
-              env, proto.tracking_id()) /*tracking_id*/,
-          base::android::ConvertUTF8ToJavaString(
-              env, getStringForManagementType(
-                       proto.management_type())) /*management_type*/,
-          base::android::ConvertUTF8ToJavaString(
-              env, getStringForTrackingIdType(
-                       proto.tracking_id_type())) /*tracking_id_type*/,
-          proto.timestamp() /*timestamp*/);
-  base::android::RunObjectCallbackAndroid(jcallback, subscription);
-}
-
-void OnLoadCallbackMultipleEntry(
-    const base::android::JavaRef<jobject>& jcallback,
-    bool success,
-    CommerceSubscriptions data) {
-  DCHECK(success) << "There was an error loading from CommerceSubscriptionDB";
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::ScopedJavaLocalRef<jobject> jlist =
-      Java_CommerceSubscription_createSubscriptionList(env);
-  for (SessionProtoDB<CommerceSubscriptionProto>::KeyAndValue& kv : data) {
-    CommerceSubscriptionProto proto = std::move(kv.second);
-    Java_CommerceSubscription_createSubscriptionAndAddToList(
-        env, jlist,
-        base::android::ConvertUTF8ToJavaString(
-            env, getStringForSubscriptionType(
-                     proto.subscription_type())) /*subscription_type*/,
-        base::android::ConvertUTF8ToJavaString(
-            env, proto.tracking_id()) /*tracking_id*/,
-        base::android::ConvertUTF8ToJavaString(
-            env, getStringForManagementType(
-                     proto.management_type())) /*management_type*/,
-        base::android::ConvertUTF8ToJavaString(
-            env, getStringForTrackingIdType(
-                     proto.tracking_id_type())) /*tracking_id_type*/,
-        proto.timestamp());
-  }
-  base::android::RunObjectCallbackAndroid(jcallback, jlist);
-}
-
-void OnUpdateCallback(
-    const base::android::JavaRef<jobject>& joncomplete_for_testing,
-    bool success) {
-  DCHECK(success) << "There was an error modifying CommerceSubscriptionDB";
-  if (joncomplete_for_testing)
-    base::android::RunRunnableAndroid(joncomplete_for_testing);
-}
-}  // namespace
-
-CommerceSubscriptionDB::CommerceSubscriptionDB(
-    content::BrowserContext* browser_context)
-    : proto_db_(SessionProtoDBFactory<CommerceSubscriptionProto>::GetInstance()
-                    ->GetForProfile(browser_context)) {}
-CommerceSubscriptionDB::~CommerceSubscriptionDB() = default;
-
-void CommerceSubscriptionDB::Save(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jkey,
-    const base::android::JavaParamRef<jstring>& jtype,
-    const base::android::JavaParamRef<jstring>& jtracking_id,
-    const base::android::JavaParamRef<jstring>& jmanagement_type,
-    const base::android::JavaParamRef<jstring>& jtracking_id_type,
-    const jlong jtimestamp,
-    const base::android::JavaParamRef<jobject>& jcallback) {
-  const std::string& key = base::android::ConvertJavaStringToUTF8(env, jkey);
-  CommerceSubscriptionProto proto;
-  proto.set_key(key);
-  proto.set_tracking_id(base::android::ConvertJavaStringToUTF8(jtracking_id));
-  proto.set_subscription_type(getSubscriptionTypeForString(
-      base::android::ConvertJavaStringToUTF8(jtype)));
-  proto.set_tracking_id_type(getTrackingIdTypeForString(
-      base::android::ConvertJavaStringToUTF8(jtracking_id_type)));
-  proto.set_management_type(getManagementTypeForString(
-      base::android::ConvertJavaStringToUTF8(jmanagement_type)));
-  proto.set_timestamp(jtimestamp);
-  proto_db_->InsertContent(
-      key, proto,
-      base::BindOnce(&OnUpdateCallback,
-                     base::android::ScopedJavaGlobalRef<jobject>(jcallback)));
-}
-
-void CommerceSubscriptionDB::Load(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jkey,
-    const base::android::JavaParamRef<jobject>& jcallback) {
-  proto_db_->LoadOneEntry(
-      base::android::ConvertJavaStringToUTF8(env, jkey),
-      base::BindOnce(&OnLoadCallbackSingleEntry,
-                     base::android::ScopedJavaGlobalRef<jobject>(jcallback)));
-}
-
-void CommerceSubscriptionDB::LoadWithPrefix(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jprefix,
-    const base::android::JavaParamRef<jobject>& jcallback) {
-  proto_db_->LoadContentWithPrefix(
-      base::android::ConvertJavaStringToUTF8(env, jprefix),
-      base::BindOnce(&OnLoadCallbackMultipleEntry,
-                     base::android::ScopedJavaGlobalRef<jobject>(jcallback)));
-}
-
-void CommerceSubscriptionDB::Delete(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jkey,
-    const base::android::JavaParamRef<jobject>& joncomplete_for_testing) {
-  proto_db_->DeleteOneEntry(
-      base::android::ConvertJavaStringToUTF8(env, jkey),
-      base::BindOnce(&OnUpdateCallback,
-                     base::android::ScopedJavaGlobalRef<jobject>(
-                         joncomplete_for_testing)));
-}
-
-void CommerceSubscriptionDB::DeleteAll(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& joncomplete_for_testing) {
-  proto_db_->DeleteAllContent(base::BindOnce(
-      &OnUpdateCallback,
-      base::android::ScopedJavaGlobalRef<jobject>(joncomplete_for_testing)));
-}
-
-void CommerceSubscriptionDB::Destroy(JNIEnv* env) {
-  proto_db_->Destroy();
-}
-
-static void JNI_CommerceSubscriptionsStorage_Init(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& obj,
-    const base::android::JavaParamRef<jobject>& jprofile) {
-  Java_CommerceSubscriptionsStorage_setNativePtr(
-      env, obj,
-      reinterpret_cast<intptr_t>(new CommerceSubscriptionDB(
-          content::BrowserContextFromJavaHandle(jprofile))));
-}
diff --git a/chrome/browser/commerce/subscriptions/commerce_subscription_db.h b/chrome/browser/commerce/subscriptions/commerce_subscription_db.h
deleted file mode 100644
index 6a2b5d1..0000000
--- a/chrome/browser/commerce/subscriptions/commerce_subscription_db.h
+++ /dev/null
@@ -1,72 +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.
-
-#ifndef CHROME_BROWSER_COMMERCE_SUBSCRIPTIONS_COMMERCE_SUBSCRIPTION_DB_H_
-#define CHROME_BROWSER_COMMERCE_SUBSCRIPTIONS_COMMERCE_SUBSCRIPTION_DB_H_
-
-#include "base/android/scoped_java_ref.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "components/commerce/core/proto/commerce_subscription_db_content.pb.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/leveldb_proto/public/proto_database.h"
-
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-namespace commerce_subscription_db {
-class CommerceSubscriptionContentProto;
-}  // namespace commerce_subscription_db
-
-template <typename T>
-class SessionProtoDB;
-
-class CommerceSubscriptionDB {
- public:
-  explicit CommerceSubscriptionDB(content::BrowserContext* browser_context);
-  CommerceSubscriptionDB(const CommerceSubscriptionDB&) = delete;
-  CommerceSubscriptionDB& operator=(const CommerceSubscriptionDB&) = delete;
-  ~CommerceSubscriptionDB();
-
-  // Save subscription for key.
-  void Save(JNIEnv* env,
-            const base::android::JavaParamRef<jstring>& jkey,
-            const base::android::JavaParamRef<jstring>& jtype,
-            const base::android::JavaParamRef<jstring>& jtracking_id,
-            const base::android::JavaParamRef<jstring>& jmanagement_type,
-            const base::android::JavaParamRef<jstring>& jtracking_id_type,
-            const jlong jtimestamp,
-            const base::android::JavaParamRef<jobject>& jcallback);
-
-  // Load subscription corresponding to key.
-  void Load(JNIEnv* env,
-            const base::android::JavaParamRef<jstring>& jkey,
-            const base::android::JavaParamRef<jobject>& jcallback);
-
-  // Load subscriptions whose keys have specific prefix.
-  void LoadWithPrefix(JNIEnv* env,
-                      const base::android::JavaParamRef<jstring>& jprefix,
-                      const base::android::JavaParamRef<jobject>& jcallback);
-
-  // Delete entry corresponding to key.
-  void Delete(JNIEnv* env,
-              const base::android::JavaParamRef<jstring>& jkey,
-              const base::android::JavaParamRef<jobject>& jcallback);
-
-  // Delete all entries in the database.
-  void DeleteAll(JNIEnv* env,
-                 const base::android::JavaParamRef<jobject>& jcallback);
-
-  // Destroy CommerceSubscriptionDB object.
-  void Destroy(JNIEnv* env);
-
- private:
-  raw_ptr<SessionProtoDB<
-      commerce_subscription_db::CommerceSubscriptionContentProto>>
-      proto_db_;
-  base::WeakPtrFactory<CommerceSubscriptionDB> weak_ptr_factory_{this};
-};
-
-#endif  // CHROME_BROWSER_COMMERCE_SUBSCRIPTIONS_COMMERCE_SUBSCRIPTION_DB_H_
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionJsonSerializerUnitTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionJsonSerializerUnitTest.java
deleted file mode 100644
index 9544c2f..0000000
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionJsonSerializerUnitTest.java
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.DeferredStartupHandler;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.PriceTrackableOffer;
-import org.chromium.chrome.test.util.browser.Features;
-
-/**
- * Tests for {@link CommerceSubscriptionJsonSerializer}.
- */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class CommerceSubscriptionJsonSerializerUnitTest {
-    @Rule
-    public TestRule mProcessor = new Features.JUnitProcessor();
-
-    private static final String FAKE_OFFER_ID = "100";
-    private static final String FAKE_PRODUCT_CLUSTER_ID = "300";
-    private static final String FAKE_CURRENT_PRICE = "1000";
-    private static final String FAKE_COUNTRY_CODE = "us";
-
-    private static final String FAKE_SUBSCRIPTION_JSON_STRING = "{ \"type\": \"PRICE_TRACK\","
-            + "\"managementType\": \"CHROME_MANAGED\", "
-            + "\"identifierType\": \"OFFER_ID\", \"identifier\": \"100\","
-            + "\"eventTimestampMicros\": \"200\" }";
-
-    private static final CommerceSubscription FAKE_CHROME_MANAGED_SUBSCRIPTION =
-            new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                    FAKE_OFFER_ID, CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                    CommerceSubscription.TrackingIdType.OFFER_ID, 200L);
-
-    private static final CommerceSubscription FAKE_USER_MANAGED_SUBSCRIPTION =
-            new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                    FAKE_PRODUCT_CLUSTER_ID,
-                    CommerceSubscription.SubscriptionManagementType.USER_MANAGED,
-                    CommerceSubscription.TrackingIdType.PRODUCT_CLUSTER_ID,
-                    new PriceTrackableOffer(FAKE_OFFER_ID, FAKE_CURRENT_PRICE, FAKE_COUNTRY_CODE));
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @After
-    public void tearDown() {
-        DeferredStartupHandler.setInstanceForTests(null);
-    }
-
-    @Test
-    public void testSerialize() throws JSONException {
-        JSONObject subscriptionJson =
-                CommerceSubscriptionJsonSerializer.serialize(FAKE_CHROME_MANAGED_SUBSCRIPTION);
-        assertThat(subscriptionJson.getString("type"),
-                equalTo(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK));
-        assertThat(subscriptionJson.getString("identifierType"),
-                equalTo(CommerceSubscription.TrackingIdType.OFFER_ID));
-        assertThat(subscriptionJson.getString("identifier"), equalTo(FAKE_OFFER_ID));
-    }
-
-    @Test
-    public void testSerialize_ClusterId() throws JSONException {
-        JSONObject subscriptionJson =
-                CommerceSubscriptionJsonSerializer.serialize(FAKE_USER_MANAGED_SUBSCRIPTION);
-        assertThat(subscriptionJson.getString("type"),
-                equalTo(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK));
-        assertThat(subscriptionJson.getString("identifierType"),
-                equalTo(CommerceSubscription.TrackingIdType.PRODUCT_CLUSTER_ID));
-        assertThat(subscriptionJson.getString("identifier"), equalTo(FAKE_PRODUCT_CLUSTER_ID));
-        assertThat(subscriptionJson.getJSONObject("userSeenOffer").getString("offerId"),
-                equalTo(FAKE_OFFER_ID));
-        assertThat(subscriptionJson.getJSONObject("userSeenOffer").getString("seenPriceMicros"),
-                equalTo(FAKE_CURRENT_PRICE));
-        assertThat(subscriptionJson.getJSONObject("userSeenOffer").getString("countryCode"),
-                equalTo(FAKE_COUNTRY_CODE));
-    }
-
-    @Test
-    public void testDeserialize() throws JSONException {
-        JSONObject fakeSubscription = new JSONObject(FAKE_SUBSCRIPTION_JSON_STRING);
-        CommerceSubscription actual =
-                CommerceSubscriptionJsonSerializer.deserialize(fakeSubscription);
-
-        assertThat(actual.getType(), equalTo(FAKE_CHROME_MANAGED_SUBSCRIPTION.getType()));
-        assertThat(actual.getTimestamp(), equalTo(FAKE_CHROME_MANAGED_SUBSCRIPTION.getTimestamp()));
-        assertThat(
-                actual.getTrackingId(), equalTo(FAKE_CHROME_MANAGED_SUBSCRIPTION.getTrackingId()));
-        assertThat(actual.getTrackingIdType(),
-                equalTo(FAKE_CHROME_MANAGED_SUBSCRIPTION.getTrackingIdType()));
-        assertThat(actual.getManagementType(),
-                equalTo(FAKE_CHROME_MANAGED_SUBSCRIPTION.getManagementType()));
-    }
-
-    @Test
-    public void testDeserialize_MissingTimestamp() throws JSONException {
-        JSONObject fakeSubscription =
-                new JSONObject("{ \"type\": \"PRICE_TRACK\", \"managementType\": "
-                        + "\"CHROME_MANAGED\", \"identifierType\": \"OFFER_ID\", "
-                        + "\"identifier\": \"100\" }");
-        assertNull(CommerceSubscriptionJsonSerializer.deserialize(fakeSubscription));
-    }
-
-    @Test
-    public void testDeserialize_MissingIdentifierOrType() throws JSONException {
-        JSONObject fakeSubscriptionMissingId =
-                new JSONObject("{ \"type\": \"PRICE_TRACK\", \"managementType\": "
-                        + "\"CHROME_MANAGED\", \"identifierType\": \"OFFER_ID\", "
-                        + "\"eventTimestampMicros\": \"200\" }");
-        assertNull(CommerceSubscriptionJsonSerializer.deserialize(fakeSubscriptionMissingId));
-
-        JSONObject fakeSubscriptionMissingIdType =
-                new JSONObject("{ \"type\": \"PRICE_TRACK\", \"managementType\": "
-                        + " \"CHROME_MANAGED\", \"identifier\": \"100\", "
-                        + "\"eventTimestampMicros\": \"200\" }");
-        assertNull(CommerceSubscriptionJsonSerializer.deserialize(fakeSubscriptionMissingIdType));
-    }
-
-    @Test
-    public void testDeserialize_MissingManagementType() throws JSONException {
-        JSONObject fakeSubscriptionMissingManagementType =
-                new JSONObject("{ \"type\": \"PRICE_TRACK\", \"identifierType\": \"OFFER_ID\","
-                        + " \"identifier\": \"100\", \"eventTimestampMicros\": \"200\" }");
-        assertNull(CommerceSubscriptionJsonSerializer.deserialize(
-                fakeSubscriptionMissingManagementType));
-    }
-
-    @Test
-    public void testDeserialize_MissingType() throws JSONException {
-        JSONObject fakeSubscriptionMissingManagementType =
-                new JSONObject("{ \"managementType\": \"CHROME_MANAGED\", \"identifierType\":"
-                        + "\"OFFER_ID\", \"identifier\": \"100\","
-                        + " \"eventTimestampMicros\": \"200\" }");
-        assertNull(CommerceSubscriptionJsonSerializer.deserialize(
-                fakeSubscriptionMissingManagementType));
-    }
-
-    @Test
-    public void testDeserialize_InvalidTimestamp() throws JSONException {
-        JSONObject fakeSubscriptionInvalidTimestamp =
-                new JSONObject("{ \"managementType\": \"CHROME_MANAGED\", "
-                        + "\"identifierType\": \"OFFER_ID\", \"identifier\": \"100\", "
-                        + "\"eventTimestampMicros\": \"lkjasdf\" }");
-        assertNull(
-                CommerceSubscriptionJsonSerializer.deserialize(fakeSubscriptionInvalidTimestamp));
-    }
-
-    @Test
-    public void testDeserialize_ProductClusterId() throws JSONException {
-        JSONObject subscriptionJson =
-                new JSONObject("{ \"type\": \"PRICE_TRACK\", \"managementType\": \"USER_MANAGED\", "
-                        + "\"identifierType\": \"PRODUCT_CLUSTER_ID\", \"identifier\": \"100\", "
-                        + "\"eventTimestampMicros\": \"200\" }");
-        CommerceSubscription actual =
-                CommerceSubscriptionJsonSerializer.deserialize(subscriptionJson);
-        assertThat(actual.getTrackingIdType(),
-                equalTo(CommerceSubscription.TrackingIdType.PRODUCT_CLUSTER_ID));
-        assertThat(actual.getManagementType(),
-                equalTo(CommerceSubscription.SubscriptionManagementType.USER_MANAGED));
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceFactoryUnitTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceFactoryUnitTest.java
index 401b833..b422e25 100644
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceFactoryUnitTest.java
+++ b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceFactoryUnitTest.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.subscriptions;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 
 import androidx.test.filters.SmallTest;
@@ -19,20 +17,19 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
 import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
 import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.signin.identitymanager.IdentityManager;
-import org.chromium.content_public.browser.BrowserContextHandle;
+import org.chromium.components.commerce.core.ShoppingService;
+
 /**
  * Unit tests for {@link CommerceSubscriptionsServiceFactory}.
  */
@@ -52,39 +49,18 @@
     private Profile mProfileTwo;
 
     @Mock
-    private CommerceSubscriptionsStorage.Natives mCommerceSubscriptionsStorageJni;
-
-    @Mock
     EndpointFetcher.Natives mEndpointFetcherJniMock;
 
     @Mock
-    IdentityServicesProvider mIdentityServicesProvider;
-
-    @Mock
-    IdentityManager mIdentityManager;
+    ShoppingService mShoppingService;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         doReturn(false).when(mProfileOne).isOffTheRecord();
         doReturn(false).when(mProfileTwo).isOffTheRecord();
-        mMocker.mock(CommerceSubscriptionsStorageJni.TEST_HOOKS, mCommerceSubscriptionsStorageJni);
         mMocker.mock(EndpointFetcherJni.TEST_HOOKS, mEndpointFetcherJniMock);
-
-        IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
-        doReturn(mIdentityManager).when(mIdentityServicesProvider).getIdentityManager(any());
-
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) {
-                CommerceSubscriptionsStorage storage =
-                        (CommerceSubscriptionsStorage) invocation.getArguments()[0];
-                storage.setNativeCommerceSubscriptionDBForTesting((long) 123);
-                return null;
-            }
-        })
-                .when(mCommerceSubscriptionsStorageJni)
-                .init(any(CommerceSubscriptionsStorage.class), any(BrowserContextHandle.class));
+        ShoppingServiceFactory.setShoppingServiceForTesting(mShoppingService);
     }
 
     @After
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceProxyUnitTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceProxyUnitTest.java
deleted file mode 100644
index ce6412b..0000000
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceProxyUnitTest.java
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import androidx.test.filters.SmallTest;
-
-import org.json.JSONException;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import org.chromium.base.Callback;
-import org.chromium.base.test.UiThreadTest;
-import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
-import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni;
-import org.chromium.chrome.browser.endpoint_fetcher.EndpointResponse;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.test.ChromeBrowserTestRule;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
-
-import java.util.ArrayList;
-
-/**
- * Tests for {@link CommerceSubscriptionsServiceProxy}.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@Batch(Batch.PER_CLASS)
-@EnableFeatures({ChromeFeatureList.COMMERCE_PRICE_TRACKING + "<Study"})
-@CommandLineFlags.
-Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "force-fieldtrials=Study/Group"})
-public class CommerceSubscriptionsServiceProxyUnitTest {
-    @Rule
-    public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
-
-    @Rule
-    public JniMocker mMocker = new JniMocker();
-
-    private static final String EXPECTED_CONTENT_TYPE = "application/json; charset=UTF-8";
-    private static final String[] EXPECTED_OAUTH_SCOPES =
-            new String[] {"https://www.googleapis.com/auth/chromememex"};
-    private static final String HTTP_GET = "GET";
-    private static final String HTTP_POST = "POST";
-    private static final String EMPTY_RESPONSE = "{}";
-    private static final String FAKE_GET_RESPONSE =
-            "{ \"subscriptions\": [ { \"type\": \"PRICE_TRACK\","
-            + " \"managementType\": \"CHROME_MANAGED\","
-            + "\"identifierType\": \"OFFER_ID\", \"identifier\": \"190190\","
-            + "\"eventTimestampMicros\": \"200\" } ] }";
-
-    private static final String DEFAULT_ENDPOINT = "https://memex-pa.googleapis.com/v1/annotations";
-    private static final String ENDPOINT_OVERRIDE = "my-endpoint.com";
-    private static final CommerceSubscription FAKE_SUBSCRIPTION =
-            new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                    "190190", CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                    CommerceSubscription.TrackingIdType.OFFER_ID);
-
-    @Mock
-    EndpointFetcher.Natives mEndpointFetcherJniMock;
-
-    @Mock
-    private Profile mProfile;
-
-    private CommerceSubscriptionsServiceProxy mServiceProxy;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mMocker.mock(EndpointFetcherJni.TEST_HOOKS, mEndpointFetcherJniMock);
-        doReturn(false).when(mProfile).isOffTheRecord();
-        Profile.setLastUsedProfileForTesting(mProfile);
-        mServiceProxy = new CommerceSubscriptionsServiceProxy(mProfile);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        Profile.setLastUsedProfileForTesting(null);
-    }
-
-    @UiThreadTest
-    @Test
-    @SmallTest
-    public void testGetSubscriptions() {
-        mockEndpointResponse(FAKE_GET_RESPONSE);
-        mServiceProxy.get(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK, (result) -> {
-            assertNotNull(result);
-            Assert.assertEquals(1, result.size());
-
-            CommerceSubscription subscription = result.get(0);
-            Assert.assertEquals(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                    subscription.getType());
-            Assert.assertEquals(CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                    subscription.getManagementType());
-            Assert.assertEquals(
-                    CommerceSubscription.TrackingIdType.OFFER_ID, subscription.getTrackingIdType());
-            Assert.assertEquals(200L, subscription.getTimestamp());
-            Assert.assertEquals("190190", subscription.getTrackingId());
-        });
-    }
-
-    @UiThreadTest
-    @Test
-    @SmallTest
-    public void testGetSubscriptions_EmptyResponse() {
-        mockEndpointResponse(EMPTY_RESPONSE);
-        mServiceProxy.get(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                (result) -> { Assert.assertTrue(result.isEmpty()); });
-    }
-
-    @UiThreadTest
-    @Test
-    @SmallTest
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:subscriptions_service_base_url/my-endpoint.com"})
-    public void testGetSubscriptions_ValidRequest() {
-        mServiceProxy.get(
-                CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK, (result) -> {});
-        verifyEndpointFetcherCalled(
-                1, "my-endpoint.com?requestParams.subscriptionType=PRICE_TRACK", "GET", "");
-    }
-
-    @UiThreadTest
-    @Test
-    @SmallTest
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:subscriptions_service_base_url/my-endpoint.com"})
-    public void testCreateSubscriptions_ValidRequest() throws JSONException {
-        String expectedJsonPayload = "";
-        mServiceProxy.create(new ArrayList<CommerceSubscription>() {
-            { add(FAKE_SUBSCRIPTION); }
-        }, (success) -> {});
-        verifyEndpointFetcherCalled(1, "my-endpoint.com", "POST",
-                "{\"createShoppingSubscriptionsParams\":"
-                        + "{\"subscriptions\":[{\"type\":\"PRICE_TRACK\","
-                        + "\"managementType\":\"CHROME_MANAGED\","
-                        + "\"identifierType\":\"OFFER_ID\",\"identifier\":\"190190\"}]}}");
-    }
-
-    @UiThreadTest
-    @Test
-    @SmallTest
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:subscriptions_service_base_url/my-endpoint.com"})
-    public void testCreateSubscriptions_InvalidResponse() throws JSONException {
-        mockEndpointResponse(EMPTY_RESPONSE);
-        mServiceProxy.create(
-                new ArrayList<CommerceSubscription>() {
-                    { add(FAKE_SUBSCRIPTION); }
-                },
-                (success) -> { Assert.assertFalse(success); });
-    }
-
-    @UiThreadTest
-    @Test
-    @SmallTest
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:subscriptions_service_base_url/my-endpoint.com"})
-    public void testCreateSubscriptions() throws JSONException {
-        mockEndpointResponse("{ \"status\": { \"code\": 0 } }");
-        mServiceProxy.create(
-                new ArrayList<CommerceSubscription>() {
-                    { add(FAKE_SUBSCRIPTION); }
-                },
-                (success) -> { Assert.assertTrue(success); });
-    }
-
-    @UiThreadTest
-    @Test
-    @SmallTest
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:subscriptions_service_base_url/my-endpoint.com"})
-    public void testDeleteSubscriptions_ValidRequest() throws JSONException {
-        CommerceSubscription fakeSubscriptionToDelete =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        "190190", CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                        CommerceSubscription.TrackingIdType.OFFER_ID, 1617309553897712L);
-
-        mServiceProxy.delete(new ArrayList<CommerceSubscription>() {
-            { add(fakeSubscriptionToDelete); }
-        }, (success) -> {});
-        verifyEndpointFetcherCalled(1, "my-endpoint.com", "POST",
-                "{\"removeShoppingSubscriptionsParams\":"
-                        + "{\"eventTimestampMicros\":[1617309553897712]}}");
-    }
-
-    @UiThreadTest
-    @Test
-    @SmallTest
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:subscriptions_service_base_url/my-endpoint.com"})
-    public void testDeleteSubscriptions_InvalidResponse() throws JSONException {
-        mockEndpointResponse(EMPTY_RESPONSE);
-        mServiceProxy.delete(
-                new ArrayList<CommerceSubscription>() {
-                    { add(FAKE_SUBSCRIPTION); }
-                },
-                (success) -> { Assert.assertFalse(success); });
-    }
-
-    @UiThreadTest
-    @Test
-    @SmallTest
-    @CommandLineFlags.
-    Add({"force-fieldtrial-params=Study.Group:subscriptions_service_base_url/my-endpoint.com"})
-    public void testDeleteSubscriptions() throws JSONException {
-        mockEndpointResponse("{ \"status\": { \"code\": 0 } }");
-        mServiceProxy.delete(
-                new ArrayList<CommerceSubscription>() {
-                    { add(FAKE_SUBSCRIPTION); }
-                },
-                (success) -> { Assert.assertTrue(success); });
-    }
-
-    private void verifyEndpointFetcherCalled(
-            int numTimes, String expectedUrl, String expectedMethod, String expectedPayload) {
-        verify(mEndpointFetcherJniMock, times(numTimes))
-                .nativeFetchOAuth(any(Profile.class), any(String.class), eq(expectedUrl),
-                        eq(expectedMethod), eq(EXPECTED_CONTENT_TYPE), eq(EXPECTED_OAUTH_SCOPES),
-                        eq(expectedPayload), anyLong(), anyInt(), any(Callback.class));
-    }
-
-    private void mockEndpointResponse(String response) {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) {
-                Callback callback = (Callback) invocation.getArguments()[9];
-                callback.onResult(new EndpointResponse(response));
-                return null;
-            }
-        })
-                .when(mEndpointFetcherJniMock)
-                .nativeFetchOAuth(any(Profile.class), any(String.class), any(String.class),
-                        any(String.class), eq(EXPECTED_CONTENT_TYPE), eq(EXPECTED_OAUTH_SCOPES),
-                        any(String.class), anyLong(), anyInt(), any(Callback.class));
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceUnitTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceUnitTest.java
index 7a71601..77a7f32 100644
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceUnitTest.java
+++ b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceUnitTest.java
@@ -7,9 +7,6 @@
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.times;
@@ -30,7 +27,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.Callback;
 import org.chromium.base.FeatureList;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.UmaRecorderHolder;
@@ -39,7 +35,6 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
-import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
 import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManagerFactory;
@@ -47,20 +42,14 @@
 import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsMetrics.AccountWaaStatus;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.browser_ui.notifications.MockNotificationManagerProxy;
+import org.chromium.components.commerce.core.ShoppingService;
 import org.chromium.components.prefs.PrefService;
-import org.chromium.components.signin.identitymanager.IdentityManager;
-import org.chromium.components.signin.identitymanager.PrimaryAccountChangeEvent;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.components.user_prefs.UserPrefsJni;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -76,11 +65,7 @@
     public JniMocker mJniMocker = new JniMocker();
 
     @Mock
-    private SubscriptionsManagerImpl mSubscriptionsManager;
-    @Mock
-    private IdentityManager mIdentityManager;
-    @Mock
-    private PrimaryAccountChangeEvent mChangeEvent;
+    private ShoppingService mShoppingService;
     @Mock
     TabModelSelector mTabModelSelector;
     @Mock
@@ -96,11 +81,7 @@
     @Mock
     private UserPrefs.Natives mUserPrefsJni;
     @Captor
-    private ArgumentCaptor<IdentityManager.Observer> mIdentityManagerObserverCaptor;
-    @Captor
     private ArgumentCaptor<PauseResumeWithNativeObserver> mPauseResumeWithNativeObserverCaptor;
-    @Captor
-    private ArgumentCaptor<Callback<List<CommerceSubscription>>> mLocalSubscriptionsCallbackCaptor;
 
     private CommerceSubscriptionsService mService;
     private SharedPreferencesManager mSharedPreferencesManager;
@@ -114,7 +95,6 @@
         UmaRecorderHolder.resetForTesting();
 
         doNothing().when(mActivityLifecycleDispatcher).register(any());
-        doNothing().when(mSubscriptionsManager).getSubscriptions(anyString(), anyBoolean(), any());
         mSharedPreferencesManager = SharedPreferencesManager.getInstance();
         mSharedPreferencesManager.writeLong(
                 CommerceSubscriptionsService.CHROME_MANAGED_SUBSCRIPTIONS_TIMESTAMP,
@@ -136,13 +116,10 @@
         mJniMocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJni);
         Profile.setLastUsedProfileForTesting(mProfile);
         when(mUserPrefsJni.get(mProfile)).thenReturn(mPrefService);
-        IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
-        when(mIdentityServicesProvider.getIdentityManager(mProfile)).thenReturn(mIdentityManager);
 
         mPriceDropNotificationManager = PriceDropNotificationManagerFactory.create();
-        mService = new CommerceSubscriptionsService(
-                mSubscriptionsManager, mIdentityManager, mPriceDropNotificationManager);
-        verify(mIdentityManager, times(1)).addObserver(mIdentityManagerObserverCaptor.capture());
+        mService =
+                new CommerceSubscriptionsService(mShoppingService, mPriceDropNotificationManager);
         mService.setImplicitSubscriptionsManagerForTesting(mImplicitSubscriptionsManager);
     }
 
@@ -156,8 +133,6 @@
     public void testDestroy() {
         mService.setImplicitSubscriptionsManagerForTesting(null);
         mService.destroy();
-        verify(mIdentityManager, times(1))
-                .removeObserver(eq(mIdentityManagerObserverCaptor.getValue()));
     }
 
     @Test
@@ -168,8 +143,6 @@
                 .register(mPauseResumeWithNativeObserverCaptor.capture());
 
         mService.destroy();
-        verify(mIdentityManager, times(1))
-                .removeObserver(eq(mIdentityManagerObserverCaptor.getValue()));
         verify(mActivityLifecycleDispatcher, times(1))
                 .unregister(eq(mPauseResumeWithNativeObserverCaptor.getValue()));
         verify(mImplicitSubscriptionsManager, times(1)).destroy();
@@ -177,13 +150,6 @@
 
     @Test
     @SmallTest
-    public void testOnPrimaryAccountChanged() {
-        mIdentityManagerObserverCaptor.getValue().onPrimaryAccountChanged(mChangeEvent);
-        verify(mSubscriptionsManager, times(1)).onIdentityChanged();
-    }
-
-    @Test
-    @SmallTest
     public void testOnResume() {
         setupTestOnResume();
         assertThat(RecordHistogram.getHistogramTotalCountForTesting(
@@ -197,37 +163,7 @@
                 RecordHistogram.getHistogramTotalCountForTesting(
                         PriceDropNotificationManagerImpl.NOTIFICATION_USER_MANAGED_COUNT_HISTOGRAM),
                 equalTo(1));
-        verify(mSubscriptionsManager, times(1))
-                .getSubscriptions(eq(CommerceSubscriptionType.PRICE_TRACK), eq(false),
-                        mLocalSubscriptionsCallbackCaptor.capture());
         verify(mImplicitSubscriptionsManager, times(1)).initializeSubscriptions();
-
-        CommerceSubscription subscription1 = new CommerceSubscription(
-                CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK, "offer_id_1",
-                CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                CommerceSubscription.TrackingIdType.OFFER_ID);
-        CommerceSubscription subscription2 =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        "offer_id_2", CommerceSubscription.SubscriptionManagementType.USER_MANAGED,
-                        CommerceSubscription.TrackingIdType.PRODUCT_CLUSTER_ID);
-
-        mLocalSubscriptionsCallbackCaptor.getValue().onResult(
-                new ArrayList<>(Arrays.asList(subscription1, subscription1, subscription2)));
-        assertThat(
-                RecordHistogram.getHistogramTotalCountForTesting(
-                        CommerceSubscriptionsMetrics.SUBSCRIPTION_CHROME_MANAGED_COUNT_HISTOGRAM),
-                equalTo(1));
-        assertThat(RecordHistogram.getHistogramValueCountForTesting(
-                           CommerceSubscriptionsMetrics.SUBSCRIPTION_CHROME_MANAGED_COUNT_HISTOGRAM,
-                           2),
-                equalTo(1));
-        assertThat(RecordHistogram.getHistogramTotalCountForTesting(
-                           CommerceSubscriptionsMetrics.SUBSCRIPTION_USER_MANAGED_COUNT_HISTOGRAM),
-                equalTo(1));
-        assertThat(
-                RecordHistogram.getHistogramValueCountForTesting(
-                        CommerceSubscriptionsMetrics.SUBSCRIPTION_USER_MANAGED_COUNT_HISTOGRAM, 1),
-                equalTo(1));
     }
 
     @Test
@@ -241,7 +177,6 @@
         assertThat(RecordHistogram.getHistogramTotalCountForTesting(
                            PriceDropNotificationManagerImpl.NOTIFICATION_ENABLED_HISTOGRAM),
                 equalTo(0));
-        verify(mSubscriptionsManager, times(0)).getSubscriptions(anyString(), anyBoolean(), any());
         verify(mImplicitSubscriptionsManager, times(0)).initializeSubscriptions();
     }
 
@@ -256,59 +191,9 @@
         assertThat(RecordHistogram.getHistogramTotalCountForTesting(
                            PriceDropNotificationManagerImpl.NOTIFICATION_ENABLED_HISTOGRAM),
                 equalTo(0));
-        verify(mSubscriptionsManager, times(0)).getSubscriptions(anyString(), anyBoolean(), any());
         verify(mImplicitSubscriptionsManager, times(0)).initializeSubscriptions();
     }
 
-    @Test
-    @SmallTest
-    public void testRecordAccountWaaStatus_SignOut() {
-        when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(false);
-
-        setupTestOnResume();
-        assertThat(RecordHistogram.getHistogramTotalCountForTesting(
-                           CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM),
-                equalTo(1));
-        assertThat(RecordHistogram.getHistogramValueCountForTesting(
-                           CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM,
-                           AccountWaaStatus.SIGN_OUT),
-                equalTo(1));
-    }
-
-    @Test
-    @SmallTest
-    public void testRecordAccountWaaStatus_SignInWaaDisabled() {
-        when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(true);
-        when(mPrefService.getBoolean(Pref.WEB_AND_APP_ACTIVITY_ENABLED_FOR_SHOPPING))
-                .thenReturn(false);
-
-        setupTestOnResume();
-        assertThat(RecordHistogram.getHistogramTotalCountForTesting(
-                           CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM),
-                equalTo(1));
-        assertThat(RecordHistogram.getHistogramValueCountForTesting(
-                           CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM,
-                           AccountWaaStatus.SIGN_IN_WAA_DISABLED),
-                equalTo(1));
-    }
-
-    @Test
-    @SmallTest
-    public void testRecordAccountWaaStatus_SignInWaaEnabled() {
-        when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(true);
-        when(mPrefService.getBoolean(Pref.WEB_AND_APP_ACTIVITY_ENABLED_FOR_SHOPPING))
-                .thenReturn(true);
-
-        setupTestOnResume();
-        assertThat(RecordHistogram.getHistogramTotalCountForTesting(
-                           CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM),
-                equalTo(1));
-        assertThat(RecordHistogram.getHistogramValueCountForTesting(
-                           CommerceSubscriptionsMetrics.ACCOUNT_WAA_STATUS_HISTOGRAM,
-                           AccountWaaStatus.SIGN_IN_WAA_ENABLED),
-                equalTo(1));
-    }
-
     private void setupTestOnResume() {
         mService.initDeferredStartupForActivity(mTabModelSelector, mActivityLifecycleDispatcher);
         verify(mActivityLifecycleDispatcher, times(1))
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsStorageTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsStorageTest.java
deleted file mode 100644
index 6f03f1d..0000000
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsStorageTest.java
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import androidx.test.filters.MediumTest;
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Tests related to {@link CommerceSubscriptionsStorage}.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@Batch(Batch.PER_CLASS)
-public class CommerceSubscriptionsStorageTest {
-    @ClassRule
-    public static ChromeTabbedActivityTestRule sActivityTestRule =
-            new ChromeTabbedActivityTestRule();
-
-    @Rule
-    public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule =
-            new BlankCTATabInitialStateRule(sActivityTestRule, false);
-
-    private static final String OFFER_ID_1 = "offer_id_1";
-    private static final String OFFER_ID_2 = "offer_id_2";
-    private static final String OFFER_ID_3 = "offer_id_3";
-    private static final String PRODUCT_CLUSTER_ID = "product_cluster_id";
-    private static final String KEY_1 = "PRICE_TRACK_OFFER_ID_offer_id_1";
-    private static final String KEY_2 = "PRICE_TRACK_OFFER_ID_offer_id_2";
-    private static final String KEY_3 = "PRICE_TRACK_IDENTIFIER_TYPE_UNSPECIFIED_offer_id_3";
-
-    private CommerceSubscriptionsStorage mStorage;
-    private CommerceSubscription mSubscription1;
-    private CommerceSubscription mSubscription2;
-    private CommerceSubscription mSubscription3;
-    private CommerceSubscription mSubscription4;
-
-    @Before
-    public void setUp() throws Exception {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mStorage = new CommerceSubscriptionsStorage(Profile.getLastUsedRegularProfile());
-        });
-
-        mSubscription1 =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        OFFER_ID_1, CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                        CommerceSubscription.TrackingIdType.OFFER_ID);
-        mSubscription2 =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        OFFER_ID_2, CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                        CommerceSubscription.TrackingIdType.OFFER_ID);
-        mSubscription3 =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        OFFER_ID_3, CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                        CommerceSubscription.TrackingIdType.IDENTIFIER_TYPE_UNSPECIFIED);
-        mSubscription4 = new CommerceSubscription(
-                CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK, PRODUCT_CLUSTER_ID,
-                CommerceSubscription.SubscriptionManagementType.USER_MANAGED,
-                CommerceSubscription.TrackingIdType.PRODUCT_CLUSTER_ID);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mStorage.deleteAll();
-            mStorage.destroy();
-        });
-    }
-
-    @SmallTest
-    @Test
-    public void testGenerateStorageID() throws TimeoutException {
-        assertEquals(KEY_1, CommerceSubscriptionsStorage.getKey(mSubscription1));
-        assertEquals(KEY_2, CommerceSubscriptionsStorage.getKey(mSubscription2));
-        assertEquals(KEY_3, CommerceSubscriptionsStorage.getKey(mSubscription3));
-    }
-
-    @MediumTest
-    @Test
-    public void testSaveLoadDelete() throws TimeoutException {
-        save(mSubscription1);
-        loadSingleAndCheckResult(
-                CommerceSubscriptionsStorage.getKey(mSubscription1), mSubscription1);
-        save(mSubscription2);
-        loadSingleAndCheckResult(
-                CommerceSubscriptionsStorage.getKey(mSubscription2), mSubscription2);
-        delete(mSubscription1);
-        loadSingleAndCheckResult(CommerceSubscriptionsStorage.getKey(mSubscription1), null);
-        loadSingleAndCheckResult(
-                CommerceSubscriptionsStorage.getKey(mSubscription2), mSubscription2);
-    }
-
-    @MediumTest
-    @Test
-    public void testLoadWithPrefix() throws TimeoutException {
-        save(mSubscription1);
-        loadSingleAndCheckResult(
-                CommerceSubscriptionsStorage.getKey(mSubscription1), mSubscription1);
-        save(mSubscription2);
-        loadSingleAndCheckResult(
-                CommerceSubscriptionsStorage.getKey(mSubscription2), mSubscription2);
-        save(mSubscription3);
-        loadSingleAndCheckResult(
-                CommerceSubscriptionsStorage.getKey(mSubscription3), mSubscription3);
-        save(mSubscription4);
-        loadSingleAndCheckResult(
-                CommerceSubscriptionsStorage.getKey(mSubscription4), mSubscription4);
-        String prefix1 =
-                String.format("%s_%s", CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        CommerceSubscription.TrackingIdType.OFFER_ID);
-        loadPrefixAndCheckResult(
-                prefix1, new ArrayList<>(Arrays.asList(mSubscription1, mSubscription2)));
-        String prefix2 = CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK;
-        loadPrefixAndCheckResult(prefix2,
-                new ArrayList<>(Arrays.asList(
-                        mSubscription3, mSubscription1, mSubscription2, mSubscription4)));
-    }
-
-    @MediumTest
-    @Test
-    public void testDeleteAll() throws TimeoutException {
-        save(mSubscription1);
-        loadSingleAndCheckResult(
-                CommerceSubscriptionsStorage.getKey(mSubscription1), mSubscription1);
-        save(mSubscription2);
-        loadSingleAndCheckResult(
-                CommerceSubscriptionsStorage.getKey(mSubscription2), mSubscription2);
-        deleteAll();
-        loadSingleAndCheckResult(CommerceSubscriptionsStorage.getKey(mSubscription1), null);
-        loadSingleAndCheckResult(CommerceSubscriptionsStorage.getKey(mSubscription2), null);
-    }
-
-    private void save(CommerceSubscription subscription) throws TimeoutException {
-        CallbackHelper ch = new CallbackHelper();
-        int chCount = ch.getCallCount();
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mStorage.saveWithCallback(subscription, new Runnable() {
-                @Override
-                public void run() {
-                    ch.notifyCalled();
-                }
-            });
-        });
-        ch.waitForCallback(chCount);
-    }
-
-    private void delete(CommerceSubscription subscription) throws TimeoutException {
-        CallbackHelper ch = new CallbackHelper();
-        int chCount = ch.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            mStorage.deleteForTesting(subscription, new Runnable() {
-                @Override
-                public void run() {
-                    ch.notifyCalled();
-                }
-            });
-        });
-        ch.waitForCallback(chCount);
-    }
-
-    private void deleteAll() throws TimeoutException {
-        CallbackHelper ch = new CallbackHelper();
-        int chCount = ch.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            mStorage.deleteAllForTesting(new Runnable() {
-                @Override
-                public void run() {
-                    ch.notifyCalled();
-                }
-            });
-        });
-        ch.waitForCallback(chCount);
-    }
-
-    private void loadSingleAndCheckResult(String key, CommerceSubscription expected)
-            throws TimeoutException {
-        SubscriptionsLoadCallbackHelper ch = new SubscriptionsLoadCallbackHelper();
-        int chCount = ch.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(() -> mStorage.load(key, (res) -> ch.notifyCalled(res)));
-        ch.waitForCallback(chCount);
-        CommerceSubscription actual = ch.getSingleResult();
-        if (expected == null) {
-            assertNull(actual);
-            return;
-        }
-        assertNotNull(actual);
-        assertEquals(expected, actual);
-    }
-
-    private void loadPrefixAndCheckResult(String prefix, List<CommerceSubscription> expected)
-            throws TimeoutException {
-        SubscriptionsLoadCallbackHelper ch = new SubscriptionsLoadCallbackHelper();
-        int chCount = ch.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> mStorage.loadWithPrefix(prefix, (res) -> ch.notifyCalled(res)));
-        ch.waitForCallback(chCount);
-        List<CommerceSubscription> actual = ch.getResultList();
-        assertNotNull(actual);
-        assertEquals(expected.size(), actual.size());
-        for (int i = 0; i < expected.size(); i++) {
-            assertEquals(expected.get(i), actual.get(i));
-        }
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java
index 50cee4b..664d6bc 100644
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java
+++ b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java
@@ -5,9 +5,9 @@
 package org.chromium.chrome.browser.subscriptions;
 
 import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -29,9 +29,6 @@
 import org.chromium.base.UserDataHost;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.SubscriptionManagementType;
-import org.chromium.chrome.browser.subscriptions.CommerceSubscription.TrackingIdType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabSelectionType;
@@ -41,6 +38,11 @@
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.commerce.core.CommerceSubscription;
+import org.chromium.components.commerce.core.IdentifierType;
+import org.chromium.components.commerce.core.ManagementType;
+import org.chromium.components.commerce.core.ShoppingService;
+import org.chromium.components.commerce.core.SubscriptionType;
 import org.chromium.url.GURL;
 
 import java.util.concurrent.TimeUnit;
@@ -72,8 +74,8 @@
         private String mMockTab2OfferId;
 
         TestImplicitPriceDropSubscriptionsManager(
-                TabModelSelector tabModelSelector, SubscriptionsManagerImpl subscriptionsManager) {
-            super(tabModelSelector, subscriptionsManager);
+                TabModelSelector tabModelSelector, ShoppingService shoppingService) {
+            super(tabModelSelector, shoppingService);
         }
 
         @Override
@@ -101,13 +103,15 @@
     @Mock
     TabModelSelector mTabModelSelector;
     @Mock
-    SubscriptionsManagerImpl mSubscriptionsManager;
+    ShoppingService mShoppingService;
     @Mock
     CriticalPersistedTabData mCriticalPersistedTabData1;
     @Mock
     CriticalPersistedTabData mCriticalPersistedTabData2;
     @Captor
     ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
+    @Captor
+    ArgumentCaptor<CommerceSubscription> mSubscriptionCaptor;
 
     private TabImpl mTab1;
     private TabImpl mTab2;
@@ -126,16 +130,16 @@
                 + TimeUnit.DAYS.toMillis(7);
         doReturn(fakeTimestamp).when(mCriticalPersistedTabData1).getTimestampMillis();
         doReturn(fakeTimestamp).when(mCriticalPersistedTabData2).getTimestampMillis();
-        mSubscription1 = new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK, OFFER1_ID,
-                SubscriptionManagementType.CHROME_MANAGED, TrackingIdType.OFFER_ID);
-        mSubscription2 = new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK, OFFER2_ID,
-                SubscriptionManagementType.CHROME_MANAGED, TrackingIdType.OFFER_ID);
+        mSubscription1 = new CommerceSubscription(SubscriptionType.PRICE_TRACK,
+                IdentifierType.OFFER_ID, OFFER1_ID, ManagementType.CHROME_MANAGED, null);
+        mSubscription2 = new CommerceSubscription(SubscriptionType.PRICE_TRACK,
+                IdentifierType.OFFER_ID, OFFER2_ID, ManagementType.CHROME_MANAGED, null);
         doReturn(2).when(mTabModel).getCount();
         doReturn(mTabModel).when(mTabModelSelector).getModel(false);
         doNothing().when(mTabModel).addObserver(mTabModelObserverCaptor.capture());
 
-        mImplicitSubscriptionsManager = new TestImplicitPriceDropSubscriptionsManager(
-                mTabModelSelector, mSubscriptionsManager);
+        mImplicitSubscriptionsManager =
+                new TestImplicitPriceDropSubscriptionsManager(mTabModelSelector, mShoppingService);
         mImplicitSubscriptionsManager.setupForFetchOfferId(mTab1, mTab2, OFFER1_ID, OFFER2_ID);
     }
 
@@ -190,22 +194,25 @@
     @Test
     public void testTabClosure() {
         mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
-        verify(mSubscriptionsManager, times(1))
-                .unsubscribe(eq(mSubscription1), any(Callback.class));
+        verify(mShoppingService, times(1))
+                .unsubscribe(mSubscriptionCaptor.capture(), any(Callback.class));
+        assertEquals(OFFER1_ID, mSubscriptionCaptor.getValue().id);
     }
 
     @Test
     public void testTabRemove() {
         mTabModelObserverCaptor.getValue().tabRemoved(mTab1);
-        verify(mSubscriptionsManager, times(1))
-                .unsubscribe(eq(mSubscription1), any(Callback.class));
+        verify(mShoppingService, times(1))
+                .unsubscribe(mSubscriptionCaptor.capture(), any(Callback.class));
+        assertEquals(OFFER1_ID, mSubscriptionCaptor.getValue().id);
     }
 
     @Test
     public void testTabSelected() {
         mTabModelObserverCaptor.getValue().didSelectTab(mTab1, TabSelectionType.FROM_USER, TAB2_ID);
-        verify(mSubscriptionsManager, times(1))
-                .unsubscribe(eq(mSubscription1), any(Callback.class));
+        verify(mShoppingService, times(1))
+                .unsubscribe(mSubscriptionCaptor.capture(), any(Callback.class));
+        assertEquals(OFFER1_ID, mSubscriptionCaptor.getValue().id);
     }
 
     @Test
@@ -215,8 +222,7 @@
         mImplicitSubscriptionsManager.setupForFetchOfferId(mTab1, mTab2, OFFER1_ID, OFFER2_ID);
 
         mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
-        verify(mSubscriptionsManager, times(0))
-                .unsubscribe(eq(mSubscription1), any(Callback.class));
+        verify(mShoppingService, times(0)).unsubscribe(any(), any(Callback.class));
     }
 
     @Test
@@ -224,8 +230,7 @@
         mImplicitSubscriptionsManager.setupForFetchOfferId(mTab1, mTab2, null, OFFER2_ID);
 
         mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
-        verify(mSubscriptionsManager, times(0))
-                .unsubscribe(eq(mSubscription1), any(Callback.class));
+        verify(mShoppingService, times(0)).unsubscribe(any(), any(Callback.class));
     }
 
     @Test
@@ -252,10 +257,19 @@
     private void initializeSubscriptionsAndVerify(
             boolean shouldSubscribeTab1, boolean shouldSubscribeTab2) {
         mImplicitSubscriptionsManager.initializeSubscriptions();
-        verify(mSubscriptionsManager, times(shouldSubscribeTab1 ? 1 : 0))
-                .subscribe(eq(mSubscription1), any(Callback.class));
-        verify(mSubscriptionsManager, times(shouldSubscribeTab2 ? 1 : 0))
-                .subscribe(eq(mSubscription2), any(Callback.class));
+        if (shouldSubscribeTab1 && shouldSubscribeTab2) {
+            verify(mShoppingService, times(2))
+                    .subscribe(mSubscriptionCaptor.capture(), any(Callback.class));
+            assertEquals(OFFER1_ID, mSubscriptionCaptor.getAllValues().get(0).id);
+            assertEquals(OFFER2_ID, mSubscriptionCaptor.getAllValues().get(1).id);
+        } else if (shouldSubscribeTab1 || shouldSubscribeTab2) {
+            verify(mShoppingService, times(1))
+                    .subscribe(mSubscriptionCaptor.capture(), any(Callback.class));
+            assertEquals(
+                    shouldSubscribeTab1 ? OFFER1_ID : OFFER2_ID, mSubscriptionCaptor.getValue().id);
+        } else {
+            verify(mShoppingService, times(0)).subscribe(any(), any(Callback.class));
+        }
     }
 
     private void verifyEligibleSubscriptionMetrics(int eligibleCount, int totalCount) {
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsLoadCallbackHelper.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsLoadCallbackHelper.java
deleted file mode 100644
index 7a915f2f..0000000
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsLoadCallbackHelper.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import org.chromium.base.test.util.CallbackHelper;
-
-import java.util.List;
-
-/**
- * Helper class for load operations to get load results from {@link CommerceSubscriptionsStorage}.
- */
-public class SubscriptionsLoadCallbackHelper extends CallbackHelper {
-    private CommerceSubscription mSingleResult;
-    private List<CommerceSubscription> mResultList;
-
-    /**
-     * Notifies that the callback has returned with single subscription and cache the result.
-     * @param subscription The {@link CommerceSubscription} returned in callback.
-     */
-    void notifyCalled(CommerceSubscription subscription) {
-        mSingleResult = subscription;
-        notifyCalled();
-    }
-
-    /**
-     * Notifies that the callback has returned with a list of subscriptions and cache the result.
-     * @param subscriptions The list of {@link CommerceSubscription} returned in callback.
-     */
-    void notifyCalled(List<CommerceSubscription> subscriptions) {
-        mResultList = subscriptions;
-        notifyCalled();
-    }
-
-    /**
-     * Gets the single {@link CommerceSubscription} from callback.
-     * @return The single {@link CommerceSubscription} in callback.
-     */
-    CommerceSubscription getSingleResult() {
-        return mSingleResult;
-    }
-
-    /**
-     * Gets the list of {@link CommerceSubscription} from callback.
-     * @return The list of {@link CommerceSubscription} in callback.
-     */
-    List<CommerceSubscription> getResultList() {
-        return mResultList;
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManagerImplTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManagerImplTest.java
deleted file mode 100644
index e6cfa27..0000000
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManagerImplTest.java
+++ /dev/null
@@ -1,582 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.subscriptions;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.Callback;
-import org.chromium.base.FeatureList;
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
-import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManagerFactory;
-import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Tests related to {@link SubscriptionsManagerImpl}.
- */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class SubscriptionsManagerImplTest {
-    @Rule
-    public TestRule mProcessor = new Features.JUnitProcessor();
-
-    @Rule
-    public JniMocker mMocker = new JniMocker();
-
-    private static final String OFFER_ID_1 = "offer_id_1";
-    private static final String OFFER_ID_2 = "offer_id_2";
-    private static final String OFFER_ID_3 = "offer_id_3";
-    private static final String OFFER_ID_4 = "offer_id_4";
-
-    @Mock
-    private Profile mProfile;
-    @Mock
-    private CommerceSubscriptionsStorage.Natives mCommerceSubscriptionsStorageJni;
-    @Mock
-    private CommerceSubscriptionsStorage mStorage;
-    @Mock
-    private CommerceSubscriptionsServiceProxy mProxy;
-
-    private SubscriptionsManagerImpl mSubscriptionsManager;
-    private CommerceSubscription mSubscription1;
-    private CommerceSubscription mSubscription2;
-    private CommerceSubscription mSubscription3;
-    private CommerceSubscription mSubscription4;
-    private FeatureList.TestValues mTestValues;
-
-    private final class SubscriptionsComparator implements Comparator<CommerceSubscription> {
-        @Override
-        public int compare(CommerceSubscription s1, CommerceSubscription s2) {
-            return s1.getTrackingId().compareTo(s2.getTrackingId());
-        }
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mTestValues = new FeatureList.TestValues();
-        mTestValues.addFeatureFlagOverride(ChromeFeatureList.COMMERCE_PRICE_TRACKING, true);
-        mTestValues.addFieldTrialParamOverride(ChromeFeatureList.COMMERCE_PRICE_TRACKING,
-                PriceTrackingFeatures.PRICE_NOTIFICATION_PARAM, "true");
-        FeatureList.setTestValues(mTestValues);
-        PriceTrackingFeatures.setIsSignedInAndSyncEnabledForTesting(true);
-
-        PriceDropNotificationManager priceDropNotificationManager =
-                PriceDropNotificationManagerFactory.create();
-        mMocker.mock(CommerceSubscriptionsStorageJni.TEST_HOOKS, mCommerceSubscriptionsStorageJni);
-        mSubscriptionsManager = new SubscriptionsManagerImpl(
-                mProfile, mStorage, mProxy, priceDropNotificationManager);
-
-        mSubscription1 =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        OFFER_ID_1, CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                        CommerceSubscription.TrackingIdType.OFFER_ID);
-        mSubscription2 =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        OFFER_ID_2, CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                        CommerceSubscription.TrackingIdType.OFFER_ID);
-        mSubscription3 =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        OFFER_ID_3, CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                        CommerceSubscription.TrackingIdType.OFFER_ID);
-        mSubscription4 =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        OFFER_ID_4, CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                        CommerceSubscription.TrackingIdType.OFFER_ID);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mStorage.deleteAll();
-            mStorage.destroy();
-            mSubscriptionsManager.setRemoteSubscriptionsForTesting(null);
-        });
-    }
-
-    @MediumTest
-    @Test
-    public void testSubscribeDeferred() {
-        List<CommerceSubscription> remoteSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription1, mSubscription2));
-
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-
-        // Call subscribe before the service is ready.
-        mSubscriptionsManager.subscribe(mSubscription1, subsCallback);
-        verify(subsCallback, never()).onResult(any(Integer.class));
-
-        // Capture the getSubscriptions callback but don't resolve it.
-        ArgumentCaptor<Callback<List<CommerceSubscription>>> getSubscriptionsCallbackCaptor =
-                ArgumentCaptor.forClass(Callback.class);
-        verify(mProxy, times(1))
-                .get(eq(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK),
-                        getSubscriptionsCallbackCaptor.capture());
-
-        setLoadWithPrefixMockResponse(new ArrayList<>());
-        setMockProxyCreateResponse(true);
-
-        // Resolve the original callback to getSubscriptions started through initTypes.
-        getSubscriptionsCallbackCaptor.getValue().onResult(remoteSubscriptions);
-
-        // Resolve the callback for getSubscriptions started through subscribe.
-        verify(mProxy, times(2))
-                .get(eq(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK),
-                        getSubscriptionsCallbackCaptor.capture());
-        getSubscriptionsCallbackCaptor.getValue().onResult(remoteSubscriptions);
-
-        verify(subsCallback, times(1)).onResult(eq(SubscriptionsManager.StatusCode.OK));
-    }
-
-    @MediumTest
-    @Test
-    public void testSubscribeDeferredInternalError() {
-        List<CommerceSubscription> remoteSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription1, mSubscription2));
-
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-
-        // Call subscribe before the service is ready.
-        mSubscriptionsManager.subscribe(mSubscription1, subsCallback);
-        verify(subsCallback, never()).onResult(any(Integer.class));
-
-        ArgumentCaptor<Callback<List<CommerceSubscription>>> getSubscriptionsCallbackCaptor =
-                ArgumentCaptor.forClass(Callback.class);
-        verify(mProxy, times(1))
-                .get(eq(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK),
-                        getSubscriptionsCallbackCaptor.capture());
-
-        setLoadWithPrefixMockResponse(new ArrayList<>());
-        setMockProxyCreateResponse(false);
-
-        // Resolve the original callback to getSubscriptions started through initTypes.
-        getSubscriptionsCallbackCaptor.getValue().onResult(remoteSubscriptions);
-        verify(subsCallback, times(1)).onResult(eq(SubscriptionsManager.StatusCode.NETWORK_ERROR));
-    }
-
-    @MediumTest
-    @Test
-    public void testSubscribeSingle() {
-        CommerceSubscription newSubscription = mSubscription4;
-        List<CommerceSubscription> remoteSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription1, mSubscription2, newSubscription));
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription2, mSubscription3));
-
-        mSubscriptionsManager.setCanHandlerequests(true);
-
-        setMockProxyCreateResponse(true);
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        mSubscriptionsManager.setRemoteSubscriptionsForTesting(remoteSubscriptions);
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-
-        // Call subscribe and verify the result.
-        mSubscriptionsManager.subscribe(newSubscription, subsCallback);
-        verify(subsCallback, times(1)).onResult(SubscriptionsManager.StatusCode.OK);
-
-        ArgumentCaptor<CommerceSubscription> storageSaveCaptor =
-                ArgumentCaptor.forClass(CommerceSubscription.class);
-        verify(mStorage, times(2)).save(storageSaveCaptor.capture());
-        System.out.println(storageSaveCaptor.getAllValues());
-
-        ArgumentCaptor<CommerceSubscription> storageDeleteCaptor =
-                ArgumentCaptor.forClass(CommerceSubscription.class);
-        verify(mStorage, times(1)).delete(storageDeleteCaptor.capture());
-        System.out.println(storageDeleteCaptor.getAllValues());
-
-        List<CommerceSubscription> subscriptionsToSave = storageSaveCaptor.getAllValues();
-        Collections.sort(subscriptionsToSave, new SubscriptionsComparator());
-
-        List<CommerceSubscription> subscriptionsToDelete = storageDeleteCaptor.getAllValues();
-        Collections.sort(subscriptionsToDelete, new SubscriptionsComparator());
-
-        assertEquals(new ArrayList<>(Arrays.asList(mSubscription1, newSubscription)),
-                subscriptionsToSave);
-        assertEquals(new ArrayList<>(Arrays.asList(mSubscription3)), subscriptionsToDelete);
-    }
-
-    @MediumTest
-    @Test
-    public void testSubscribeInvalidSubscription() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        // Null subscription.
-        CommerceSubscription newSubscription = null;
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-        mSubscriptionsManager.subscribe(newSubscription, subsCallback);
-        verify(subsCallback, times(1)).onResult(SubscriptionsManager.StatusCode.INVALID_ARGUMENT);
-
-        // Invalid type.
-        newSubscription = new CommerceSubscription(
-                CommerceSubscription.CommerceSubscriptionType.TYPE_UNSPECIFIED, OFFER_ID_1,
-                CommerceSubscription.SubscriptionManagementType.CHROME_MANAGED,
-                CommerceSubscription.TrackingIdType.OFFER_ID);
-        Callback<Integer> subsCallback2 = Mockito.mock(Callback.class);
-        mSubscriptionsManager.subscribe(newSubscription, subsCallback2);
-        verify(subsCallback, times(1)).onResult(SubscriptionsManager.StatusCode.INVALID_ARGUMENT);
-    }
-
-    @MediumTest
-    @Test
-    public void testSubscribeDuplicatesNop() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription2, mSubscription3));
-        List<CommerceSubscription> newSubscriptions = localSubscriptions;
-
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-        mSubscriptionsManager.subscribe(newSubscriptions, subsCallback);
-        verify(subsCallback, times(1)).onResult(SubscriptionsManager.StatusCode.OK);
-        verify(mProxy, never()).create(any(List.class), any(Callback.class));
-    }
-
-    @MediumTest
-    @Test
-    public void testSubscribeCreateUnique() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription2, mSubscription3));
-        List<CommerceSubscription> newSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription1, mSubscription2));
-
-        setLoadWithPrefixMockResponse(localSubscriptions);
-        setMockProxyCreateResponse(true);
-
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-        mSubscriptionsManager.subscribe(newSubscriptions, subsCallback);
-
-        ArgumentCaptor<List<CommerceSubscription>> subscriptionsToCreateCaptor =
-                ArgumentCaptor.forClass(List.class);
-        verify(mProxy, times(1)).create(subscriptionsToCreateCaptor.capture(), any(Callback.class));
-        assertEquals(new ArrayList<>(Arrays.asList(mSubscription1)),
-                subscriptionsToCreateCaptor.getValue());
-    }
-
-    @MediumTest
-    @Test
-    public void testSubscribeList() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        List<CommerceSubscription> newSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription1, mSubscription2));
-        List<CommerceSubscription> remoteSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription1, mSubscription2, mSubscription4));
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription2, mSubscription3));
-
-        setMockProxyCreateResponse(true);
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        mSubscriptionsManager.setRemoteSubscriptionsForTesting(remoteSubscriptions);
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-
-        // Call subscribe and verify the result.
-        mSubscriptionsManager.subscribe(newSubscriptions, subsCallback);
-        verify(subsCallback, times(1)).onResult(SubscriptionsManager.StatusCode.OK);
-
-        ArgumentCaptor<CommerceSubscription> storageSaveCaptor =
-                ArgumentCaptor.forClass(CommerceSubscription.class);
-        verify(mStorage, times(2)).save(storageSaveCaptor.capture());
-
-        ArgumentCaptor<CommerceSubscription> storageDeleteCaptor =
-                ArgumentCaptor.forClass(CommerceSubscription.class);
-        verify(mStorage, times(1)).delete(storageDeleteCaptor.capture());
-        System.out.println(storageDeleteCaptor.getAllValues());
-
-        List<CommerceSubscription> subscriptionsToSave = storageSaveCaptor.getAllValues();
-        Collections.sort(subscriptionsToSave, new SubscriptionsComparator());
-
-        List<CommerceSubscription> subscriptionsToDelete = storageDeleteCaptor.getAllValues();
-        Collections.sort(subscriptionsToDelete, new SubscriptionsComparator());
-
-        assertEquals(new ArrayList<>(Arrays.asList(mSubscription1, mSubscription4)),
-                subscriptionsToSave);
-        assertEquals(new ArrayList<>(Arrays.asList(mSubscription3)), subscriptionsToDelete);
-    }
-
-    @MediumTest
-    @Test
-    public void testUnsubscribe() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        CommerceSubscription removedSubscription = mSubscription3;
-        List<CommerceSubscription> remoteSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription2, mSubscription4));
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription2, removedSubscription, mSubscription4));
-
-        setMockProxyDeleteResponse(true);
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        mSubscriptionsManager.setRemoteSubscriptionsForTesting(remoteSubscriptions);
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-
-        // Call unsubscribe and verify the result.
-        mSubscriptionsManager.unsubscribe(removedSubscription, subsCallback);
-        verify(subsCallback, times(1)).onResult(SubscriptionsManager.StatusCode.OK);
-
-        ArgumentCaptor<CommerceSubscription> storageSaveCaptor =
-                ArgumentCaptor.forClass(CommerceSubscription.class);
-        verify(mStorage, never()).save(storageSaveCaptor.capture());
-
-        ArgumentCaptor<CommerceSubscription> storageDeleteCaptor =
-                ArgumentCaptor.forClass(CommerceSubscription.class);
-        verify(mStorage, times(1)).delete(storageDeleteCaptor.capture());
-        System.out.println(storageDeleteCaptor.getAllValues());
-
-        List<CommerceSubscription> subscriptionsToDelete = storageDeleteCaptor.getAllValues();
-        Collections.sort(subscriptionsToDelete, new SubscriptionsComparator());
-
-        assertEquals(new ArrayList<>(Arrays.asList(removedSubscription)), subscriptionsToDelete);
-    }
-
-    @MediumTest
-    @Test
-    public void testUnsubscribeNop() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription2, mSubscription3));
-
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-        mSubscriptionsManager.unsubscribe(mSubscription4, subsCallback);
-
-        ArgumentCaptor<List<CommerceSubscription>> subscriptionsToDeleteCaptor =
-                ArgumentCaptor.forClass(List.class);
-        verify(mProxy, never()).delete(subscriptionsToDeleteCaptor.capture(), any(Callback.class));
-    }
-
-    @MediumTest
-    @Test
-    public void testUnsubscribeDeleteIfInCache() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription2, mSubscription3));
-
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        Callback<Integer> subsCallback = Mockito.mock(Callback.class);
-        mSubscriptionsManager.unsubscribe(mSubscription2, subsCallback);
-
-        ArgumentCaptor<List<CommerceSubscription>> subscriptionsToDeleteCaptor =
-                ArgumentCaptor.forClass(List.class);
-        verify(mProxy, times(1)).delete(subscriptionsToDeleteCaptor.capture(), any(Callback.class));
-        assertEquals(new ArrayList<>(Arrays.asList(mSubscription2)),
-                subscriptionsToDeleteCaptor.getValue());
-    }
-
-    @MediumTest
-    @Test
-    public void testGetLocalSubscriptions() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription1, mSubscription2));
-
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        Callback<List<CommerceSubscription>> getSubscriptionsCallback =
-                Mockito.mock(Callback.class);
-        mSubscriptionsManager.getSubscriptions(
-                CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK, false,
-                getSubscriptionsCallback);
-
-        ArgumentCaptor<List<CommerceSubscription>> resultCaptor =
-                ArgumentCaptor.forClass(List.class);
-        verify(getSubscriptionsCallback, times(1)).onResult(resultCaptor.capture());
-
-        Collections.sort(resultCaptor.getValue(), new SubscriptionsComparator());
-        Collections.sort(localSubscriptions, new SubscriptionsComparator());
-        assertEquals(localSubscriptions, resultCaptor.getValue());
-    }
-
-    @MediumTest
-    @Test
-    public void testIsSubscribedDifferentTrackingIdType() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription2));
-
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        CommerceSubscription subscriptionToCheck =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        mSubscription1.getTrackingId(), mSubscription1.getType(),
-                        CommerceSubscription.TrackingIdType.PRODUCT_CLUSTER_ID);
-
-        Callback<Boolean> isSubscribedCallback = Mockito.mock(Callback.class);
-        mSubscriptionsManager.isSubscribed(subscriptionToCheck, isSubscribedCallback);
-
-        ArgumentCaptor<Boolean> resultCaptor = ArgumentCaptor.forClass(Boolean.class);
-        verify(isSubscribedCallback, times(1)).onResult(resultCaptor.capture());
-        assertEquals(false, resultCaptor.getValue());
-    }
-
-    @MediumTest
-    @Test
-    public void testIsSubscribedDifferentTimestamps() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        CommerceSubscription subscription3 =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        mSubscription1.getTrackingId(), mSubscription1.getType(),
-                        mSubscription1.getTrackingIdType(), 1234L);
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription1, subscription3));
-
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        CommerceSubscription subscriptionToCheck =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        mSubscription1.getTrackingId(), mSubscription1.getType(),
-                        mSubscription1.getTrackingIdType(), 222L);
-
-        Callback<Boolean> isSubscribedCallback = Mockito.mock(Callback.class);
-        mSubscriptionsManager.isSubscribed(subscriptionToCheck, isSubscribedCallback);
-
-        ArgumentCaptor<Boolean> resultCaptor = ArgumentCaptor.forClass(Boolean.class);
-        verify(isSubscribedCallback, times(1)).onResult(resultCaptor.capture());
-        assertEquals(true, resultCaptor.getValue());
-    }
-
-    @MediumTest
-    @Test
-    public void testIsSubscribed() {
-        mSubscriptionsManager.setCanHandlerequests(true);
-        List<CommerceSubscription> localSubscriptions =
-                new ArrayList<>(Arrays.asList(mSubscription1, mSubscription2));
-
-        setLoadWithPrefixMockResponse(localSubscriptions);
-
-        CommerceSubscription subscriptionToCheck =
-                new CommerceSubscription(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK,
-                        mSubscription1.getTrackingId(), mSubscription1.getType(),
-                        mSubscription1.getTrackingIdType());
-
-        Callback<Boolean> isSubscribedCallback = Mockito.mock(Callback.class);
-        mSubscriptionsManager.isSubscribed(subscriptionToCheck, isSubscribedCallback);
-
-        ArgumentCaptor<Boolean> resultCaptor = ArgumentCaptor.forClass(Boolean.class);
-        verify(isSubscribedCallback, times(1)).onResult(resultCaptor.capture());
-        assertEquals(true, resultCaptor.getValue());
-    }
-
-    @MediumTest
-    @Test
-    public void testOnIdentityChanged_AccountCleared() {
-        // Fetch subscriptions when SubscriptionManager is created.
-        verify(mStorage, times(1)).deleteAll();
-        verify(mProxy, times(1))
-                .get(eq(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK),
-                        any(Callback.class));
-
-        // Simulate user signs out. We should delete local storage but not fetch data from server.
-        PriceTrackingFeatures.setIsSignedInAndSyncEnabledForTesting(false);
-        mSubscriptionsManager.onIdentityChanged();
-        verify(mStorage, times(2)).deleteAll();
-        verify(mProxy, times(1))
-                .get(eq(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK),
-                        any(Callback.class));
-        verify(mProxy, times(0)).queryAndUpdateWaaEnabled();
-    }
-
-    @MediumTest
-    @Test
-    public void testOnIdentityChanged_AccountChanged() {
-        // Fetch subscriptions when SubscriptionManager is created.
-        verify(mStorage, times(1)).deleteAll();
-        verify(mProxy, times(1))
-                .get(eq(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK),
-                        any(Callback.class));
-
-        // Simulate user switches account. We should delete local storage and also fetch new data
-        // from server.
-        PriceTrackingFeatures.setIsSignedInAndSyncEnabledForTesting(true);
-        mSubscriptionsManager.onIdentityChanged();
-        verify(mStorage, times(3)).deleteAll();
-        verify(mProxy, times(2))
-                .get(eq(CommerceSubscription.CommerceSubscriptionType.PRICE_TRACK),
-                        any(Callback.class));
-        verify(mProxy, times(1)).queryAndUpdateWaaEnabled();
-    }
-
-    private void setLoadWithPrefixMockResponse(List<CommerceSubscription> subscriptions) {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) {
-                Callback callback = (Callback) invocation.getArguments()[1];
-                callback.onResult(subscriptions);
-                return null;
-            }
-        })
-                .when(mStorage)
-                .loadWithPrefix(any(String.class), any(Callback.class));
-    }
-
-    private void setMockProxyCreateResponse(boolean expectedResult) {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) {
-                Callback callback = (Callback) invocation.getArguments()[1];
-                callback.onResult(expectedResult);
-                return null;
-            }
-        })
-                .when(mProxy)
-                .create(any(List.class), any(Callback.class));
-    }
-
-    private void setMockProxyDeleteResponse(boolean expectedResult) {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) {
-                Callback callback = (Callback) invocation.getArguments()[1];
-                callback.onResult(expectedResult);
-                return null;
-            }
-        })
-                .when(mProxy)
-                .delete(any(List.class), any(Callback.class));
-    }
-
-    private void printList(List<CommerceSubscription> list) {
-        for (CommerceSubscription ss : list) {
-            System.out.println(ss.getTrackingId());
-        }
-    }
-}
diff --git a/chrome/browser/commerce/subscriptions/test/android/test_java_sources.gni b/chrome/browser/commerce/subscriptions/test/android/test_java_sources.gni
index 4b0514b..afb59c2 100644
--- a/chrome/browser/commerce/subscriptions/test/android/test_java_sources.gni
+++ b/chrome/browser/commerce/subscriptions/test/android/test_java_sources.gni
@@ -5,11 +5,9 @@
 # TODO(crbug/1210158): This should be a separate build target when circular
 # dependencies are removed.
 commerce_subscriptions_junit_test_sources = [
-  "//chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionJsonSerializerUnitTest.java",
   "//chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceFactoryUnitTest.java",
   "//chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceUnitTest.java",
   "//chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java",
-  "//chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsManagerImplTest.java",
 ]
 
 commerce_subscriptions_junit_test_deps = [
@@ -35,29 +33,3 @@
   "//url:gurl_java",
   "//url:gurl_junit_test_support",
 ]
-
-commerce_subscriptions_java_test_sources = [
-  "//chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceProxyUnitTest.java",
-  "//chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsStorageTest.java",
-  "//chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/SubscriptionsLoadCallbackHelper.java",
-]
-
-commerce_subscriptions_java_test_deps = [
-  "//base:base_java",
-  "//base:base_java_test_support",
-  "//base:jni_java",
-  "//build/android:build_java",
-  "//chrome/android:base_module_java",
-  "//chrome/browser/android/lifecycle:java",
-  "//chrome/browser/endpoint_fetcher:java",
-  "//chrome/browser/flags:java",
-  "//chrome/browser/profiles/android:java",
-  "//chrome/browser/tabmodel:java",
-  "//chrome/test/android:chrome_java_integration_test_support",
-  "//content/public/test/android:content_java_test_support",
-  "//third_party/androidx:androidx_test_core_java",
-  "//third_party/androidx:androidx_test_runner_java",
-  "//third_party/hamcrest:hamcrest_java",
-  "//third_party/junit",
-  "//third_party/mockito:mockito_java",
-]
diff --git a/chrome/browser/download/notification/download_notification_browsertest.cc b/chrome/browser/download/notification/download_notification_browsertest.cc
index 9e15158..283222f 100644
--- a/chrome/browser/download/notification/download_notification_browsertest.cc
+++ b/chrome/browser/download/notification/download_notification_browsertest.cc
@@ -66,6 +66,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
+#include "components/user_manager/user_manager.h"
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chromeos/startup/browser_init_params.h"
 #endif
diff --git a/chrome/browser/enterprise/connectors/common.cc b/chrome/browser/enterprise/connectors/common.cc
index 6fa91a2..2d6874b6 100644
--- a/chrome/browser/enterprise/connectors/common.cc
+++ b/chrome/browser/enterprise/connectors/common.cc
@@ -16,6 +16,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "components/user_manager/user.h"
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/enterprise/connectors/connectors_prefs.cc b/chrome/browser/enterprise/connectors/connectors_prefs.cc
index 2f4ec4e..97e119c 100644
--- a/chrome/browser/enterprise/connectors/connectors_prefs.cc
+++ b/chrome/browser/enterprise/connectors/connectors_prefs.cc
@@ -63,10 +63,4 @@
   RegisterDeviceTrustConnectorProfilePrefs(registry);
 }
 
-#if BUILDFLAG(IS_MAC)
-void RegisterLocalPrefs(PrefRegistrySimple* registry) {
-  RegisterDeviceTrustConnectorLocalPrefs(registry);
-}
-#endif
-
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/connectors_prefs.h b/chrome/browser/enterprise/connectors/connectors_prefs.h
index fc75a6f..ef6f1c5 100644
--- a/chrome/browser/enterprise/connectors/connectors_prefs.h
+++ b/chrome/browser/enterprise/connectors/connectors_prefs.h
@@ -45,10 +45,6 @@
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
-#if BUILDFLAG(IS_MAC)
-void RegisterLocalPrefs(PrefRegistrySimple* registry);
-#endif
-
 }  // namespace enterprise_connectors
 
 #endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_CONNECTORS_PREFS_H_
diff --git a/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.cc b/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.cc
index e8b154d7..abe1777 100644
--- a/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.cc
@@ -19,6 +19,11 @@
 BrowserDeviceTrustConnectorService::~BrowserDeviceTrustConnectorService() =
     default;
 
+bool BrowserDeviceTrustConnectorService::IsConnectorEnabled() const {
+  return DeviceTrustConnectorService::IsConnectorEnabled() &&
+         !key_manager_->HasPermanentFailure();
+}
+
 void BrowserDeviceTrustConnectorService::OnConnectorEnabled() {
   key_manager_->StartInitialization();
 }
diff --git a/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.h b/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.h
index a99b361f..fd9933e7c 100644
--- a/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.h
+++ b/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.h
@@ -29,12 +29,16 @@
 
   ~BrowserDeviceTrustConnectorService() override;
 
+  // DeviceTrustConnectorService:
+  bool IsConnectorEnabled() const override;
+
  protected:
   // Hook that is called to notify that the policy changed and the connector
   // became, or is still, enabled.
   void OnConnectorEnabled() override;
 
  private:
+  // Browser-process-level object that will outlive the current instance.
   raw_ptr<DeviceTrustKeyManager> key_manager_;
 };
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service_unittest.cc
similarity index 65%
rename from chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service_unittest.cc
rename to chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service_unittest.cc
index aaa45eab..2b28bf5 100644
--- a/chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service_unittest.cc
@@ -1,8 +1,8 @@
-// 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.
 
-#include "chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.h"
+#include "chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.h"
 
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
@@ -28,17 +28,15 @@
 
 }  // namespace
 
-class MacDeviceTrustConnectorServiceTest
+class BrowserDeviceTrustConnectorServiceTest
     : public testing::Test,
       public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {
  protected:
   void SetUp() override {
     RegisterDeviceTrustConnectorProfilePrefs(profile_prefs_.registry());
-    RegisterDeviceTrustConnectorLocalPrefs(local_prefs_.registry());
     feature_list_.InitWithFeatureState(kDeviceTrustConnectorEnabled,
                                        is_flag_enabled());
     UpdateAllowlistProfilePreference();
-    UpdateKeyCreationLocalPreference();
   }
 
   void UpdateAllowlistProfilePreference() {
@@ -49,48 +47,43 @@
                                         base::Value(base::Value::List()));
   }
 
-  void UpdateKeyCreationLocalPreference() {
-    local_prefs_.SetManagedPref(kDeviceTrustDisableKeyCreationPref,
-                                base::Value(!is_key_creation_enabled()));
-  }
-
-  std::unique_ptr<MacDeviceTrustConnectorService> CreateService() {
-    return std::make_unique<MacDeviceTrustConnectorService>(
-        &mock_key_manager_, &profile_prefs_, &local_prefs_);
+  std::unique_ptr<BrowserDeviceTrustConnectorService> CreateService() {
+    auto service = std::make_unique<BrowserDeviceTrustConnectorService>(
+        &mock_key_manager_, &profile_prefs_);
+    service->Initialize();
+    return service;
   }
 
   bool is_attestation_flow_enabled() {
     return is_flag_enabled() && is_policy_enabled() &&
-           is_key_creation_enabled();
+           !has_permanent_key_creation_failure();
   }
 
   bool is_flag_enabled() { return std::get<0>(GetParam()); }
   bool is_policy_enabled() { return std::get<1>(GetParam()); }
-  bool is_key_creation_enabled() { return !std::get<2>(GetParam()); }
+  bool has_permanent_key_creation_failure() { return std::get<2>(GetParam()); }
 
   base::test::ScopedFeatureList feature_list_;
-  MockDeviceTrustKeyManager mock_key_manager_;
+  testing::StrictMock<MockDeviceTrustKeyManager> mock_key_manager_;
   TestingPrefServiceSimple profile_prefs_;
-  TestingPrefServiceSimple local_prefs_;
 };
 
-TEST_P(MacDeviceTrustConnectorServiceTest, IsConnectorEnabled) {
+TEST_P(BrowserDeviceTrustConnectorServiceTest, IsConnectorEnabled) {
+  if (is_flag_enabled() && is_policy_enabled()) {
+    // Called when the service is initialized.
+    EXPECT_CALL(mock_key_manager_, StartInitialization());
+
+    // Called in `IsConnectorEnabled`.
+    EXPECT_CALL(mock_key_manager_, HasPermanentFailure())
+        .WillOnce(testing::Return(has_permanent_key_creation_failure()));
+  }
+
   auto service = CreateService();
-  service->Initialize();
   EXPECT_EQ(is_attestation_flow_enabled(), service->IsConnectorEnabled());
 }
 
-// Tests that key manager is initialized only when key creation is not disabled.
-TEST_P(MacDeviceTrustConnectorServiceTest, OnConnectorEnabled) {
-  auto service = CreateService();
-  EXPECT_CALL(mock_key_manager_, StartInitialization())
-      .Times(is_key_creation_enabled() ? 1 : 0);
-
-  service->OnConnectorEnabled();
-}
-
 INSTANTIATE_TEST_SUITE_P(,
-                         MacDeviceTrustConnectorServiceTest,
+                         BrowserDeviceTrustConnectorServiceTest,
                          testing::Combine(testing::Bool(),
                                           testing::Bool(),
                                           testing::Bool()));
diff --git a/chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.cc b/chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.cc
deleted file mode 100644
index 4856584..0000000
--- a/chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.cc
+++ /dev/null
@@ -1,36 +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 "chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.h"
-
-#include "chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.h"
-#include "chrome/browser/enterprise/connectors/device_trust/prefs.h"
-#include "components/enterprise/browser/device_trust/device_trust_key_manager.h"
-#include "components/prefs/pref_service.h"
-
-namespace enterprise_connectors {
-
-MacDeviceTrustConnectorService::MacDeviceTrustConnectorService(
-    DeviceTrustKeyManager* key_manager,
-    PrefService* profile_prefs,
-    PrefService* local_prefs)
-    : BrowserDeviceTrustConnectorService(key_manager, profile_prefs),
-      local_prefs_(local_prefs) {
-  DCHECK(local_prefs_);
-}
-
-MacDeviceTrustConnectorService::~MacDeviceTrustConnectorService() = default;
-
-bool MacDeviceTrustConnectorService::IsConnectorEnabled() const {
-  return BrowserDeviceTrustConnectorService::IsConnectorEnabled() &&
-         !local_prefs_->GetBoolean(kDeviceTrustDisableKeyCreationPref);
-}
-
-void MacDeviceTrustConnectorService::OnConnectorEnabled() {
-  if (!local_prefs_->GetBoolean(kDeviceTrustDisableKeyCreationPref)) {
-    BrowserDeviceTrustConnectorService::OnConnectorEnabled();
-  }
-}
-
-}  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.h b/chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.h
deleted file mode 100644
index 51c815c..0000000
--- a/chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.h
+++ /dev/null
@@ -1,47 +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 CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_BROWSER_MAC_DEVICE_TRUST_CONNECTOR_SERVICE_H_
-#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_BROWSER_MAC_DEVICE_TRUST_CONNECTOR_SERVICE_H_
-
-#include "base/memory/raw_ptr.h"
-#include "chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.h"
-
-class PrefService;
-
-namespace enterprise_connectors {
-
-class DeviceTrustKeyManager;
-
-// Mac implementation of the browser device trust connector service in charge of
-// monitoring the status of the Device Trust connector (e.g. enabled or not) on
-// Mac platforms.
-class MacDeviceTrustConnectorService
-    : public BrowserDeviceTrustConnectorService {
- public:
-  MacDeviceTrustConnectorService(DeviceTrustKeyManager* key_manager,
-                                 PrefService* profile_prefs,
-                                 PrefService* local_prefs);
-
-  MacDeviceTrustConnectorService(const MacDeviceTrustConnectorService&) =
-      delete;
-  MacDeviceTrustConnectorService& operator=(
-      const MacDeviceTrustConnectorService&) = delete;
-
-  ~MacDeviceTrustConnectorService() override;
-
-  // Returns whether the Device Trust connector is enabled or not.
-  bool IsConnectorEnabled() const override;
-
-  // Hook that is called to notify that the policy changed and the connector
-  // became, or is still, enabled.
-  void OnConnectorEnabled() override;
-
- private:
-  base::raw_ptr<PrefService> local_prefs_;
-};
-
-}  // namespace enterprise_connectors
-
-#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_BROWSER_MAC_DEVICE_TRUST_CONNECTOR_SERVICE_H_
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_connector_service_factory.cc b/chrome/browser/enterprise/connectors/device_trust/device_trust_connector_service_factory.cc
index 40ea7725..714fe38 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_connector_service_factory.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_connector_service_factory.cc
@@ -13,11 +13,7 @@
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 #include "chrome/browser/browser_process.h"
-#if BUILDFLAG(IS_MAC)
-#include "chrome/browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service.h"
-#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
 #include "chrome/browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service.h"
-#endif  // BUILDFLAG(IS_MAC)
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "components/enterprise/browser/controller/chrome_browser_cloud_management_controller.h"
 #include "components/enterprise/browser/device_trust/device_trust_key_manager.h"
@@ -86,20 +82,16 @@
     auto* key_manager = g_browser_process->browser_policy_connector()
                             ->chrome_browser_cloud_management_controller()
                             ->GetDeviceTrustKeyManager();
-#if BUILDFLAG(IS_MAC)
-    service = new MacDeviceTrustConnectorService(
-        key_manager, profile->GetPrefs(), g_browser_process->local_state());
-#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
     service = new BrowserDeviceTrustConnectorService(key_manager,
                                                      profile->GetPrefs());
-#endif  // BUILDFLAG(IS_MAC)
   }
 #else
   service = new DeviceTrustConnectorService(profile->GetPrefs());
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 
-  if (service)
+  if (service) {
     service->Initialize();
+  }
 
   return service;
 }
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn
index 73493cfc..6f0b67a 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/BUILD.gn
@@ -24,7 +24,6 @@
 
   deps = [
     "//chrome/browser/enterprise/connectors/device_trust/common",
-    "//components/prefs",
     "//services/network/public/cpp",
     "//third_party/abseil-cpp:absl",
   ]
@@ -58,7 +57,6 @@
       "mac_key_rotation_command.h",
     ]
     deps += [
-      "//chrome/browser/enterprise/connectors/device_trust:prefs",
       "//chrome/browser/enterprise/connectors/device_trust/key_management/core/mac",
       "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network",
       "//chrome/browser/enterprise/connectors/device_trust/key_management/installer:elevated_rotation",
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command_factory.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command_factory.cc
index 256b905..8d188a8 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command_factory.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command_factory.cc
@@ -8,7 +8,6 @@
 #include "base/notreached.h"
 #include "build/build_config.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command.h"
-#include "components/prefs/pref_service.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -43,15 +42,13 @@
 }
 
 std::unique_ptr<KeyRotationCommand> KeyRotationCommandFactory::CreateCommand(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    PrefService* local_prefs) {
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
 #if BUILDFLAG(IS_WIN)
   return std::make_unique<WinKeyRotationCommand>();
 #elif BUILDFLAG(IS_LINUX)
   return std::make_unique<LinuxKeyRotationCommand>(url_loader_factory);
 #elif BUILDFLAG(IS_MAC)
-  return std::make_unique<MacKeyRotationCommand>(url_loader_factory,
-                                                 local_prefs);
+  return std::make_unique<MacKeyRotationCommand>(url_loader_factory);
 #else
   return nullptr;
 #endif  // BUILDFLAG(IS_WIN)
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command_factory.h b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command_factory.h
index 806315a7..4b8b6f2 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command_factory.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command_factory.h
@@ -9,8 +9,6 @@
 
 #include "base/memory/scoped_refptr.h"
 
-class PrefService;
-
 namespace network {
 class SharedURLLoaderFactory;
 }  // namespace network
@@ -27,11 +25,9 @@
 
   // Creates a platform-specific key rotation command
   // object. The shared `url_loader_factory` is used in both the linux and mac
-  // key rotation for mojo support, and the `local_prefs` is needed in the mac
-  // key rotation exclusively for updating a local preference.
+  // key rotation for mojo support.
   virtual std::unique_ptr<KeyRotationCommand> CreateCommand(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      PrefService* local_prefs);
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
  protected:
   static void SetFactoryInstanceForTesting(KeyRotationCommandFactory* factory);
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command.cc
index f1c083ac..b53b2ee 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command.cc
@@ -20,9 +20,7 @@
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/network/mojo_key_network_delegate.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/installer/metrics_util.h"
-#include "chrome/browser/enterprise/connectors/device_trust/prefs.h"
 #include "chrome/common/channel_info.h"
-#include "components/prefs/pref_service.h"
 #include "components/version_info/channel.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "url/gurl.h"
@@ -71,10 +69,8 @@
 }  // namespace
 
 MacKeyRotationCommand::MacKeyRotationCommand(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    PrefService* local_prefs)
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
     : url_loader_factory_(std::move(url_loader_factory)),
-      local_prefs_(local_prefs),
       background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
@@ -96,7 +92,6 @@
   if (!client_->VerifySecureEnclaveSupported()) {
     SYSLOG(ERROR) << "Device trust key rotation failed. The secure enclave is "
                      "not supported.";
-    local_prefs_->SetBoolean(kDeviceTrustDisableKeyCreationPref, true);
     std::move(callback).Run(KeyRotationCommand::Status::FAILED_OS_RESTRICTION);
     return;
   }
@@ -148,7 +143,6 @@
   if (result == KeyRotationManager::Result::FAILED_KEY_CONFLICT) {
     SYSLOG(ERROR) << "Device trust key rotation failed. Conflict "
                      "with the key that exists on the server.";
-    local_prefs_->SetBoolean(kDeviceTrustDisableKeyCreationPref, true);
     std::move(pending_callback_)
         .Run(KeyRotationCommand::Status::FAILED_KEY_CONFLICT);
     return;
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command.h b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command.h
index 96fc04f..5acc563 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command.h
@@ -15,8 +15,6 @@
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/secure_enclave_client.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager.h"
 
-class PrefService;
-
 namespace network {
 class SharedURLLoaderFactory;
 }  // namespace network
@@ -25,9 +23,8 @@
 
 class MacKeyRotationCommand : public KeyRotationCommand {
  public:
-  MacKeyRotationCommand(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      PrefService* local_prefs);
+  explicit MacKeyRotationCommand(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
   ~MacKeyRotationCommand() override;
 
@@ -45,7 +42,6 @@
   void OnKeyRotationTimeout();
 
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  base::raw_ptr<PrefService> local_prefs_;
   std::unique_ptr<KeyRotationManager> key_rotation_manager_;
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command_unittest.cc
index c69cdbe4..aeee956 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mac_key_rotation_command_unittest.cc
@@ -18,8 +18,6 @@
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/installer/key_rotation_manager.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/installer/metrics_util.h"
-#include "chrome/browser/enterprise/connectors/device_trust/prefs.h"
-#include "components/prefs/testing_pref_service.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -88,10 +86,8 @@
     mock_persistence_delegate_ = mock_persistence_delegate.get();
     EXPECT_CALL(*mock_persistence_delegate_, LoadKeyPair());
 
-    RegisterDeviceTrustConnectorLocalPrefs(local_prefs_.registry());
-
     rotation_command_ = absl::WrapUnique(
-        new MacKeyRotationCommand(test_shared_loader_factory_, &local_prefs_));
+        new MacKeyRotationCommand(test_shared_loader_factory_));
 
     KeyRotationManager::SetForTesting(KeyRotationManager::CreateForTesting(
         std::move(mock_network_delegate),
@@ -113,7 +109,6 @@
   MockKeyPersistenceDelegate* mock_persistence_delegate_ = nullptr;
   test::ScopedKeyPersistenceDelegateFactory scoped_factory_;
   KeyRotationCommand::Params params_;
-  TestingPrefServiceSimple local_prefs_;
 };
 
 // Tests a failed key rotation due to the secure enclave not being supported.
@@ -124,7 +119,6 @@
   base::test::TestFuture<KeyRotationCommand::Status> future;
   rotation_command_->Trigger(params_, future.GetCallback());
   EXPECT_EQ(KeyRotationCommand::Status::FAILED_OS_RESTRICTION, future.Get());
-  EXPECT_TRUE(local_prefs_.GetBoolean(kDeviceTrustDisableKeyCreationPref));
 }
 
 // Tests a failed key rotation due to an invalid command to rotate.
@@ -137,7 +131,6 @@
   base::test::TestFuture<KeyRotationCommand::Status> future;
   rotation_command_->Trigger(params_, future.GetCallback());
   EXPECT_EQ(KeyRotationCommand::Status::FAILED, future.Get());
-  EXPECT_FALSE(local_prefs_.GetBoolean(kDeviceTrustDisableKeyCreationPref));
 }
 
 // Tests a failed key rotation due to failure creating a new signing key pair.
@@ -153,7 +146,6 @@
   base::test::TestFuture<KeyRotationCommand::Status> future;
   rotation_command_->Trigger(params_, future.GetCallback());
   EXPECT_EQ(KeyRotationCommand::Status::FAILED, future.Get());
-  EXPECT_FALSE(local_prefs_.GetBoolean(kDeviceTrustDisableKeyCreationPref));
 }
 
 // Tests a failed key rotation due to a store key failure.
@@ -170,7 +162,6 @@
   base::test::TestFuture<KeyRotationCommand::Status> future;
   rotation_command_->Trigger(params_, future.GetCallback());
   EXPECT_EQ(KeyRotationCommand::Status::FAILED, future.Get());
-  EXPECT_FALSE(local_prefs_.GetBoolean(kDeviceTrustDisableKeyCreationPref));
 }
 
 // Tests a failed key rotation when uploading a the key to the dm server
@@ -198,7 +189,6 @@
   base::test::TestFuture<KeyRotationCommand::Status> future;
   rotation_command_->Trigger(params_, future.GetCallback());
   EXPECT_EQ(KeyRotationCommand::Status::FAILED_KEY_CONFLICT, future.Get());
-  EXPECT_TRUE(local_prefs_.GetBoolean(kDeviceTrustDisableKeyCreationPref));
 }
 
 // Tests a failed key rotation due to a failure sending the key to the dm
@@ -226,7 +216,6 @@
   base::test::TestFuture<KeyRotationCommand::Status> future;
   rotation_command_->Trigger(params_, future.GetCallback());
   EXPECT_EQ(KeyRotationCommand::Status::FAILED, future.Get());
-  EXPECT_FALSE(local_prefs_.GetBoolean(kDeviceTrustDisableKeyCreationPref));
 }
 
 // Tests when the key rotation is successful.
@@ -251,7 +240,6 @@
   base::test::TestFuture<KeyRotationCommand::Status> future;
   rotation_command_->Trigger(params_, future.GetCallback());
   EXPECT_EQ(KeyRotationCommand::Status::SUCCEEDED, future.Get());
-  EXPECT_FALSE(local_prefs_.GetBoolean(kDeviceTrustDisableKeyCreationPref));
 
   // Advancing beyond timeout time doesn't cause any crashes.
   FastForwardBeyondTimeout();
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/scoped_key_rotation_command_factory.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/scoped_key_rotation_command_factory.cc
index 3ee07a3..5ea1ca2 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/scoped_key_rotation_command_factory.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/scoped_key_rotation_command_factory.cc
@@ -9,7 +9,6 @@
 
 #include "base/check.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/mock_key_rotation_command.h"
-#include "components/prefs/pref_service.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -31,8 +30,7 @@
 
 std::unique_ptr<KeyRotationCommand>
 ScopedKeyRotationCommandFactory::CreateCommand(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    PrefService* local_prefs) {
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
   if (mock_key_rotation_command_) {
     return std::move(mock_key_rotation_command_);
   }
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/scoped_key_rotation_command_factory.h b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/scoped_key_rotation_command_factory.h
index f62579eb..88db58e 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/scoped_key_rotation_command_factory.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/scoped_key_rotation_command_factory.h
@@ -13,8 +13,6 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-class PrefService;
-
 namespace enterprise_connectors {
 
 namespace test {
@@ -31,8 +29,8 @@
 
   // KeyRotationCommandFactory:
   std::unique_ptr<KeyRotationCommand> CreateCommand(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      PrefService* local_prefs) override;
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+      override;
 
  private:
   std::unique_ptr<test::MockKeyRotationCommand> mock_key_rotation_command_;
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher.cc
index 7103858..506f70b 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher.cc
@@ -9,7 +9,6 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_impl.h"
-#include "components/prefs/pref_service.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace enterprise_connectors {
@@ -18,11 +17,10 @@
 std::unique_ptr<KeyRotationLauncher> KeyRotationLauncher::Create(
     policy::BrowserDMTokenStorage* dm_token_storage,
     policy::DeviceManagementService* device_management_service,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    PrefService* local_prefs) {
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
   return std::make_unique<KeyRotationLauncherImpl>(
       dm_token_storage, device_management_service,
-      std::move(url_loader_factory), local_prefs);
+      std::move(url_loader_factory));
 }
 
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher.h b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher.h
index ed8200f..4ebb4fb 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher.h
@@ -12,8 +12,6 @@
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/key_rotation_command.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-class PrefService;
-
 namespace network {
 class SharedURLLoaderFactory;
 }  // namespace network
@@ -34,8 +32,7 @@
   static std::unique_ptr<KeyRotationLauncher> Create(
       policy::BrowserDMTokenStorage* dm_token_storage,
       policy::DeviceManagementService* device_management_service,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      PrefService* local_prefs);
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
   virtual ~KeyRotationLauncher() = default;
 
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_impl.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_impl.cc
index 4cb422e..fd7f6070c 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_impl.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_impl.cc
@@ -20,7 +20,6 @@
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "components/policy/core/common/cloud/dm_auth.h"
 #include "components/policy/core/common/cloud/dmserver_job_configurations.h"
-#include "components/prefs/pref_service.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -44,17 +43,14 @@
 KeyRotationLauncherImpl::KeyRotationLauncherImpl(
     policy::BrowserDMTokenStorage* dm_token_storage,
     policy::DeviceManagementService* device_management_service,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    PrefService* local_prefs)
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
     : dm_token_storage_(dm_token_storage),
       device_management_service_(device_management_service),
       url_loader_factory_(std::move(url_loader_factory)),
-      local_prefs_(local_prefs),
       network_delegate_(url_loader_factory_) {
   DCHECK(dm_token_storage_);
   DCHECK(device_management_service_);
   DCHECK(url_loader_factory_);
-  DCHECK(local_prefs_);
 }
 KeyRotationLauncherImpl::~KeyRotationLauncherImpl() = default;
 
@@ -77,7 +73,7 @@
   KeyRotationCommand::Params params{dm_token.value(), dm_server_url.value(),
                                     nonce};
   command_ = KeyRotationCommandFactory::GetInstance()->CreateCommand(
-      url_loader_factory_, local_prefs_);
+      url_loader_factory_);
   if (!command_) {
     // Command can be nullptr if trying to create a key on a unsupported
     // platform.
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_impl.h b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_impl.h
index c2f83186..a127d03 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_impl.h
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_impl.h
@@ -17,8 +17,6 @@
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/network/key_upload_request.h"
 #include "chrome/browser/enterprise/connectors/device_trust/key_management/core/network/mojo_key_network_delegate.h"
 
-class PrefService;
-
 namespace network {
 class SharedURLLoaderFactory;
 }  // namespace network
@@ -35,8 +33,7 @@
   KeyRotationLauncherImpl(
       policy::BrowserDMTokenStorage* dm_token_storage,
       policy::DeviceManagementService* device_management_service,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      PrefService* local_prefs);
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   ~KeyRotationLauncherImpl() override;
 
   // KeyRotationLauncher:
@@ -56,7 +53,6 @@
   raw_ptr<policy::BrowserDMTokenStorage> dm_token_storage_;
   raw_ptr<policy::DeviceManagementService> device_management_service_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  base::raw_ptr<PrefService> local_prefs_;
   MojoKeyNetworkDelegate network_delegate_;
 
   std::unique_ptr<KeyRotationCommand> command_;
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_unittest.cc
index 97f326e..eb849df 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/browser/key_rotation_launcher_unittest.cc
@@ -28,7 +28,6 @@
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "components/policy/core/common/cloud/mock_device_management_service.h"
 #include "components/policy/proto/device_management_backend.pb.h"
-#include "components/prefs/testing_pref_service.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -80,9 +79,9 @@
 
     test_key_pair_ = CreateFakeKeyPair();
 
-    launcher_ = KeyRotationLauncher::Create(
-        &fake_dm_token_storage_, &fake_device_management_service_,
-        test_shared_loader_factory_, &local_prefs_);
+    launcher_ = KeyRotationLauncher::Create(&fake_dm_token_storage_,
+                                            &fake_device_management_service_,
+                                            test_shared_loader_factory_);
   }
 
   void SetDMToken() {
@@ -125,7 +124,6 @@
   scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_ =
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           &test_url_loader_factory_);
-  TestingPrefServiceSimple local_prefs_;
   std::unique_ptr<SigningKeyPair> test_key_pair_;
   std::unique_ptr<KeyRotationLauncher> launcher_;
 };
diff --git a/chrome/browser/enterprise/connectors/device_trust/prefs.cc b/chrome/browser/enterprise/connectors/device_trust/prefs.cc
index 416e0ce..16c0ba3 100644
--- a/chrome/browser/enterprise/connectors/device_trust/prefs.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/prefs.cc
@@ -9,19 +9,8 @@
 const char kContextAwareAccessSignalsAllowlistPref[] =
     "enterprise_connectors.device_trust.origins";
 
-#if BUILDFLAG(IS_MAC)
-const char kDeviceTrustDisableKeyCreationPref[] =
-    "enterprise_connectors.device_trust.disable_key_creation";
-#endif
-
 void RegisterDeviceTrustConnectorProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterListPref(kContextAwareAccessSignalsAllowlistPref);
 }
 
-#if BUILDFLAG(IS_MAC)
-void RegisterDeviceTrustConnectorLocalPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterBooleanPref(kDeviceTrustDisableKeyCreationPref, false);
-}
-#endif
-
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/prefs.h b/chrome/browser/enterprise/connectors/device_trust/prefs.h
index 55387f0..03306d2 100644
--- a/chrome/browser/enterprise/connectors/device_trust/prefs.h
+++ b/chrome/browser/enterprise/connectors/device_trust/prefs.h
@@ -13,23 +13,9 @@
 // Pref that maps to the "ContextAwareAccessSignalsAllowlistPref" policy.
 extern const char kContextAwareAccessSignalsAllowlistPref[];
 
-#if BUILDFLAG(IS_MAC)
-// The pref on whether the device trust key creation is disabled for the
-// current user. The device trust key creation is disabled when a key for
-// the device is already present on the Server but a key upload is
-// requested with a another key not signed by the previous one. The key
-// creation is enabled by default.
-extern const char kDeviceTrustDisableKeyCreationPref[];
-#endif
-
 // Registers the device trust connectors profile preferences.
 void RegisterDeviceTrustConnectorProfilePrefs(PrefRegistrySimple* registry);
 
-// Registers the device trust connectors local preferences.
-#if BUILDFLAG(IS_MAC)
-void RegisterDeviceTrustConnectorLocalPrefs(PrefRegistrySimple* registry);
-#endif
-
 }  // namespace enterprise_connectors
 
 #endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_PREFS_H_
diff --git a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc
index 56655c0..82e1dd9 100644
--- a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.cc
@@ -23,7 +23,6 @@
 #include "chrome/install_static/install_util.h"
 #include "chrome/installer/util/util_constants.h"
 #include "components/policy/proto/device_management_backend.pb.h"
-#include "components/prefs/pref_service.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -81,8 +80,7 @@
 
 std::unique_ptr<KeyRotationCommand>
 DeviceTrustTestEnvironmentWin::CreateCommand(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    PrefService* local_prefs) {
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
   if (!worker_thread_.IsRunning()) {
     // Make sure the worker thread is running. Its task runner can be reused for
     // all created commands, and its destruction will be handled automatically.
diff --git a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.h b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.h
index b5b5348e..1525bdb 100644
--- a/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.h
+++ b/chrome/browser/enterprise/connectors/device_trust/test/device_trust_test_environment_win.h
@@ -22,8 +22,8 @@
 
   // KeyRotationCommandFactory:
   std::unique_ptr<KeyRotationCommand> CreateCommand(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      PrefService* local_prefs) override;
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+      override;
 
   // DeviceTrustTestEnvironment:
   void SetUpExistingKey() override;
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index af3e9a7f..d13614a 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -81,6 +81,8 @@
 #include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h"
 #include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h"
 #include "components/account_manager_core/pref_names.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "ui/chromeos/events/pref_names.h"
 #endif
 
diff --git a/chrome/browser/extensions/forced_extensions/install_stage_tracker.cc b/chrome/browser/extensions/forced_extensions/install_stage_tracker.cc
index 62e456b..0c8e747 100644
--- a/chrome/browser/extensions/forced_extensions/install_stage_tracker.cc
+++ b/chrome/browser/extensions/forced_extensions/install_stage_tracker.cc
@@ -14,6 +14,9 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "components/user_manager/user.h"          // nogncheck
+#include "components/user_manager/user_manager.h"  // nogncheck
+#include "components/user_manager/user_type.h"     // nogncheck
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace extensions {
diff --git a/chrome/browser/extensions/installed_loader.cc b/chrome/browser/extensions/installed_loader.cc
index 9cbe4fc..a06999b 100644
--- a/chrome/browser/extensions/installed_loader.cc
+++ b/chrome/browser/extensions/installed_loader.cc
@@ -56,6 +56,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "components/user_manager/user.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 using content::BrowserThread;
diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc
index 1d3b4b65..24b1b79 100644
--- a/chrome/browser/file_select_helper.cc
+++ b/chrome/browser/file_select_helper.cc
@@ -469,6 +469,7 @@
     for (size_t i = 0; i < data.paths.size(); ++i) {
       result.paths_results[i] = false;
     }
+    RunFileChooserEnd();
   }
 }
 
diff --git a/chrome/browser/file_select_helper.h b/chrome/browser/file_select_helper.h
index bacd6fd..d4b4920 100644
--- a/chrome/browser/file_select_helper.h
+++ b/chrome/browser/file_select_helper.h
@@ -105,6 +105,9 @@
                            ContentAnalysisCompletionCallback_FolderUpload_Bad);
   FRIEND_TEST_ALL_PREFIXES(
       FileSelectHelperTest,
+      ContentAnalysisCompletionCallback_FolderUploadBlockedThenAllowed);
+  FRIEND_TEST_ALL_PREFIXES(
+      FileSelectHelperTest,
       ContentAnalysisCompletionCallback_FolderUpload_OKBad);
   FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, GetFileTypesFromAcceptType);
   FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, MultipleFileExtensionsForMime);
diff --git a/chrome/browser/file_select_helper_unittest.cc b/chrome/browser/file_select_helper_unittest.cc
index 9acb607f..c53fc05 100644
--- a/chrome/browser/file_select_helper_unittest.cc
+++ b/chrome/browser/file_select_helper_unittest.cc
@@ -481,9 +481,16 @@
 
   EXPECT_FALSE(file_select_helper->IsDirectoryEnumerationStartedForTesting());
 
+  // Calling the content analysis completion callback would normally
+  // release `file_select_helper`, so we add a reference and validate that
+  // it goes down to 1 after the call.
+  file_select_helper->AddRef();
+  EXPECT_FALSE(file_select_helper->HasOneRef());
+
   file_select_helper->FolderUploadContentAnalysisCompletionCallback(
       data_dir_, data, result);
 
+  EXPECT_TRUE(file_select_helper->HasOneRef());
   EXPECT_FALSE(file_select_helper->IsDirectoryEnumerationStartedForTesting());
 }
 
@@ -508,12 +515,67 @@
 
   EXPECT_FALSE(file_select_helper->IsDirectoryEnumerationStartedForTesting());
 
+  // Calling the content analysis completion callback would normally
+  // release `file_select_helper`, so we add a reference and validate that
+  // it goes down to 1 after the call.
+  file_select_helper->AddRef();
+  EXPECT_FALSE(file_select_helper->HasOneRef());
+
+  EXPECT_FALSE(file_select_helper->HasOneRef());
   file_select_helper->FolderUploadContentAnalysisCompletionCallback(
       data_dir_, data, result);
 
+  EXPECT_TRUE(file_select_helper->HasOneRef());
   EXPECT_FALSE(file_select_helper->IsDirectoryEnumerationStartedForTesting());
 }
 
+TEST_F(FileSelectHelperTest,
+       ContentAnalysisCompletionCallback_FolderUploadBlockedThenAllowed) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  scoped_refptr<FileSelectHelper> file_select_helper =
+      new FileSelectHelper(&profile);
+
+  std::vector<blink::mojom::FileChooserFileInfoPtr> files;
+  auto listener = base::MakeRefCounted<TestFileSelectListener>(&files);
+  file_select_helper->SetFileSelectListenerForTesting(std::move(listener));
+  file_select_helper->DontAbortOnMissingWebContentsForTesting();
+
+  enterprise_connectors::ContentAnalysisDelegate::Data data;
+  enterprise_connectors::ContentAnalysisDelegate::Result result;
+
+  // bar.doc has a blocking verdict, which blocks the entire folder.
+  PrepareContentAnalysisCompletionCallbackArgs(
+      {data_dir_.AppendASCII("foo.doc"), data_dir_.AppendASCII("bar.doc")},
+      {true, false}, nullptr, &data, &result);
+
+  EXPECT_FALSE(file_select_helper->IsDirectoryEnumerationStartedForTesting());
+
+  // Calling the content analysis completion callback would normally
+  // release `file_select_helper`, so we add a reference and validate that
+  // it goes down to 1 after the call.
+  file_select_helper->AddRef();
+  EXPECT_FALSE(file_select_helper->HasOneRef());
+
+  file_select_helper->FolderUploadContentAnalysisCompletionCallback(
+      data_dir_, data, result);
+
+  EXPECT_TRUE(file_select_helper->HasOneRef());
+  EXPECT_FALSE(file_select_helper->IsDirectoryEnumerationStartedForTesting());
+
+  // bar.doc now has a safe verdict, so the entire folder is allowed.
+  PrepareContentAnalysisCompletionCallbackArgs(
+      {data_dir_.AppendASCII("foo.doc"), data_dir_.AppendASCII("bar.doc")},
+      {true, true}, nullptr, &data, &result);
+
+  EXPECT_FALSE(file_select_helper->IsDirectoryEnumerationStartedForTesting());
+
+  file_select_helper->FolderUploadContentAnalysisCompletionCallback(
+      data_dir_, data, result);
+
+  EXPECT_TRUE(file_select_helper->IsDirectoryEnumerationStartedForTesting());
+}
+
 TEST_F(FileSelectHelperTest, GetFileTypesFromAcceptType) {
   content::BrowserTaskEnvironment task_environment;
   TestingProfile profile;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index b884def..faf05d0 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3954,7 +3954,7 @@
   },
   {
     "name": "global-media-controls-cast-start-stop",
-    "owners": [  "takumif", "muyaoxu@google.com", "openscreen-eng@google.com" ],
+    "owners": [ "takumif", "muyaoxu@google.com", "openscreen-eng@google.com" ],
     "expiry_milestone": 130
   },
   {
@@ -4620,6 +4620,11 @@
     "expiry_milestone": 115
   },
   {
+    "name": "media-remoting-without-fullscreen",
+    "owners": [ "muyaoxu@google.com", "openscreen-eng@google.com" ],
+    "expiry_milestone": 120
+  },
+  {
     "name": "media-router-cast-allow-all-ips",
     "owners": [ "mfoltz" ],
     // This flag is used by users with unusual network configurations to allow
@@ -6513,11 +6518,6 @@
     "expiry_milestone": 110
   },
   {
-    "name": "sync-access-handle-all-sync-surface",
-    "owners": [ "dslee@google.com", "chrome-owp-storage@google.com" ],
-    "expiry_milestone": 111
-  },
-  {
     "name": "sync-autofill-wallet-usage-data",
     "owners": [ "alexandertekle@google.com", "treib" ],
     "expiry_milestone": 120
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 308718d..fe0648a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1539,9 +1539,14 @@
 const char kGlobalMediaControlsForCastDescription[] =
     "Shows Cast sessions in the Global Media Controls UI.";
 
+const char kMediaRemotingWithoutFullscreenName[] =
+    "Media Remoting without videos in fullscreen mode";
+const char kMediaRemotingWithoutFullscreenDescription[] =
+    "Starts Media Remoting from Global Media Controls without making the "
+    "videos fullscreen.";
+
 const char kGlobalMediaControlsModernUIName[] =
     "Global Media Controls Modern UI";
-
 const char kGlobalMediaControlsModernUIDescription[] =
     "Use a redesigned version of the Global Media Controls UI. Requires "
     "#global-media-controls to also be enabled.";
@@ -2829,11 +2834,6 @@
 const char kSuppressToolbarCapturesDescription[] =
     "Suppress Toolbar Captures except when certain properties change.";
 
-const char kSyncAccessHandleAllSyncSurfaceName[] =
-    "Sync Access Handle All Sync Surface";
-const char kSyncAccessHandleAllSyncSurfaceDescription[] =
-    "Enables all-sync surface for SyncAccessHandle in File System Access API.";
-
 const char kSyncAutofillWalletUsageDataName[] =
     "Sync Autofill Wallet Usage Data";
 const char kSyncAutofillWalletUsageDataDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 136daa5..cadae42 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -858,6 +858,9 @@
 extern const char kGlobalMediaControlsForCastName[];
 extern const char kGlobalMediaControlsForCastDescription[];
 
+extern const char kMediaRemotingWithoutFullscreenName[];
+extern const char kMediaRemotingWithoutFullscreenDescription[];
+
 extern const char kGlobalMediaControlsModernUIName[];
 extern const char kGlobalMediaControlsModernUIDescription[];
 
@@ -1602,9 +1605,6 @@
 extern const char kSuggestionsWithSubStringMatchName[];
 extern const char kSuggestionsWithSubStringMatchDescription[];
 
-extern const char kSyncAccessHandleAllSyncSurfaceName[];
-extern const char kSyncAccessHandleAllSyncSurfaceDescription[];
-
 extern const char kSyncAutofillWalletUsageDataName[];
 extern const char kSyncAutofillWalletUsageDataDescription[];
 
diff --git a/chrome/browser/media/router/mojo/media_sink_service_status.cc b/chrome/browser/media/router/mojo/media_sink_service_status.cc
index 3052b7e..a072640 100644
--- a/chrome/browser/media/router/mojo/media_sink_service_status.cc
+++ b/chrome/browser/media/router/mojo/media_sink_service_status.cc
@@ -59,10 +59,6 @@
   const MediaSink& sink = sink_internal.sink();
   dict.Set("id", base::Value(TruncateSinkId(sink.id())));
   dict.Set("name", base::Value(sink.name()));
-  if (sink.description())
-    dict.Set("description", base::Value(*sink.description()));
-  if (sink.domain())
-    dict.Set("domain", base::Value(*sink.domain()));
   dict.Set("icon_type", base::Value(static_cast<int>(sink.icon_type())));
 
   if (sink_internal.is_dial_sink()) {
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc b/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc
index 93f1eca..39490ec 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc
@@ -27,6 +27,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_type.h"
 #endif
 
 namespace webrtc_event_logging {
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_data_store.cc b/chrome/browser/metrics/tab_stats/tab_stats_data_store.cc
index cb57fea..03463e11 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_data_store.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_data_store.cc
@@ -59,12 +59,18 @@
   tab_stats_.tab_discard_counts[static_cast<size_t>(
       LifecycleUnitDiscardReason::URGENT)] =
       pref_service->GetInteger(::prefs::kTabStatsDiscardsUrgent);
+  tab_stats_.tab_discard_counts[static_cast<size_t>(
+      LifecycleUnitDiscardReason::PROACTIVE)] =
+      pref_service->GetInteger(::prefs::kTabStatsDiscardsProactive);
   tab_stats_.tab_reload_counts[static_cast<size_t>(
       LifecycleUnitDiscardReason::EXTERNAL)] =
       pref_service->GetInteger(::prefs::kTabStatsReloadsExternal);
   tab_stats_.tab_reload_counts[static_cast<size_t>(
       LifecycleUnitDiscardReason::URGENT)] =
       pref_service->GetInteger(::prefs::kTabStatsReloadsUrgent);
+  tab_stats_.tab_reload_counts[static_cast<size_t>(
+      LifecycleUnitDiscardReason::PROACTIVE)] =
+      pref_service->GetInteger(::prefs::kTabStatsReloadsProactive);
 }
 
 TabStatsDataStore::~TabStatsDataStore() {}
@@ -218,6 +224,13 @@
       else
         pref_service_->SetInteger(::prefs::kTabStatsReloadsUrgent, count);
       break;
+    case LifecycleUnitDiscardReason::PROACTIVE:
+      if (is_discarded) {
+        pref_service_->SetInteger(::prefs::kTabStatsDiscardsProactive, count);
+      } else {
+        pref_service_->SetInteger(::prefs::kTabStatsReloadsProactive, count);
+      }
+      break;
   }
 }
 
@@ -226,8 +239,10 @@
   tab_stats_.tab_reload_counts.fill(0U);
   pref_service_->SetInteger(::prefs::kTabStatsDiscardsExternal, 0);
   pref_service_->SetInteger(::prefs::kTabStatsDiscardsUrgent, 0);
+  pref_service_->SetInteger(::prefs::kTabStatsDiscardsProactive, 0);
   pref_service_->SetInteger(::prefs::kTabStatsReloadsExternal, 0);
   pref_service_->SetInteger(::prefs::kTabStatsReloadsUrgent, 0);
+  pref_service_->SetInteger(::prefs::kTabStatsReloadsProactive, 0);
 }
 
 absl::optional<TabStatsDataStore::TabID> TabStatsDataStore::GetTabIDForTesting(
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_data_store_unittest.cc b/chrome/browser/metrics/tab_stats/tab_stats_data_store_unittest.cc
index 265851b..5701bb4 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_data_store_unittest.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_data_store_unittest.cc
@@ -75,8 +75,10 @@
   // and reload counts from the pref service.
   constexpr size_t kExpectedDiscardsExternal = 3;
   constexpr size_t kExpectedDiscardsUrgent = 5;
+  constexpr size_t kExpectedDiscardsProactive = 6;
   constexpr size_t kExpectedReloadsExternal = 8;
   constexpr size_t kExpectedReloadsUrgent = 13;
+  constexpr size_t kExpectedReloadsProactive = 4;
   for (size_t i = 0; i < kExpectedDiscardsExternal; ++i) {
     data_store_->OnTabDiscardStateChange(LifecycleUnitDiscardReason::EXTERNAL,
                                          /*is_discarded=*/true);
@@ -85,6 +87,10 @@
     data_store_->OnTabDiscardStateChange(LifecycleUnitDiscardReason::URGENT,
                                          /*is_discarded=*/true);
   }
+  for (size_t i = 0; i < kExpectedDiscardsProactive; ++i) {
+    data_store_->OnTabDiscardStateChange(LifecycleUnitDiscardReason::PROACTIVE,
+                                         /*is_discarded=*/true);
+  }
   for (size_t i = 0; i < kExpectedReloadsExternal; ++i) {
     data_store_->OnTabDiscardStateChange(LifecycleUnitDiscardReason::EXTERNAL,
                                          /*is_discarded=*/false);
@@ -93,15 +99,23 @@
     data_store_->OnTabDiscardStateChange(LifecycleUnitDiscardReason::URGENT,
                                          /*is_discarded=*/false);
   }
+  for (size_t i = 0; i < kExpectedReloadsProactive; ++i) {
+    data_store_->OnTabDiscardStateChange(LifecycleUnitDiscardReason::PROACTIVE,
+                                         /*is_discarded=*/false);
+  }
 
   const size_t external =
       static_cast<size_t>(LifecycleUnitDiscardReason::EXTERNAL);
   const size_t urgent = static_cast<size_t>(LifecycleUnitDiscardReason::URGENT);
+  const size_t proactive =
+      static_cast<size_t>(LifecycleUnitDiscardReason::PROACTIVE);
   TabsStats stats = data_store_->tab_stats();
   EXPECT_EQ(kExpectedDiscardsExternal, stats.tab_discard_counts[external]);
   EXPECT_EQ(kExpectedDiscardsUrgent, stats.tab_discard_counts[urgent]);
+  EXPECT_EQ(kExpectedDiscardsProactive, stats.tab_discard_counts[proactive]);
   EXPECT_EQ(kExpectedReloadsExternal, stats.tab_reload_counts[external]);
   EXPECT_EQ(kExpectedReloadsUrgent, stats.tab_reload_counts[urgent]);
+  EXPECT_EQ(kExpectedReloadsProactive, stats.tab_reload_counts[proactive]);
 
   // Resets the |data_store_| and checks discard/reload counters are restored.
   data_store_ = std::make_unique<TabStatsDataStore>(&pref_service_);
@@ -109,8 +123,10 @@
   TabsStats stats2 = data_store_->tab_stats();
   EXPECT_EQ(kExpectedDiscardsExternal, stats2.tab_discard_counts[external]);
   EXPECT_EQ(kExpectedDiscardsUrgent, stats2.tab_discard_counts[urgent]);
+  EXPECT_EQ(kExpectedDiscardsProactive, stats2.tab_discard_counts[proactive]);
   EXPECT_EQ(kExpectedReloadsExternal, stats2.tab_reload_counts[external]);
   EXPECT_EQ(kExpectedReloadsUrgent, stats2.tab_reload_counts[urgent]);
+  EXPECT_EQ(kExpectedReloadsProactive, stats2.tab_reload_counts[proactive]);
 }
 
 TEST_F(TabStatsDataStoreTest, TrackTabUsageDuringInterval) {
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
index 6ec7e22f..d733739 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
@@ -131,9 +131,14 @@
 const char TabStatsTracker::UmaStatsReportingDelegate::
     kDailyDiscardsUrgentHistogramName[] = "Discarding.DailyDiscards.Urgent";
 const char TabStatsTracker::UmaStatsReportingDelegate::
+    kDailyDiscardsProactiveHistogramName[] =
+        "Discarding.DailyDiscards.Proactive";
+const char TabStatsTracker::UmaStatsReportingDelegate::
     kDailyReloadsExternalHistogramName[] = "Discarding.DailyReloads.External";
 const char TabStatsTracker::UmaStatsReportingDelegate::
     kDailyReloadsUrgentHistogramName[] = "Discarding.DailyReloads.Urgent";
+const char TabStatsTracker::UmaStatsReportingDelegate::
+    kDailyReloadsProactiveHistogramName[] = "Discarding.DailyReloads.Proactive";
 
 const TabStatsDataStore::TabsStats& TabStatsTracker::tab_stats() const {
   return tab_stats_data_store_->tab_stats();
@@ -251,8 +256,10 @@
   // Preferences for saving discard/reload counts.
   registry->RegisterIntegerPref(::prefs::kTabStatsDiscardsExternal, 0);
   registry->RegisterIntegerPref(::prefs::kTabStatsDiscardsUrgent, 0);
+  registry->RegisterIntegerPref(::prefs::kTabStatsDiscardsProactive, 0);
   registry->RegisterIntegerPref(::prefs::kTabStatsReloadsExternal, 0);
   registry->RegisterIntegerPref(::prefs::kTabStatsReloadsUrgent, 0);
+  registry->RegisterIntegerPref(::prefs::kTabStatsReloadsProactive, 0);
 }
 
 void TabStatsTracker::TabStatsDailyObserver::OnDailyEvent(
@@ -521,14 +528,20 @@
       static_cast<size_t>(LifecycleUnitDiscardReason::EXTERNAL);
   const size_t urgent_index =
       static_cast<size_t>(LifecycleUnitDiscardReason::URGENT);
+  const size_t proactive_index =
+      static_cast<size_t>(LifecycleUnitDiscardReason::PROACTIVE);
   base::UmaHistogramCounts10000(kDailyDiscardsExternalHistogramName,
                                 tab_stats.tab_discard_counts[external_index]);
   base::UmaHistogramCounts10000(kDailyDiscardsUrgentHistogramName,
                                 tab_stats.tab_discard_counts[urgent_index]);
+  base::UmaHistogramCounts10000(kDailyDiscardsProactiveHistogramName,
+                                tab_stats.tab_discard_counts[proactive_index]);
   base::UmaHistogramCounts10000(kDailyReloadsExternalHistogramName,
                                 tab_stats.tab_reload_counts[external_index]);
   base::UmaHistogramCounts10000(kDailyReloadsUrgentHistogramName,
                                 tab_stats.tab_reload_counts[urgent_index]);
+  base::UmaHistogramCounts10000(kDailyReloadsProactiveHistogramName,
+                                tab_stats.tab_reload_counts[proactive_index]);
 }
 
 void TabStatsTracker::UmaStatsReportingDelegate::ReportHeartbeatMetrics(
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker.h b/chrome/browser/metrics/tab_stats/tab_stats_tracker.h
index 0dda526c..f224c8f 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker.h
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker.h
@@ -255,8 +255,10 @@
   // by external/urgent event.
   static const char kDailyDiscardsExternalHistogramName[];
   static const char kDailyDiscardsUrgentHistogramName[];
+  static const char kDailyDiscardsProactiveHistogramName[];
   static const char kDailyReloadsExternalHistogramName[];
   static const char kDailyReloadsUrgentHistogramName[];
+  static const char kDailyReloadsProactiveHistogramName[];
 
   UmaStatsReportingDelegate() = default;
 
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc b/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc
index d586541..935430b 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker_unittest.cc
@@ -426,8 +426,10 @@
 
   constexpr size_t kExpectedDiscardsExternal = 3;
   constexpr size_t kExpectedDiscardsUrgent = 5;
+  constexpr size_t kExpectedDiscardsProactive = 11;
   constexpr size_t kExpectedReloadsExternal = 7;
   constexpr size_t kExpectedReloadsUrgent = 9;
+  constexpr size_t kExpectedReloadsProactive = 10;
   for (size_t i = 0; i < kExpectedDiscardsExternal; ++i) {
     tab_stats_tracker_->DiscardedStateChange(
         this, LifecycleUnitDiscardReason::EXTERNAL, /*is_discarded*/ true);
@@ -436,6 +438,10 @@
     tab_stats_tracker_->DiscardedStateChange(
         this, LifecycleUnitDiscardReason::URGENT, /*is_discarded*/ true);
   }
+  for (size_t i = 0; i < kExpectedDiscardsProactive; ++i) {
+    tab_stats_tracker_->DiscardedStateChange(
+        this, LifecycleUnitDiscardReason::PROACTIVE, /*is_discarded*/ true);
+  }
   for (size_t i = 0; i < kExpectedReloadsExternal; ++i) {
     tab_stats_tracker_->DiscardedStateChange(
         this, LifecycleUnitDiscardReason::EXTERNAL, /*is_discarded*/ false);
@@ -444,6 +450,10 @@
     tab_stats_tracker_->DiscardedStateChange(
         this, LifecycleUnitDiscardReason::URGENT, /*is_discarded*/ false);
   }
+  for (size_t i = 0; i < kExpectedReloadsProactive; ++i) {
+    tab_stats_tracker_->DiscardedStateChange(
+        this, LifecycleUnitDiscardReason::PROACTIVE, /*is_discarded*/ false);
+  }
 
   // Triggers the daily event.
   tab_stats_tracker_->TriggerDailyEvent();
@@ -456,17 +466,25 @@
       UmaStatsReportingDelegate::kDailyDiscardsUrgentHistogramName,
       kExpectedDiscardsUrgent, 1);
   histogram_tester_.ExpectUniqueSample(
+      UmaStatsReportingDelegate::kDailyDiscardsProactiveHistogramName,
+      kExpectedDiscardsProactive, 1);
+  histogram_tester_.ExpectUniqueSample(
       UmaStatsReportingDelegate::kDailyReloadsExternalHistogramName,
       kExpectedReloadsExternal, 1);
   histogram_tester_.ExpectUniqueSample(
       UmaStatsReportingDelegate::kDailyReloadsUrgentHistogramName,
       kExpectedReloadsUrgent, 1);
+  histogram_tester_.ExpectUniqueSample(
+      UmaStatsReportingDelegate::kDailyReloadsProactiveHistogramName,
+      kExpectedReloadsProactive, 1);
 
   // Checks that the second report also updates the histograms properly.
   constexpr size_t kExpectedDiscardsExternal2 = 15;
   constexpr size_t kExpectedDiscardsUrgent2 = 25;
+  constexpr size_t kExpectedDiscardsProactive2 = 55;
   constexpr size_t kExpectedReloadsExternal2 = 35;
   constexpr size_t kExpectedReloadsUrgent2 = 45;
+  constexpr size_t kExpectedReloadsProactive2 = 40;
   for (size_t i = 0; i < kExpectedDiscardsExternal2; ++i) {
     tab_stats_tracker_->DiscardedStateChange(
         this, LifecycleUnitDiscardReason::EXTERNAL, /*is_discarded=*/true);
@@ -475,6 +493,10 @@
     tab_stats_tracker_->DiscardedStateChange(
         this, LifecycleUnitDiscardReason::URGENT, /*is_discarded=*/true);
   }
+  for (size_t i = 0; i < kExpectedDiscardsProactive2; ++i) {
+    tab_stats_tracker_->DiscardedStateChange(
+        this, LifecycleUnitDiscardReason::PROACTIVE, /*is_discarded=*/true);
+  }
   for (size_t i = 0; i < kExpectedReloadsExternal2; ++i) {
     tab_stats_tracker_->DiscardedStateChange(
         this, LifecycleUnitDiscardReason::EXTERNAL, /*is_discarded=*/false);
@@ -483,6 +505,10 @@
     tab_stats_tracker_->DiscardedStateChange(
         this, LifecycleUnitDiscardReason::URGENT, /*is_discarded=*/false);
   }
+  for (size_t i = 0; i < kExpectedReloadsProactive2; ++i) {
+    tab_stats_tracker_->DiscardedStateChange(
+        this, LifecycleUnitDiscardReason::PROACTIVE, /*is_discarded=*/false);
+  }
 
   // Triggers the daily event again.
   tab_stats_tracker_->TriggerDailyEvent();
@@ -495,11 +521,17 @@
       UmaStatsReportingDelegate::kDailyDiscardsUrgentHistogramName,
       kExpectedDiscardsUrgent2, 1);
   histogram_tester_.ExpectBucketCount(
+      UmaStatsReportingDelegate::kDailyDiscardsProactiveHistogramName,
+      kExpectedDiscardsProactive2, 1);
+  histogram_tester_.ExpectBucketCount(
       UmaStatsReportingDelegate::kDailyReloadsExternalHistogramName,
       kExpectedReloadsExternal2, 1);
   histogram_tester_.ExpectBucketCount(
       UmaStatsReportingDelegate::kDailyReloadsUrgentHistogramName,
       kExpectedReloadsUrgent2, 1);
+  histogram_tester_.ExpectBucketCount(
+      UmaStatsReportingDelegate::kDailyReloadsProactiveHistogramName,
+      kExpectedReloadsProactive2, 1);
 }
 
 TEST_F(TabStatsTrackerTest, TabUsageGetsReported) {
diff --git a/chrome/browser/nearby_sharing/BUILD.gn b/chrome/browser/nearby_sharing/BUILD.gn
index 5f5838a..097a57b 100644
--- a/chrome/browser/nearby_sharing/BUILD.gn
+++ b/chrome/browser/nearby_sharing/BUILD.gn
@@ -22,7 +22,7 @@
     "//base",
     "//chrome/browser/ui/webui/nearby_share:share_type",
     "//chromeos/ash/services/nearby/public/mojom",
-    "//chromeos/ash/services/nearby/public/mojom:nearby_share_target_types",
+    "//chromeos/ash/services/nearby/public/mojom:nearby_share_settings",
     "//components/drive",
     "//net",
     "//url",
diff --git a/chrome/browser/nearby_sharing/certificates/BUILD.gn b/chrome/browser/nearby_sharing/certificates/BUILD.gn
index 250c2ea..6f8014b 100644
--- a/chrome/browser/nearby_sharing/certificates/BUILD.gn
+++ b/chrome/browser/nearby_sharing/certificates/BUILD.gn
@@ -35,7 +35,7 @@
     "//chrome/browser/nearby_sharing/logging",
     "//chrome/browser/nearby_sharing/proto",
     "//chrome/browser/nearby_sharing/scheduling",
-    "//chrome/browser/ui/webui/nearby_share/public/mojom",
+    "//chromeos/ash/services/nearby/public/mojom",
     "//components/leveldb_proto",
     "//components/prefs",
     "//crypto",
@@ -59,7 +59,7 @@
     ":certificates",
     "//base",
     "//chrome/browser/nearby_sharing/proto",
-    "//chrome/browser/ui/webui/nearby_share/public/mojom",
+    "//chromeos/ash/services/nearby/public/mojom",
     "//components/leveldb_proto",
     "//components/prefs",
     "//crypto",
@@ -89,7 +89,7 @@
     "//chrome/browser/nearby_sharing/local_device_data:test_support",
     "//chrome/browser/nearby_sharing/proto",
     "//chrome/browser/nearby_sharing/scheduling:test_support",
-    "//chrome/browser/ui/webui/nearby_share/public/mojom",
+    "//chromeos/ash/services/nearby/public/mojom",
     "//components/leveldb_proto:test_support",
     "//components/prefs:test_support",
     "//crypto",
diff --git a/chrome/browser/nearby_sharing/certificates/common_unittest.cc b/chrome/browser/nearby_sharing/certificates/common_unittest.cc
index 941a00d..603c244 100644
--- a/chrome/browser/nearby_sharing/certificates/common_unittest.cc
+++ b/chrome/browser/nearby_sharing/certificates/common_unittest.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate.h"
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h"
 #include "chrome/browser/nearby_sharing/certificates/test_util.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(NearbyShareCertificatesCommonTest, AuthenticationTokenHash) {
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h
index 0e5aae5..8086c6b0 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h
@@ -15,7 +15,7 @@
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_encrypted_metadata_key.h"
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 // The Nearby Share certificate manager maintains the local device's private
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.cc
index 849e0dd4..48dea70 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.cc
@@ -27,7 +27,7 @@
 #include "chrome/browser/nearby_sharing/proto/certificate_rpc.pb.h"
 #include "chrome/browser/nearby_sharing/proto/encrypted_metadata.pb.h"
 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_factory.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/prefs/pref_service.h"
 #include "device/bluetooth/bluetooth_adapter.h"
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h
index aac79f80..171c788 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h
@@ -22,7 +22,7 @@
 #include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h"
 #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class NearbyShareClient;
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl_unittest.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl_unittest.cc
index ce57fe8..416aaab 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl_unittest.cc
@@ -15,7 +15,7 @@
 #include "chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.h"
 #include "chrome/browser/nearby_sharing/local_device_data/fake_nearby_share_local_device_data_manager.h"
 #include "chrome/browser/nearby_sharing/scheduling/fake_nearby_share_scheduler_factory.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage.h b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage.h
index 0b6f685d..3ddcc1c 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage.h
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage.h
@@ -9,7 +9,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 // Stores local-device private certificates and remote-device public
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl_unittest.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl_unittest.cc
index b478b585..91a4b2a 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl_unittest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_storage_impl.h"
 #include "chrome/browser/nearby_sharing/certificates/test_util.h"
 #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "components/leveldb_proto/testing/fake_db.h"
 #include "components/prefs/pref_registry.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate_unittest.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate_unittest.cc
index b61ea9a..c5490609 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate_unittest.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate_unittest.cc
@@ -7,7 +7,7 @@
 #include "chrome/browser/nearby_sharing/certificates/constants.h"
 #include "chrome/browser/nearby_sharing/certificates/test_util.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
index 82ad825..50b21f6 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
@@ -17,7 +17,7 @@
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_encrypted_metadata_key.h"
 #include "chrome/browser/nearby_sharing/proto/encrypted_metadata.pb.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace crypto {
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate_unittest.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate_unittest.cc
index d05fab6..b3899cd3 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate_unittest.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate_unittest.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h"
 #include "chrome/browser/nearby_sharing/certificates/test_util.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/chrome/browser/nearby_sharing/certificates/test_util.h b/chrome/browser/nearby_sharing/certificates/test_util.h
index 88f3cb6..56d80e72 100644
--- a/chrome/browser/nearby_sharing/certificates/test_util.h
+++ b/chrome/browser/nearby_sharing/certificates/test_util.h
@@ -14,7 +14,7 @@
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h"
 #include "chrome/browser/nearby_sharing/proto/encrypted_metadata.pb.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "crypto/ec_private_key.h"
 #include "crypto/symmetric_key.h"
 
diff --git a/chrome/browser/nearby_sharing/common/BUILD.gn b/chrome/browser/nearby_sharing/common/BUILD.gn
index 495cf48..eaa0e29 100644
--- a/chrome/browser/nearby_sharing/common/BUILD.gn
+++ b/chrome/browser/nearby_sharing/common/BUILD.gn
@@ -21,7 +21,7 @@
   public_deps = [ "//base" ]
 
   deps = [
-    "//chrome/browser/ui/webui/nearby_share/public/mojom",
+    "//chromeos/ash/services/nearby/public/mojom",
     "//components/pref_registry",
     "//components/prefs",
     "//net",
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_enums.h b/chrome/browser/nearby_sharing/common/nearby_share_enums.h
index dce9c1c..c9274f4 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_enums.h
+++ b/chrome/browser/nearby_sharing/common/nearby_share_enums.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_NEARBY_SHARING_COMMON_NEARBY_SHARE_ENUMS_H_
 #define CHROME_BROWSER_NEARBY_SHARING_COMMON_NEARBY_SHARE_ENUMS_H_
 
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 
 // Represents the advertising bluetooth power for Nearby Connections.
 enum class PowerLevel {
diff --git a/chrome/browser/nearby_sharing/contacts/BUILD.gn b/chrome/browser/nearby_sharing/contacts/BUILD.gn
index 6ccc318..9cf4d3b 100644
--- a/chrome/browser/nearby_sharing/contacts/BUILD.gn
+++ b/chrome/browser/nearby_sharing/contacts/BUILD.gn
@@ -26,7 +26,7 @@
     "//chrome/browser/nearby_sharing/logging",
     "//chrome/browser/nearby_sharing/proto",
     "//chrome/browser/nearby_sharing/scheduling",
-    "//chrome/browser/ui/webui/nearby_share/public/mojom",
+    "//chromeos/ash/services/nearby/public/mojom",
     "//components/prefs",
     "//crypto",
   ]
@@ -71,7 +71,7 @@
     "//chrome/browser/nearby_sharing/proto",
     "//chrome/browser/nearby_sharing/scheduling",
     "//chrome/browser/nearby_sharing/scheduling:test_support",
-    "//chrome/browser/ui/webui/nearby_share/public/mojom",
+    "//chromeos/ash/services/nearby/public/mojom",
     "//components/sync_preferences:test_support",
     "//content/test:test_support",
     "//testing/gtest",
diff --git a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h
index 42fd712..e72a556 100644
--- a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h
+++ b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h
@@ -13,7 +13,7 @@
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
diff --git a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc
index 68d00f4..4e227b4 100644
--- a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc
+++ b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.cc
@@ -22,8 +22,8 @@
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler.h"
 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_factory.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom-shared.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-shared.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "components/prefs/pref_service.h"
 #include "crypto/secure_hash.h"
 
diff --git a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl_unittest.cc b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl_unittest.cc
index b9f1d442..460ff040 100644
--- a/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl_unittest.cc
@@ -25,8 +25,8 @@
 #include "chrome/browser/nearby_sharing/scheduling/fake_nearby_share_scheduler.h"
 #include "chrome/browser/nearby_sharing/scheduling/fake_nearby_share_scheduler_factory.h"
 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_factory.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom-test-utils.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-test-utils.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/nearby_sharing/local_device_data/BUILD.gn b/chrome/browser/nearby_sharing/local_device_data/BUILD.gn
index 3cca7acd..2b340f8 100644
--- a/chrome/browser/nearby_sharing/local_device_data/BUILD.gn
+++ b/chrome/browser/nearby_sharing/local_device_data/BUILD.gn
@@ -16,7 +16,7 @@
     "nearby_share_local_device_data_manager_impl.h",
   ]
 
-  public_deps = [ "//chrome/browser/ui/webui/nearby_share/public/mojom" ]
+  public_deps = [ "//chromeos/ash/services/nearby/public/mojom" ]
 
   deps = [
     "//base",
diff --git a/chrome/browser/nearby_sharing/local_device_data/fake_nearby_share_local_device_data_manager.h b/chrome/browser/nearby_sharing/local_device_data/fake_nearby_share_local_device_data_manager.h
index 13f93c2..2a662b31 100644
--- a/chrome/browser/nearby_sharing/local_device_data/fake_nearby_share_local_device_data_manager.h
+++ b/chrome/browser/nearby_sharing/local_device_data/fake_nearby_share_local_device_data_manager.h
@@ -12,7 +12,7 @@
 #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h"
 #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class NearbyShareClientFactory;
diff --git a/chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h b/chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h
index e6ce8f1..8d8d9508 100644
--- a/chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h
+++ b/chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h
@@ -12,7 +12,7 @@
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 // The maximum length in bytes allowed for a device name, as encoded in UTF-8 in
diff --git a/chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h b/chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h
index cbc3c28..84d9802 100644
--- a/chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h
+++ b/chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h
@@ -13,7 +13,7 @@
 #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h"
 #include "chrome/browser/nearby_sharing/proto/device_rpc.pb.h"
 #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class NearbyShareClientFactory;
diff --git a/chrome/browser/nearby_sharing/nearby_share_settings.h b/chrome/browser/nearby_sharing/nearby_share_settings.h
index bf0b2cf..17b0053 100644
--- a/chrome/browser/nearby_sharing/nearby_share_settings.h
+++ b/chrome/browser/nearby_sharing/nearby_share_settings.h
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
diff --git a/chrome/browser/nearby_sharing/nearby_share_settings_unittest.cc b/chrome/browser/nearby_sharing/nearby_share_settings_unittest.cc
index 4ce8e333..130605b 100644
--- a/chrome/browser/nearby_sharing/nearby_share_settings_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_share_settings_unittest.cc
@@ -14,11 +14,11 @@
 #include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
 #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
 #include "chrome/browser/nearby_sharing/local_device_data/fake_nearby_share_local_device_data_manager.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom-test-utils.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-test-utils.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
index bc572b66..9bc74b9 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
@@ -42,10 +42,10 @@
 #include "chrome/browser/nearby_sharing/share_target.h"
 #include "chrome/browser/nearby_sharing/transfer_metadata.h"
 #include "chrome/browser/nearby_sharing/wifi_network_configuration/wifi_network_configuration_handler.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "chrome/services/sharing/public/proto/wire_format.pb.h"
 #include "chromeos/ash/services/nearby/public/cpp/nearby_process_manager.h"
 #include "chromeos/ash/services/nearby/public/mojom/nearby_decoder_types.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "net/base/network_change_notifier.h"
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
index feaeefd..b6f82de 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
@@ -51,7 +51,6 @@
 #include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/ui/ash/test_session_controller.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "chrome/services/sharing/nearby/decoder/advertisement_decoder.h"
 #include "chrome/services/sharing/public/cpp/advertisement.h"
 #include "chrome/services/sharing/public/proto/wire_format.pb.h"
@@ -62,6 +61,7 @@
 #include "chromeos/ash/services/nearby/public/cpp/mock_nearby_process_manager.h"
 #include "chromeos/ash/services/nearby/public/cpp/mock_nearby_sharing_decoder.h"
 #include "chromeos/ash/services/nearby/public/mojom/nearby_connections_types.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/nearby_sharing/paired_key_verification_runner.h b/chrome/browser/nearby_sharing/paired_key_verification_runner.h
index b0f232c..3ef6d008 100644
--- a/chrome/browser/nearby_sharing/paired_key_verification_runner.h
+++ b/chrome/browser/nearby_sharing/paired_key_verification_runner.h
@@ -16,8 +16,8 @@
 #include "chrome/browser/nearby_sharing/incoming_frames_reader.h"
 #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h"
 #include "chrome/browser/nearby_sharing/share_target.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "chromeos/ash/services/nearby/public/mojom/nearby_decoder_types.mojom.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PairedKeyVerificationRunner {
diff --git a/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc b/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
index d7e9db6c..e84b6d47 100644
--- a/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
+++ b/chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.cc
@@ -118,6 +118,14 @@
     data_decoder = std::make_unique<data_decoder::DataDecoder>();
   }
 #endif
+
+  bool is_default_chrome_app_migrated;
+#if BUILDFLAG(IS_ANDROID)
+  is_default_chrome_app_migrated = false;
+#else
+  is_default_chrome_app_migrated = true;
+#endif
+
   auto most_visited_sites = std::make_unique<ntp_tiles::MostVisitedSites>(
       profile->GetPrefs(), TopSitesFactory::GetForProfile(profile),
 #if BUILDFLAG(IS_ANDROID)
@@ -144,11 +152,6 @@
 #else
       nullptr,
 #endif
-#if !BUILDFLAG(IS_ANDROID)
-      web_app::IsAnyChromeAppToWebAppMigrationEnabled(CHECK_DEREF(profile))
-#else
-      false
-#endif
-  );
+      is_default_chrome_app_migrated);
   return most_visited_sites;
 }
diff --git a/chrome/browser/performance_manager/mechanisms/page_discarder.cc b/chrome/browser/performance_manager/mechanisms/page_discarder.cc
index d015e55..809a05a 100644
--- a/chrome/browser/performance_manager/mechanisms/page_discarder.cc
+++ b/chrome/browser/performance_manager/mechanisms/page_discarder.cc
@@ -7,7 +7,6 @@
 #include "base/functional/bind.h"
 #include "base/task/task_traits.h"
 #include "build/build_config.h"
-#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -31,7 +30,8 @@
 // UrgentlyDiscardMultiplePages can keep reclaiming until the reclaim target is
 // met or there is no discardable page.
 bool DiscardPagesOnUIThread(
-    const std::vector<std::pair<WebContentsProxy, uint64_t>>& proxies_and_pmf) {
+    const std::vector<std::pair<WebContentsProxy, uint64_t>>& proxies_and_pmf,
+    resource_coordinator::LifecycleUnitDiscardReason discard_reason) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   if (disabled_for_testing)
@@ -49,7 +49,7 @@
       continue;
 
     if (lifecycle_unit->DiscardTab(
-            resource_coordinator::LifecycleUnitDiscardReason::URGENT,
+            discard_reason,
             /*memory_footprint_estimate=*/proxy.second)) {
       result = true;
     }
@@ -67,6 +67,7 @@
 
 void PageDiscarder::DiscardPageNodes(
     const std::vector<const PageNode*>& page_nodes,
+    resource_coordinator::LifecycleUnitDiscardReason discard_reason,
     base::OnceCallback<void(bool)> post_discard_cb) {
   std::vector<std::pair<WebContentsProxy, uint64_t>> proxies_and_pmf;
   proxies_and_pmf.reserve(page_nodes.size());
@@ -76,7 +77,8 @@
   }
   content::GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
       FROM_HERE,
-      base::BindOnce(&DiscardPagesOnUIThread, std::move(proxies_and_pmf)),
+      base::BindOnce(&DiscardPagesOnUIThread, std::move(proxies_and_pmf),
+                     discard_reason),
       std::move(post_discard_cb));
 }
 
diff --git a/chrome/browser/performance_manager/mechanisms/page_discarder.h b/chrome/browser/performance_manager/mechanisms/page_discarder.h
index f64c8c8..99a4044 100644
--- a/chrome/browser/performance_manager/mechanisms/page_discarder.h
+++ b/chrome/browser/performance_manager/mechanisms/page_discarder.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/functional/callback.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
 
 namespace performance_manager {
 
@@ -28,8 +29,10 @@
 
   // Discards |page_nodes| and runs |post_discard_cb| on the origin sequence
   // once this is done.
-  virtual void DiscardPageNodes(const std::vector<const PageNode*>& page_nodes,
-                                base::OnceCallback<void(bool)> post_discard_cb);
+  virtual void DiscardPageNodes(
+      const std::vector<const PageNode*>& page_nodes,
+      ::mojom::LifecycleUnitDiscardReason discard_reason,
+      base::OnceCallback<void(bool)> post_discard_cb);
 };
 
 }  // namespace mechanism
diff --git a/chrome/browser/performance_manager/mechanisms/page_discarder_browsertest.cc b/chrome/browser/performance_manager/mechanisms/page_discarder_browsertest.cc
index b8e37d1c..2371f33 100644
--- a/chrome/browser/performance_manager/mechanisms/page_discarder_browsertest.cc
+++ b/chrome/browser/performance_manager/mechanisms/page_discarder_browsertest.cc
@@ -59,7 +59,7 @@
 
             mechanism::PageDiscarder discarder;
             discarder.DiscardPageNodes(
-                {page_node.get()},
+                {page_node.get()}, ::mojom::LifecycleUnitDiscardReason::URGENT,
                 base::BindOnce(
                     [](base::OnceClosure quit_closure, bool success) {
                       EXPECT_TRUE(success);
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper.cc b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
index 4307c58a..dab9a5b 100644
--- a/chrome/browser/performance_manager/policies/page_discarding_helper.cc
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
@@ -16,6 +16,7 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/performance_manager/mechanisms/page_discarder.h"
 #include "chrome/browser/performance_manager/policies/policy_features.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
 #include "components/performance_manager/graph/node_attached_data_impl.h"
 #include "components/performance_manager/graph/page_node_impl.h"
 #include "components/performance_manager/public/graph/frame_node.h"
@@ -197,7 +198,7 @@
   LOG(WARNING) << "Discarding " << discard_attempts.size() << " pages";
 
   page_discarder_->DiscardPageNodes(
-      discard_attempts,
+      discard_attempts, ::mojom::LifecycleUnitDiscardReason::URGENT,
       base::BindOnce(&PageDiscardingHelper::PostDiscardAttemptCallback,
                      weak_factory_.GetWeakPtr(), reclaim_target_kb,
                      discard_protected_tabs, std::move(split_callback.second)));
@@ -208,7 +209,9 @@
   if (CanUrgentlyDiscard(page_node,
                          /* consider_minimum_protection_time */ false) ==
       CanUrgentlyDiscardResult::kEligible) {
-    page_discarder_->DiscardPageNodes({page_node}, base::DoNothing());
+    page_discarder_->DiscardPageNodes(
+        {page_node}, ::mojom::LifecycleUnitDiscardReason::PROACTIVE,
+        base::DoNothing());
   }
 }
 
diff --git a/chrome/browser/performance_manager/test_support/page_discarding_utils.cc b/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
index 91f51d0e..5420bee8 100644
--- a/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
+++ b/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
@@ -24,6 +24,7 @@
 
 void LenientMockPageDiscarder::DiscardPageNodes(
     const std::vector<const PageNode*>& page_nodes,
+    ::mojom::LifecycleUnitDiscardReason discard_reason,
     base::OnceCallback<void(bool)> post_discard_cb) {
   bool result = false;
   for (auto* node : page_nodes) {
diff --git a/chrome/browser/performance_manager/test_support/page_discarding_utils.h b/chrome/browser/performance_manager/test_support/page_discarding_utils.h
index 7cf412f2..b3ddbd7 100644
--- a/chrome/browser/performance_manager/test_support/page_discarding_utils.h
+++ b/chrome/browser/performance_manager/test_support/page_discarding_utils.h
@@ -35,6 +35,7 @@
  private:
   void DiscardPageNodes(
       const std::vector<const PageNode*>& page_nodes,
+      ::mojom::LifecycleUnitDiscardReason discard_reason,
       base::OnceCallback<void(bool)> post_discard_cb) override;
 };
 using MockPageDiscarder = ::testing::StrictMock<LenientMockPageDiscarder>;
diff --git a/chrome/browser/policy/chrome_browser_cloud_management_controller_desktop.cc b/chrome/browser/policy/chrome_browser_cloud_management_controller_desktop.cc
index 3f03198c..3d11fb9 100644
--- a/chrome/browser/policy/chrome_browser_cloud_management_controller_desktop.cc
+++ b/chrome/browser/policy/chrome_browser_cloud_management_controller_desktop.cc
@@ -250,7 +250,7 @@
     auto key_rotation_launcher =
         enterprise_connectors::KeyRotationLauncher::Create(
             BrowserDMTokenStorage::Get(), GetDeviceManagementService(),
-            GetSharedURLLoaderFactory(), g_browser_process->local_state());
+            GetSharedURLLoaderFactory());
     return std::make_unique<enterprise_connectors::DeviceTrustKeyManagerImpl>(
         std::move(key_rotation_launcher));
   }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index adfd3f5..836ace1 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -839,9 +839,6 @@
   { key::kPaymentMethodQueryEnabled,
     payments::kCanMakePaymentEnabled,
     base::Value::Type::BOOLEAN },
-  { key::kFileSystemSyncAccessHandleAsyncInterfaceEnabled,
-    storage::kFileSystemSyncAccessHandleAsyncInterfaceEnabled,
-    base::Value::Type::BOOLEAN },
 
 #if !BUILDFLAG(IS_CHROMEOS)
   { key::kMetricsReportingEnabled,
@@ -1673,6 +1670,9 @@
   { key::kNewWindowsInKioskAllowed,
     prefs::kNewWindowsInKioskAllowed,
     base::Value::Type::BOOLEAN },
+  { key::kKioskTroubleshootingToolsEnabled,
+    prefs::kKioskTroubleshootingToolsEnabled,
+    base::Value::Type::BOOLEAN },
 #endif // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 3455173b..576bf34 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -815,6 +815,14 @@
 // Deprecated 01/2023
 const char kSendDownloadToCloudPref[] =
     "enterprise_connectors.send_download_to_cloud";
+#if BUILDFLAG(IS_MAC)
+const char kDeviceTrustDisableKeyCreationPref[] =
+    "enterprise_connectors.device_trust.disable_key_creation";
+#endif  // BUILDFLAG(IS_MAC)
+
+// Deprecated 01/2023.
+const char kFileSystemSyncAccessHandleAsyncInterfaceEnabled[] =
+    "policy.file_system_sync_access_handle_async_interface_enabled";
 
 // Register local state used only for migration (clearing or moving to a new
 // key).
@@ -887,6 +895,9 @@
 
   // Deprecated 01/2023
   registry->RegisterListPref(kSendDownloadToCloudPref);
+#if BUILDFLAG(IS_MAC)
+  registry->RegisterBooleanPref(kDeviceTrustDisableKeyCreationPref, false);
+#endif  // BUILDFLAG(IS_MAC)
 }
 
 // Register prefs used only for migration (clearing or moving to a new key).
@@ -1089,6 +1100,10 @@
   // Deprecated 12/2022.
   registry->RegisterBooleanPref(kAutofillWalletImportStorageCheckboxState,
                                 true);
+
+  // Deprecated 01/2023.
+  registry->RegisterBooleanPref(
+      kFileSystemSyncAccessHandleAsyncInterfaceEnabled, false);
 }
 
 }  // namespace
@@ -1284,7 +1299,6 @@
 #if BUILDFLAG(IS_MAC)
   confirm_quit::RegisterLocalState(registry);
   QuitWithAppsController::RegisterPrefs(registry);
-  enterprise_connectors::RegisterLocalPrefs(registry);
   system_media_permissions::RegisterSystemMediaPermissionStatesPrefs(registry);
   AppShimRegistry::Get()->RegisterLocalPrefs(registry);
 #endif
@@ -1839,6 +1853,9 @@
 
   // Added 01/2023
   local_state->ClearPref(kSendDownloadToCloudPref);
+#if BUILDFLAG(IS_MAC)
+  local_state->ClearPref(kDeviceTrustDisableKeyCreationPref);
+#endif  // BUILDFLAG(IS_MAC)
 
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS
@@ -2137,6 +2154,9 @@
   // Added 12/2022.
   profile_prefs->ClearPref(kAutofillWalletImportStorageCheckboxState);
 
+  // Added 01/2023.
+  profile_prefs->ClearPref(kFileSystemSyncAccessHandleAsyncInterfaceEnabled);
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
index e2eb0bd..6f750d0a 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
@@ -523,8 +523,17 @@
 }
 
 void PrivacySandboxService::RecordPrivacySandbox4StartupMetrics() {
-  // TODO(crbug.com/1378703): Add metrics for inidividial PS APIs being enabled
-  // or not in the beginning here.
+  // Record the status of the APIs.
+  const bool topics_enabled =
+      pref_service_->GetBoolean(prefs::kPrivacySandboxM1TopicsEnabled);
+  base::UmaHistogramBoolean("Settings.PrivacySandbox.Topics.Enabled",
+                            topics_enabled);
+  base::UmaHistogramBoolean(
+      "Settings.PrivacySandbox.Fledge.Enabled",
+      pref_service_->GetBoolean(prefs::kPrivacySandboxM1FledgeEnabled));
+  base::UmaHistogramBoolean(
+      "Settings.PrivacySandbox.AdMeasurement.Enabled",
+      pref_service_->GetBoolean(prefs::kPrivacySandboxM1AdMeasurementEnabled));
 
   const std::string privacy_sandbox_prompt_startup_histogram =
       "Settings.PrivacySandbox.PromptStartupState";
@@ -591,8 +600,6 @@
     }
 
     // Consent decision made at this point.
-    const bool topics_enabled =
-        pref_service_->GetBoolean(prefs::kPrivacySandboxM1TopicsEnabled);
 
     // Notice Acknowledged
     const bool notice_acknowledged = pref_service_->GetBoolean(
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service.h b/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
index 9893fe5..8bb690d 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
@@ -365,6 +365,8 @@
   FRIEND_TEST_ALL_PREFIXES(
       PrivacySandboxServiceM1Test,
       RecordPrivacySandbox4StartupMetrics_PromptNotSuppressed_ROW);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceM1Test,
+                           RecordPrivacySandbox4StartupMetrics_APIs);
 
   // Should be used only for tests when mocking the service.
   PrivacySandboxService();
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc
index dae9756..0f21e92 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc
@@ -3441,6 +3441,59 @@
       /*expected_count=*/1);
 }
 
+TEST_F(PrivacySandboxServiceM1Test, RecordPrivacySandbox4StartupMetrics_APIs) {
+  // Each test for the APIs are scoped below to ensure we start with a clean
+  // HistogramTester as each call to `RecordPrivacySandbox4StartupMetrics` emits
+  // histograms for all APIs.
+
+  // Topics
+  {
+    base::HistogramTester histogram_tester;
+    prefs()->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, true);
+    privacy_sandbox_service()->RecordPrivacySandbox4StartupMetrics();
+    histogram_tester.ExpectBucketCount("Settings.PrivacySandbox.Topics.Enabled",
+                                       static_cast<int>(true),
+                                       /*expected_count=*/1);
+
+    prefs()->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, false);
+    privacy_sandbox_service()->RecordPrivacySandbox4StartupMetrics();
+    histogram_tester.ExpectBucketCount("Settings.PrivacySandbox.Topics.Enabled",
+                                       static_cast<int>(false),
+                                       /*expected_count=*/1);
+  }
+
+  // Fledge
+  {
+    base::HistogramTester histogram_tester;
+    prefs()->SetBoolean(prefs::kPrivacySandboxM1FledgeEnabled, true);
+    privacy_sandbox_service()->RecordPrivacySandbox4StartupMetrics();
+    histogram_tester.ExpectBucketCount("Settings.PrivacySandbox.Fledge.Enabled",
+                                       static_cast<int>(true),
+                                       /*expected_count=*/1);
+    prefs()->SetBoolean(prefs::kPrivacySandboxM1FledgeEnabled, false);
+    privacy_sandbox_service()->RecordPrivacySandbox4StartupMetrics();
+    histogram_tester.ExpectBucketCount("Settings.PrivacySandbox.Fledge.Enabled",
+                                       static_cast<int>(false),
+                                       /*expected_count=*/1);
+  }
+
+  // Ad measurement
+  {
+    base::HistogramTester histogram_tester;
+    prefs()->SetBoolean(prefs::kPrivacySandboxM1AdMeasurementEnabled, true);
+    privacy_sandbox_service()->RecordPrivacySandbox4StartupMetrics();
+    histogram_tester.ExpectBucketCount(
+        "Settings.PrivacySandbox.AdMeasurement.Enabled", static_cast<int>(true),
+        /*expected_count=*/1);
+    prefs()->SetBoolean(prefs::kPrivacySandboxM1AdMeasurementEnabled, false);
+    privacy_sandbox_service()->RecordPrivacySandbox4StartupMetrics();
+    histogram_tester.ExpectBucketCount(
+        "Settings.PrivacySandbox.AdMeasurement.Enabled",
+        static_cast<int>(false),
+        /*expected_count=*/1);
+  }
+}
+
 class PrivacySandboxServiceM1PromptTest : public PrivacySandboxServiceM1Test {
  public:
   void InitializeFeaturesBeforeStart() override {
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index f90b9af..03a8762 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -135,6 +135,7 @@
 #include "chrome/browser/ash/arc/policy/arc_policy_util.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process_platform_part_ash.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_type.h"
@@ -454,10 +455,12 @@
   // since it may refer to profile that has been in use in previous session.
   // That profile dir may not be mounted in this session so instead return
   // active profile from current session.
-  base::FilePath profile_dir =
-      ash::ProfileHelper::Get()->GetActiveUserProfileDir();
+  user_manager::UserManager* manager = user_manager::UserManager::Get();
+  // IsLoggedIn check above ensures |user| is non-null.
+  const auto* user = manager->GetActiveUser();
   Profile* profile = profile_manager->GetProfileByPath(
-      profile_manager->user_data_dir().Append(profile_dir));
+      ash::BrowserContextHelper::Get()->GetBrowserContextPathByUserIdHash(
+          user->username_hash()));
 #else
   // TODO(crbug.com/1363933): Once Lacros is launched pre-login, we should
   // probably do something analogous to the !IsLoggedIn() check above.
@@ -737,8 +740,14 @@
 
 base::FilePath ProfileManager::GetInitialProfileDir() {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (IsLoggedIn())
-    return ash::ProfileHelper::Get()->GetActiveUserProfileDir();
+  if (IsLoggedIn()) {
+    user_manager::UserManager* manager = user_manager::UserManager::Get();
+    // IsLoggedIn check above ensures |user| is non-null.
+    const auto* user = manager->GetActiveUser();
+    return base::FilePath(
+        ash::BrowserContextHelper::GetUserBrowserContextDirName(
+            user->username_hash()));
+  }
 #endif
   base::FilePath relative_profile_dir;
   // TODO(mirandac): should not automatically be default profile.
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index 2465bfaf..4f74aab 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -170,6 +170,10 @@
               extensions::GetCurrentFeatureSessionType());
     session_type_ = extensions::ScopedCurrentFeatureSessionType(
         extensions::GetCurrentFeatureSessionType());
+
+    // Initializes ProfileHelper.
+    // TODO(crbug.com/1325210): Migrate into BrowserContextHelper.
+    ash::ProfileHelper::Get();
 #endif
   }
 
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit_state.mojom b/chrome/browser/resource_coordinator/lifecycle_unit_state.mojom
index 31793ef..5f486bf 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit_state.mojom
+++ b/chrome/browser/resource_coordinator/lifecycle_unit_state.mojom
@@ -59,4 +59,8 @@
   // The discard is requested urgently by TabManager when the system is in a
   // critical condition.
   URGENT = 1,
+  // The discard is requested proactively by PerformanceManager to free up
+  // memory before the system is under memory pressure, such as when Memory
+  // Saver Mode initiates a discard.
+  PROACTIVE = 2,
 };
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index eee60837..40d27af 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -114,6 +114,8 @@
       return StateChangeReason::EXTENSION_INITIATED;
     case LifecycleUnitDiscardReason::URGENT:
       return StateChangeReason::SYSTEM_MEMORY_PRESSURE;
+    case LifecycleUnitDiscardReason::PROACTIVE:
+      return StateChangeReason::BROWSER_INITIATED;
   }
 }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index 29dce2a..57eb8d04 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -77,9 +77,6 @@
     /** @private {CursorRange} */
     this.pageSel_ = null;
 
-    /** @private {CursorRange} */
-    this.previousRange_ = null;
-
     /** @private {boolean} */
     this.talkBackEnabled_ = false;
 
@@ -117,6 +114,7 @@
     await LocalStorage.init();
     BrailleBackground.init();
     ChromeVoxPrefs.init();
+    ChromeVoxRange.init();
     TtsBackground.init();
     ChromeVoxBackground.init();
 
@@ -185,7 +183,7 @@
    * @override
    */
   setCurrentRange(newRange) {
-    this.previousRange_ = this.currentRange_;
+    ChromeVoxRange.previous = this.currentRange_;
     this.currentRange_ = newRange;
   }
 
@@ -314,7 +312,7 @@
     }
 
     if (!this.currentRange_ || !this.currentRange_.isValid()) {
-      ChromeVoxRange.set(this.previousRange_);
+      ChromeVoxRange.set(ChromeVoxRange.previous);
     }
   }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index 0178b2c..4bbdbce88 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -722,13 +722,11 @@
       };
 
       const rootNode = await this.runWithLoadedTree(this.iframesDoc);
-      chrome.automation.getDesktop(function(desktopNode) {
-        runTestIfIframeIsLoaded(rootNode);
+      runTestIfIframeIsLoaded(rootNode);
 
-        desktopNode.addEventListener('loadComplete', function(evt) {
-          runTestIfIframeIsLoaded(rootNode);
-        }, true);
-      });
+      this.desktop_.addEventListener('loadComplete', function(evt) {
+        runTestIfIframeIsLoaded(rootNode);
+      }, true);
     });
 
 /** Tests navigating into and out of iframes using nextObject */
@@ -774,13 +772,11 @@
       };
 
       const rootNode = await this.runWithLoadedTree(this.iframesDoc);
-      chrome.automation.getDesktop(function(desktopNode) {
-        runTestIfIframeIsLoaded(rootNode);
+      runTestIfIframeIsLoaded(rootNode);
 
-        desktopNode.addEventListener('loadComplete', function(evt) {
-          runTestIfIframeIsLoaded(rootNode);
-        }, true);
-      });
+      this.desktop_.addEventListener('loadComplete', function(evt) {
+        runTestIfIframeIsLoaded(rootNode);
+      }, true);
     });
 
 AX_TEST_F('ChromeVoxBackgroundTest', 'SelectOptionSelected', async function() {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_range.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_range.js
index e4069fc..4abaee1 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_range.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_range.js
@@ -5,6 +5,7 @@
 /**
  * @fileoverview Classes that handle the ChromeVox range.
  */
+import {AsyncUtil} from '../../common/async_util.js';
 import {AutomationUtil} from '../../common/automation_util.js';
 import {CursorRange} from '../../common/cursors/range.js';
 import {BridgeConstants} from '../common/bridge_constants.js';
@@ -40,6 +41,22 @@
  * split between those two locations.
  */
 export class ChromeVoxRange {
+  /** @private */
+  constructor() {
+    /** @private {?CursorRange} */
+    this.previous_ = null;
+  }
+
+  static init() {
+    if (ChromeVoxRange.instance) {
+      throw new Error('Cannot create more than one ChromeVoxRange');
+    }
+    ChromeVoxRange.instance = new ChromeVoxRange();
+
+    BridgeHelper.registerHandler(
+        TARGET, Action.CLEAR_CURRENT_RANGE, () => ChromeVoxRange.set(null));
+  }
+
   /** @param {ChromeVoxRangeObserver} observer */
   static addObserver(observer) {
     ChromeVoxRange.observers_.push(observer);
@@ -59,6 +76,16 @@
     return ChromeVoxState.instance.currentRange;
   }
 
+  /** @return {?CursorRange} */
+  static get previous() {
+    return ChromeVoxRange.instance.previous_;
+  }
+
+  /** @param {?CursorRange} oldRange */
+  static set previous(oldRange) {
+    ChromeVoxRange.instance.previous_ = oldRange;
+  }
+
   /**
    * @param {?CursorRange} newRange The new range.
    * @param {boolean=} opt_fromEditing
@@ -115,10 +142,39 @@
       observer.onCurrentRangeChanged(range, opt_fromEditing);
     }
   }
+
+  /**
+   * Check for loss of focus which results in us invalidating our current
+   * range. Note the getFocus() callback is synchronous, so the focus will be
+   * updated when this function returns (despite being technicallly a separate
+   * function call).
+   */
+  static async maybeResetFromFocus() {
+    const focus = await AsyncUtil.getFocus();
+    const cur = ChromeVoxRange.current;
+    // If the current node is not valid and there's a current focus:
+    if (cur && !cur.isValid() && focus) {
+      ChromeVoxRange.set(CursorRange.fromNode(focus));
+    }
+
+    // If there's no focused node:
+    if (!focus) {
+      ChromeVoxRange.set(null);
+      return;
+    }
+
+    // This case detects when TalkBack (in ARC++) is enabled (which also
+    // covers when the ARC++ window is active). Clear the ChromeVox range
+    // so keys get passed through for ChromeVox commands.
+    if (ChromeVoxState.instance.talkBackEnabled &&
+        // This additional check is not strictly necessary, but we use it to
+        // ensure we are never inadvertently losing focus. ARC++ windows set
+        // "focus" on a root view.
+        focus.role === RoleType.CLIENT) {
+      ChromeVoxRange.set(null);
+    }
+  }
 }
 
 /** @private {!Array<ChromeVoxRangeObserver>} */
 ChromeVoxRange.observers_ = [];
-
-BridgeHelper.registerHandler(
-    TARGET, Action.CLEAR_CURRENT_RANGE, () => ChromeVoxRange.set(null));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
index 55692e5..9a02e1d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -106,9 +106,7 @@
       return true;
     }
 
-    // Check for loss of focus which results in us invalidating our current
-    // range. Note this call is synchronous.
-    chrome.automation.getFocus(focus => this.checkForLossOfFocus_(focus));
+    ChromeVoxRange.maybeResetFromFocus();
 
     // These commands don't require a current range.
     switch (command) {
@@ -1073,33 +1071,6 @@
         .go();
   }
 
-  /**
-   * @param {AutomationNode} focusedNode
-   * @private
-   */
-  checkForLossOfFocus_(focusedNode) {
-    const cur = ChromeVoxRange.current;
-    if (cur && !cur.isValid() && focusedNode) {
-      ChromeVoxRange.set(CursorRange.fromNode(focusedNode));
-    }
-
-    if (!focusedNode) {
-      ChromeVoxRange.set(null);
-      return;
-    }
-
-    // This case detects when TalkBack (in ARC++) is enabled (which also
-    // covers when the ARC++ window is active). Clear the ChromeVox range
-    // so keys get passed through for ChromeVox commands.
-    if (ChromeVoxState.instance.talkBackEnabled &&
-        // This additional check is not strictly necessary, but we use it to
-        // ensure we are never inadvertently losing focus. ARC++ windows set
-        // "focus" on a root view.
-        focusedNode.role === RoleType.CLIENT) {
-      ChromeVoxRange.set(null);
-    }
-  }
-
   /** @private */
   cycleTypingEcho_() {
     LocalStorage.set(
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
index 8f92aa7..eeb3416 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
@@ -16,6 +16,8 @@
 
     // Alphabetical based on file path.
     await importModule(
+        'ChromeVoxState', '/chromevox/background/chromevox_state.js');
+    await importModule(
         'DesktopAutomationHandler',
         '/chromevox/background/desktop_automation_handler.js');
     await importModule(
@@ -27,12 +29,8 @@
     await importModule('EventGenerator', '/common/event_generator.js');
     await importModule('KeyCode', '/common/key_code.js');
 
-    await new Promise(r => {
-      chrome.automation.getDesktop(desktop => {
-        this.handler_ = DesktopAutomationInterface.instance;
-        r();
-      });
-    });
+    await ChromeVoxState.ready();
+    this.handler_ = DesktopAutomationInterface.instance;
 
     globalThis.EventType = chrome.automation.EventType;
     globalThis.RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler_test.js
index 528cbbe..f36d078 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler_test.js
@@ -43,7 +43,7 @@
       // A Search keydown does not get eaten when there's no range and there
       // was no previous range. TalkBack is handled elsewhere.
       ChromeVoxRange.set(null);
-      ChromeVoxState.instance.previousRange_ = null;
+      ChromeVoxRange.previous = null;
       const searchDown2 = {};
       searchDown2.metaKey = true;
       keyboardHandler.onKeyDown(searchDown2);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
index 6a51159..4ad0fcb0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
@@ -857,10 +857,6 @@
   ancestryHelper_(args) {
     let {node, prevNode, buff, formatLog, type, ancestors, formatName} = args;
 
-    const rule = new AncestryOutputRule(type);
-    // First, look up the event type's format block.
-    const eventBlock = OutputRule.RULES[rule.event];
-
     const excludeRoles =
         args.exclude ? new Set(args.exclude.map(node => node.role)) : new Set();
 
@@ -876,11 +872,10 @@
         continue;
       }
 
-      // Reset the rule to DEFAULT so we don't unintentionally use a value from
-      // the last iteration.
-      rule.role = CustomRole.DEFAULT;
-      rule.populateRole(formatNode.role, roleInfo.inherits, formatName);
-      rule.populateNavigation(formatName);
+      const rule = new AncestryOutputRule(
+          type, formatNode.role, roleInfo.inherits, formatName);
+      // First, look up the event type's format block.
+      const eventBlock = OutputRule.RULES[rule.event];
 
       if (eventBlock[rule.role][formatName]) {
         rule.output = eventBlock[rule.role][formatName].speak ?
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_rules.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_rules.js
index 1de7ae1..311e6a4 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_rules.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_rules.js
@@ -24,13 +24,13 @@
 export class OutputRule {
   /** @param {!OutputEventType} event */
   constructor(event) {
-    /** @private {!OutputEventType} */
+    /** @protected {!OutputEventType} */
     this.event_ = this.getEvent_(event);
-    /** @private {!ChromeVoxRole} */
+    /** @protected {!ChromeVoxRole} */
     this.role_ = CustomRole.DEFAULT;
-    /** @private {string|undefined} */
+    /** @protected {string|undefined} */
     this.navigation_;
-    /** @private {string|undefined} */
+    /** @protected {string|undefined} */
     this.output_;
   }
 
@@ -59,7 +59,7 @@
   /**
    * @param {ChromeVoxRole|undefined} role
    * @param {ChromeVoxRole|undefined} parentRole
-   * @param {string} formatName
+   * @param {string|undefined} formatName
    * @return {boolean} true if the role was set, false otherwise.
    */
   populateRole(role, parentRole, formatName) {
@@ -109,6 +109,17 @@
 }
 
 export class AncestryOutputRule extends OutputRule {
+  /**
+   * @param {!OutputEventType} eventType
+   * @param {ChromeVoxRole|undefined} nodeRole
+   * @param {ChromeVoxRole|undefined} parentRole
+   * @param {string|undefined} formatName
+   */
+  constructor(eventType, nodeRole, parentRole, formatName) {
+    super(eventType);
+    this.populateRole(nodeRole, parentRole, formatName);
+    this.populateNavigation(formatName);
+  }
   /** @param {string|undefined} formatName */
   populateNavigation(formatName) {
     if (formatName && OutputRule.RULES[this.event_][this.role_][formatName]) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode_test.js
index 41f05300..b274e47d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/learn_mode_test.js
@@ -34,13 +34,14 @@
         ['BrailleKeyEvent', 'BrailleKeyCommand'],
         '/chromevox/common/braille/braille_key_types.js');
     await importModule('QueueMode', '/chromevox/common/tts_types.js');
+    await importModule('AsyncUtil', '/common/async_util.js');
     await importModule('KeyCode', '/common/key_code.js');
   }
 
   async runOnLearnModePage() {
     return new Promise(async resolve => {
       const mockFeedback = this.createMockFeedback();
-      const desktop = await new Promise(r => chrome.automation.getDesktop(r));
+      const desktop = await AsyncUtil.getDesktop();
       function listener(evt) {
         if (evt.target.docUrl.indexOf('learn_mode/learn_mode.html') === -1 ||
             !evt.target.docLoaded) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js
index d8276d8..8f00226 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_test.js
@@ -27,6 +27,7 @@
         'CommandHandlerInterface',
         '/chromevox/background/command_handler_interface.js');
     await importModule('TtsSettings', '/chromevox/common/tts_types.js');
+    await importModule('AsyncUtil', '/common/async_util.js');
     await importModule('EventGenerator', '/common/event_generator.js');
     await importModule('KeyCode', '/common/key_code.js');
     await importModule('LocalStorage', '/common/local_storage.js');
@@ -35,8 +36,7 @@
   async loadOptionsPage() {
     return new Promise(async resolve => {
       const mockFeedback = this.createMockFeedback();
-      const desktop =
-          await new Promise(resolve => chrome.automation.getDesktop(resolve));
+      const desktop = await AsyncUtil.getDesktop();
       desktop.addEventListener(
           EventType.LOAD_COMPLETE, evt => {
             if (evt.target.docUrl.indexOf('options/options.html') === -1 ||
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/menu_manager.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/menu_manager.js
index 9d0651eb..c8f81f0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/menu_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/menu_manager.js
@@ -9,6 +9,8 @@
 
 import {PanelMenu, PanelSearchMenu} from './panel_menu.js';
 
+const $ = (id) => document.getElementById(id);
+
 export class MenuManager {
   constructor() {
     /**
@@ -30,6 +32,22 @@
     this.searchMenu_ = null;
   }
 
+  /**
+   * Clear any previous menus. The menus are all regenerated each time the
+   * menus are opened.
+   */
+  clearMenus() {
+    while (this.menus_.length) {
+      const menu = this.menus_.pop();
+      $('menu-bar').removeChild(menu.menuBarItemElement);
+      $('menus_background').removeChild(menu.menuContainerElement);
+    }
+    if (this.activeMenu_) {
+      this.lastMenu_ = this.activeMenu_.menuMsg;
+    }
+    this.activeMenu_ = null;
+  }
+
   /** Disables menu items that are prohibited without a signed-in user. */
   denySignedOut() {
     for (const menu of this.menus_) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
index a4da665..c272aaf 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
@@ -31,6 +31,8 @@
 import {PanelMenu, PanelNodeMenu, PanelSearchMenu} from './panel_menu.js';
 import {PanelMode, PanelModeInfo} from './panel_mode.js';
 
+const $ = (id) => document.getElementById(id);
+
 /** Class to manage the panel. */
 export class Panel extends PanelInterface {
   /** @private */
@@ -308,7 +310,7 @@
     const onFocusDo = async () => {
       window.removeEventListener('focus', onFocusDo);
       // Clear any existing menus and clear the callback.
-      this.clearMenus_();
+      this.menuManager_.clearMenus();
       this.pendingCallback_ = null;
 
       const eventSource = await BackgroundBridge.EventSource.get();
@@ -528,30 +530,13 @@
    */
   async onSearch_() {
     this.setMode_(PanelMode.SEARCH);
-    this.clearMenus_();
+    this.menuManager_.clearMenus();
     this.pendingCallback_ = null;
     this.updateFromPrefs_();
     await ISearchUI.init(this.searchInput_);
   }
 
   /**
-   * Clear any previous menus. The menus are all regenerated each time the
-   * menus are opened.
-   * @private
-   */
-  clearMenus_() {
-    while (this.menuManager_.menus.length) {
-      const menu = this.menuManager_.menus.pop();
-      $('menu-bar').removeChild(menu.menuBarItemElement);
-      $('menus_background').removeChild(menu.menuContainerElement);
-    }
-    if (this.menuManager_.activeMenu) {
-      this.menuManager_.lastMenu = this.menuManager_.activeMenu.menuMsg;
-    }
-    this.menuManager_.activeMenu = null;
-  }
-
-  /**
    * Create a new menu with the given name and add it to the menu bar.
    * @param {string} menuMsg The msg id of the new menu to add.
    * @return {!PanelMenu} The menu just created.
@@ -1051,7 +1036,7 @@
     await BackgroundBridge.PanelBackground.setPanelCollapseWatcher;
 
     // Make sure all menus are cleared to avoid bogus output when we re-open.
-    this.clearMenus_();
+    this.menuManager_.clearMenus();
 
     // Make sure we're not in full-screen mode.
     this.setMode_(PanelMode.COLLAPSED);
@@ -1320,15 +1305,6 @@
   longClick: 'force_long_click_on_current_item',
 };
 
-/**
- * Shortcut for document.getElementById.
- * @param {string} id of the element.
- * @return {Element} with the id.
- */
-function $(id) {
-  return document.getElementById(id);
-}
-
 window.addEventListener('load', async () => await Panel.init(), false);
 
 /** @type {Panel} */
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
index 8d13875..6006e71 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {AsyncUtil} from '../common/async_util.js';
 import {EventHandler} from '../common/event_handler.js';
 
 import {SACommands} from './commands.js';
@@ -18,41 +19,40 @@
  * codebase.
  */
 export class SwitchAccess {
-  static initialize() {
+  static async initialize() {
     SwitchAccess.instance = new SwitchAccess();
 
-    chrome.automation.getDesktop(desktop => {
-      chrome.automation.getFocus(focus => {
-        // Focus is available. Finish init without waiting for further events.
-        // Disallow web view nodes, which indicate a root web area is still
-        // loading and pending focus.
-        if (focus && focus.role !== chrome.automation.RoleType.WEB_VIEW) {
-          SwitchAccess.finishInit_(desktop);
-          return;
-        }
+    const desktop = await AsyncUtil.getDesktop();
+    const focus = await AsyncUtil.getFocus();
 
-        // Wait for the focus to be sent. If |focus| was undefined, this is
-        // guaranteed. Otherwise, also set a timed callback to ensure we do
-        // eventually init.
-        let callbackId = 0;
-        const listener = maybeEvent => {
-          if (maybeEvent &&
-              maybeEvent.target.role === chrome.automation.RoleType.WEB_VIEW) {
-            return;
-          }
+    // Focus is available. Finish init without waiting for further events.
+    // Disallow web view nodes, which indicate a root web area is still
+    // loading and pending focus.
+    if (focus && focus.role !== chrome.automation.RoleType.WEB_VIEW) {
+      SwitchAccess.finishInit_(desktop);
+      return;
+    }
 
-          desktop.removeEventListener(
-              chrome.automation.EventType.FOCUS, listener, false);
-          clearTimeout(callbackId);
+    // Wait for the focus to be sent. If |focus| was undefined, this is
+    // guaranteed. Otherwise, also set a timed callback to ensure we do
+    // eventually init.
+    let callbackId = 0;
+    const listener = maybeEvent => {
+      if (maybeEvent &&
+          maybeEvent.target.role === chrome.automation.RoleType.WEB_VIEW) {
+        return;
+      }
 
-          SwitchAccess.finishInit_(desktop);
-        };
+      desktop.removeEventListener(
+          chrome.automation.EventType.FOCUS, listener, false);
+      clearTimeout(callbackId);
 
-        desktop.addEventListener(
-            chrome.automation.EventType.FOCUS, listener, false);
-        callbackId = setTimeout(listener, 5000);
-      });
-    });
+      SwitchAccess.finishInit_(desktop);
+    };
+
+    desktop.addEventListener(
+        chrome.automation.EventType.FOCUS, listener, false);
+    callbackId = setTimeout(listener, 5000);
   }
 
   /** @private */
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_test.js
index 6ac071d..eb2baba6a 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_test.js
@@ -13,28 +13,36 @@
   }
 };
 
-AX_TEST_F('SwitchAccessSwitchAccessTest', 'NoFocusDefersInit', function() {
-  // Build a new SwitchAccess instance with hooks.
-  let initCount = 0;
-  SwitchAccess.finishInit_ = () => initCount++;
+async function waitForAsyncToResolve() {
+  await new Promise(resolve => setTimeout(resolve, 0));
+}
 
-  // A fake desktop.
-  const fakeDesktop = {};
-  fakeDesktop.addEventListener = () => {};
-  fakeDesktop.removeEventListener = () => {};
+AX_TEST_F(
+    'SwitchAccessSwitchAccessTest', 'NoFocusDefersInit', async function() {
+      // Build a new SwitchAccess instance with hooks.
+      let initCount = 0;
+      SwitchAccess.finishInit_ = () => initCount++;
 
-  // Stub out this to be synchronous.
-  chrome.automation.getDesktop = callback => callback(fakeDesktop);
+      // A fake desktop.
+      const fakeDesktop = {};
+      fakeDesktop.addEventListener = () => {};
+      fakeDesktop.removeEventListener = () => {};
 
-  // Stub this out as well so that focus is undefined.
-  chrome.automation.getFocus = callback => callback();
+      // Stub out this to be synchronous.
+      chrome.automation.getDesktop = callback => callback(fakeDesktop);
 
-  // Initialize; we should not have called finishInit_ since there's no focus.
-  SwitchAccess.initialize();
-  assertEquals(0, initCount);
+      // Stub this out as well so that focus is undefined.
+      chrome.automation.getFocus = callback => callback();
 
-  // Restub this to pass a "focused" node.
-  chrome.automation.getFocus = callback => callback({});
-  SwitchAccess.initialize();
-  assertEquals(1, initCount);
-});
+      // Initialize; we should not have called finishInit_ since there's no
+      // focus.
+      SwitchAccess.initialize();
+      await waitForAsyncToResolve();
+      assertEquals(0, initCount);
+
+      // Restub this to pass a "focused" node.
+      chrome.automation.getFocus = callback => callback({});
+      SwitchAccess.initialize();
+      await waitForAsyncToResolve();
+      assertEquals(1, initCount);
+    });
diff --git a/chrome/browser/resources/discards/discards_tab.ts b/chrome/browser/resources/discards/discards_tab.ts
index b562395..13e4a50e 100644
--- a/chrome/browser/resources/discards/discards_tab.ts
+++ b/chrome/browser/resources/discards/discards_tab.ts
@@ -181,6 +181,8 @@
         return 'external';
       case LifecycleUnitDiscardReason.URGENT:
         return 'urgent';
+      case LifecycleUnitDiscardReason.PROACTIVE:
+        return 'proactive';
     }
   }
 
diff --git a/chrome/browser/resources/nearby_share/BUILD.gn b/chrome/browser/resources/nearby_share/BUILD.gn
index eb4ebee..a67648ad 100644
--- a/chrome/browser/resources/nearby_share/BUILD.gn
+++ b/chrome/browser/resources/nearby_share/BUILD.gn
@@ -59,7 +59,7 @@
   resource_path_rewrites = [
     "chrome/browser/ui/webui/nearby_share/nearby_share.mojom-lite.js|mojo/nearby_share.mojom-lite.js",
     "chrome/browser/ui/webui/nearby_share/nearby_share_share_type.mojom-lite.js|mojo/nearby_share_share_type.mojom-lite.js",
-    "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom-lite.js|mojo/nearby_share_settings.mojom-lite.js",
+    "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-lite.js|mojo/nearby_share_settings.mojom-lite.js",
     "chromeos/ash/services/nearby/public/mojom/nearby_share_target_types.mojom-lite.js|mojo/nearby_share_target_types.mojom-lite.js",
   ]
 }
@@ -112,7 +112,7 @@
   resource_path_rewrites = [
     "chrome/browser/ui/webui/nearby_share/nearby_share.mojom-lite.js|mojo/nearby_share.mojom-lite.js",
     "chrome/browser/ui/webui/nearby_share/nearby_share_share_type.mojom-lite.js|mojo/nearby_share_share_type.mojom-lite.js",
-    "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom-lite.js|mojo/nearby_share_settings.mojom-lite.js",
+    "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-lite.js|mojo/nearby_share_settings.mojom-lite.js",
     "chromeos/ash/services/nearby/public/mojom/nearby_share_target_types.mojom-lite.js|mojo/nearby_share_target_types.mojom-lite.js",
   ]
 }
@@ -121,7 +121,6 @@
 preprocess_if_expr("preprocess_mojo") {
   deps = [
     "//chrome/browser/ui/webui/nearby_share:mojom_js",
-    "//chrome/browser/ui/webui/nearby_share/public/mojom:mojom_js__generator",
     "//chromeos/ash/services/nearby/public/mojom:mojom_js",
   ]
   in_folder = root_gen_dir
@@ -130,7 +129,7 @@
   in_files = [
     "chrome/browser/ui/webui/nearby_share/nearby_share.mojom-lite.js",
     "chrome/browser/ui/webui/nearby_share/nearby_share_share_type.mojom-lite.js",
-    "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom-lite.js",
+    "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-lite.js",
     "chromeos/ash/services/nearby/public/mojom/nearby_share_target_types.mojom-lite.js",
   ]
 }
diff --git a/chrome/browser/resources/nearby_share/shared/BUILD.gn b/chrome/browser/resources/nearby_share/shared/BUILD.gn
index e69705d..c621da6d 100644
--- a/chrome/browser/resources/nearby_share/shared/BUILD.gn
+++ b/chrome/browser/resources/nearby_share/shared/BUILD.gn
@@ -115,7 +115,7 @@
 js_library("nearby_contact_manager") {
   deps = [
     "//ash/webui/common/resources:cr.m",
-    "//chrome/browser/ui/webui/nearby_share/public/mojom:mojom_js_library_for_compile",
+    "//chromeos/ash/services/nearby/public/mojom:nearby_share_settings_js_library_for_compile",
   ]
 }
 
@@ -136,7 +136,7 @@
   deps = [
     "//chrome/browser/ui/webui/nearby_share:mojom_js_library_for_compile",
     "//chrome/browser/ui/webui/nearby_share:share_type_js_library_for_compile",
-    "//chromeos/ash/services/nearby/public/mojom:nearby_share_target_types_js_library_for_compile",
+    "//chromeos/ash/services/nearby/public/mojom:nearby_share_settings_js_library_for_compile",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
@@ -178,7 +178,7 @@
     "//ash/webui/common/resources:i18n_behavior",
     "//chrome/browser/ui/webui/nearby_share:mojom_js_library_for_compile",
     "//chrome/browser/ui/webui/nearby_share:share_type_js_library_for_compile",
-    "//chromeos/ash/services/nearby/public/mojom:nearby_share_target_types_js_library_for_compile",
+    "//chromeos/ash/services/nearby/public/mojom:nearby_share_settings_js_library_for_compile",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
@@ -200,7 +200,7 @@
 js_library("nearby_share_settings") {
   deps = [
     "//ash/webui/common/resources:cr.m",
-    "//chrome/browser/ui/webui/nearby_share/public/mojom:mojom_js_library_for_compile",
+    "//chromeos/ash/services/nearby/public/mojom:nearby_share_settings_js_library_for_compile",
     "//url/mojom:url_mojom_gurl_js_library_for_compile",
   ]
 }
diff --git a/chrome/browser/resources/settings/privacy_page/cookies_page.html b/chrome/browser/resources/settings/privacy_page/cookies_page.html
index de2743e..92412d97 100644
--- a/chrome/browser/resources/settings/privacy_page/cookies_page.html
+++ b/chrome/browser/resources/settings/privacy_page/cookies_page.html
@@ -104,7 +104,7 @@
               <div class="bullet-line" id="blockThirdPartyIncognitoBulTwo">
                 <iron-icon icon="settings:block"></iron-icon>
                 <div class="secondary">
-                  [[getThirdPartyCookiesPageBlockThirdIncognitoBulTwoLabel_()]]
+                  [[getThirdPartyCookiesPageBlockThirdPartyIncognitoBulTwoLabel_()]]
                 </div>
               </div>
             </div>
@@ -180,10 +180,11 @@
                       $i18n{cookiePageBlockThirdIncognitoBulOne}
                 </div>
               </div>
-              <div class="bullet-line" id="cookiePageBlockThirdIncognitoBulTwo">
+              <div class="bullet-line"
+                  id="cookiesPageBlockThirdPartyIncognitoBulTwo">
                 <iron-icon icon="settings:block"></iron-icon>
                 <div class="secondary">
-                  [[getCookiePageBlockThirdIncognitoBulTwoLabel_()]]
+                  [[getCookiesPageBlockThirdPartyIncognitoBulTwoLabel_()]]
                 </div>
               </div>
             </div>
diff --git a/chrome/browser/resources/settings/privacy_page/cookies_page.ts b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
index 044a4274..d8d7653cdd 100644
--- a/chrome/browser/resources/settings/privacy_page/cookies_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
@@ -206,14 +206,15 @@
     }
   }
 
-  private getThirdPartyCookiesPageBlockThirdIncognitoBulTwoLabel_(): string {
+  private getThirdPartyCookiesPageBlockThirdPartyIncognitoBulTwoLabel_():
+      string {
     return this.i18n(
         this.enableFirstPartySetsUI_ ?
             'cookiePageBlockThirdIncognitoBulTwoFps' :
             'thirdPartyCookiesPageBlockIncognitoBulTwo');
   }
 
-  private getCookiePageBlockThirdIncognitoBulTwoLabel_(): string {
+  private getCookiesPageBlockThirdPartyIncognitoBulTwoLabel_(): string {
     return this.i18n(
         this.enableFirstPartySetsUI_ ?
             'cookiePageBlockThirdIncognitoBulTwoFps' :
diff --git a/chrome/browser/resources/side_panel/user_notes/BUILD.gn b/chrome/browser/resources/side_panel/user_notes/BUILD.gn
index e33b9234..83b8910 100644
--- a/chrome/browser/resources/side_panel/user_notes/BUILD.gn
+++ b/chrome/browser/resources/side_panel/user_notes/BUILD.gn
@@ -11,6 +11,11 @@
 
   static_files = [ "user_notes.html" ]
 
+  web_component_files = [
+    "app.ts",
+    "user_note.ts",
+  ]
+
   non_web_component_files = [ "user_notes_api_proxy.ts" ]
 
   mojo_files_deps = [ "//chrome/browser/ui/webui/side_panel/user_notes:mojo_bindings_js__generator" ]
diff --git a/chrome/browser/resources/side_panel/user_notes/app.html b/chrome/browser/resources/side_panel/user_notes/app.html
new file mode 100644
index 0000000..a368abd
--- /dev/null
+++ b/chrome/browser/resources/side_panel/user_notes/app.html
@@ -0,0 +1,11 @@
+<style>
+  user-note {
+    margin: 12px 0;
+  }
+</style>
+
+<template id="notesList" is="dom-repeat" items="[[notes_]]"
+    sort="sortByModificationTime_">
+  <user-note note="[[item]]"></user-note>
+</template>
+<user-note id="entryNote"></user-note>
\ No newline at end of file
diff --git a/chrome/browser/resources/side_panel/user_notes/app.ts b/chrome/browser/resources/side_panel/user_notes/app.ts
new file mode 100644
index 0000000..e82ec6a0
--- /dev/null
+++ b/chrome/browser/resources/side_panel/user_notes/app.ts
@@ -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.
+
+import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import '../strings.m.js';
+import './user_note.js';
+
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {listenOnce} from 'chrome://resources/js/util_ts.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './app.html.js';
+import {Note} from './user_notes.mojom-webui.js';
+import {UserNotesApiProxy, UserNotesApiProxyImpl} from './user_notes_api_proxy.js';
+
+export interface UserNotesAppElement {
+  $: {
+    notesList: HTMLElement,
+  };
+}
+
+export class UserNotesAppElement extends PolymerElement {
+  static get is() {
+    return 'user-notes-app';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+
+  static get properties() {
+    return {
+      notes_: {
+        type: Array,
+        value: () => [],
+      },
+    };
+  }
+
+  private notes_: Note[];
+  private userNotesApi_: UserNotesApiProxy =
+      UserNotesApiProxyImpl.getInstance();
+  private listenerId_: number|null = null;
+
+  override connectedCallback() {
+    super.connectedCallback();
+
+    const callbackRouter = this.userNotesApi_.getCallbackRouter();
+    this.listenerId_ = callbackRouter.notesChanged.addListener(() => {
+      this.updateNotes();
+    });
+
+    listenOnce(this.$.notesList, 'dom-change', () => {
+      // Push the ShowUi() callback to the event queue to allow deferred
+      // rendering to take place.
+      this.userNotesApi_.showUi();
+    });
+    this.updateNotes();
+  }
+
+  override disconnectedCallback() {
+    super.disconnectedCallback();
+
+    assert(this.listenerId_);
+    this.userNotesApi_.getCallbackRouter().removeListener(this.listenerId_);
+    this.listenerId_ = null;
+  }
+
+  /**
+   * Fetches the latest notes from the browser.
+   */
+  private async updateNotes() {
+    const {notes} = await this.userNotesApi_.getNotesForCurrentTab();
+    this.notes_ = notes;
+  }
+
+  private sortByModificationTime_(note1: Note, note2: Note): number {
+    return Number(
+        note1.lastModificationTime.internalValue -
+        note2.lastModificationTime.internalValue);
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'user-notes-app': UserNotesAppElement;
+  }
+}
+
+customElements.define(UserNotesAppElement.is, UserNotesAppElement);
diff --git a/chrome/browser/resources/side_panel/user_notes/user_note.html b/chrome/browser/resources/side_panel/user_notes/user_note.html
new file mode 100644
index 0000000..797615d
--- /dev/null
+++ b/chrome/browser/resources/side_panel/user_notes/user_note.html
@@ -0,0 +1,131 @@
+<style include="mwb-element-shared-style">
+  :host {
+    --note-background-color: var(--google-grey-100);
+    --note-content-vertical-padding: 6px;
+    --note-content-horizontal-padding: 12px;
+    --note-placeholder-text-color: var(--google-grey-500);
+    border-radius: 8px;
+    color: var(--cr-primary-text-color);
+    display: block;
+    font-size: var(--mwb-primary-text-font-size);
+  }
+
+  @media (prefers-color-scheme: dark) {
+    :host {
+      /* TODO(crbug.com/1394044): define background color and placeholder color
+       * for dark mode. */
+    }
+  }
+
+  :host(:not([editing_])) {
+    background-color: var(--note-background-color);
+    padding: var(--note-content-vertical-padding) 0;
+  }
+
+  :host([editing_]) {
+    border: 1px solid var(--cr-separator-color);
+    position: relative;
+  }
+
+  #inactiveHeader {
+    align-items: center;
+    color: var(--cr-secondary-text-color);
+    display: flex;
+    font-size: var(--mwb-secondary-text-font-size);
+    padding: 0 var(--note-content-horizontal-padding)
+            var(--note-content-vertical-padding);
+  }
+
+  #headerText {
+    flex-grow: 1;
+    position: relative;
+  }
+
+  #menuButton {
+    --cr-icon-button-fill-color: var(--cr-secondary-text-color);
+  }
+
+  #noteContent {
+    color: var(--cr-primary-text-color);
+    padding: var(--note-content-vertical-padding)
+            var(--note-content-horizontal-padding);
+  }
+
+  :host([editing_]) #noteContent {
+    min-height: 24px;
+    position: relative;
+  }
+
+  :host([editing_]:focus-within) #noteContent,
+  :host([editing_]) #noteContent:not(:empty) {
+    min-height: 62px;
+    padding: var(--note-content-vertical-padding)
+            var(--note-content-horizontal-padding) 42px;
+  }
+
+  #placeholderText {
+    color: var(--note-placeholder-text-color);
+    padding: var(--note-content-vertical-padding)
+        var(--note-content-horizontal-padding);
+    position: absolute;
+    user-select: none;
+  }
+
+  #editingFooter {
+    align-items: end;
+    bottom: 0;
+    box-sizing: border-box;
+    display: flex;
+    flex-direction: row;
+    padding: var(--note-content-vertical-padding)
+            var(--note-content-horizontal-padding);
+    position: absolute;
+    width: 100%;
+  }
+
+  #noteContent:empty:not(:focus-within) + #editingFooter  {
+    display: none;
+  }
+
+  :host(:focus-within) #editingFooter  {
+    display: flex;
+  }
+
+  #characterCounter {
+    color: var(--cr-secondary-text-color);
+    flex: 1;
+    font-size: var(--mwb-secondary-text-font-size);
+    padding-bottom: 4px;
+  }
+
+  #cancelButton {
+    margin-inline-end: var(--note-content-horizontal-padding);
+  }
+
+  #placeholderText:has(+ #noteContent:focus) {
+    display: none;
+  }
+</style>
+
+<template is="dom-if" if="[[!editing_]]" restamp>
+  <div id="inactiveHeader" hidden="[[editing_]]">
+    <div id="headerText">[[note.lastModificationTimeText]]</div>
+    <cr-icon-button id="menuButton" class="no-overlap" iron-icon="cr:more-vert"
+        on-click="onMenuButtonClick_" >
+    </cr-icon-button>
+  </div>
+</template>
+<div id="placeholderText" hidden="[[!showPlaceholder_]]">$i18n{addANote}</div>
+<div id="noteContent" contenteditable$="[[getContentEditable_(editing_)]]"
+    on-input="onNoteContentInput_"></div>
+<template is="dom-if" if="[[editing_]]" restamp>
+  <div id="editingFooter">
+    <span id="characterCounter">[[characterCounter_]]/176</span>
+    <cr-button id="cancelButton" on-click="onCancelClick_">
+      $i18n{cancel}
+    </cr-button>
+    <cr-button id="addButton" class="action-button" on-click="onAddClick_">
+      $i18n{add}
+    </cr-button>
+  </div>
+</template>
diff --git a/chrome/browser/resources/side_panel/user_notes/user_note.ts b/chrome/browser/resources/side_panel/user_notes/user_note.ts
new file mode 100644
index 0000000..6d621bd2
--- /dev/null
+++ b/chrome/browser/resources/side_panel/user_notes/user_note.ts
@@ -0,0 +1,131 @@
+// 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 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/cr_elements/mwb_element_shared_style.css.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import 'chrome://resources/cr_elements/icons.html.js';
+
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './user_note.html.js';
+import {Note} from './user_notes.mojom-webui.js';
+import {UserNotesApiProxy, UserNotesApiProxyImpl} from './user_notes_api_proxy.js';
+
+export interface UserNoteElement {
+  $: {
+    noteContent: HTMLElement,
+  };
+}
+
+export class UserNoteElement extends PolymerElement {
+  static get is() {
+    return 'user-note';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+
+  static get properties() {
+    return {
+      note: {
+        type: Object,
+        observer: 'onNoteChanged_',
+      },
+
+      editing_: {
+        type: Boolean,
+        reflectToAttribute: true,
+        value: false,
+      },
+
+      characterCounter_: {
+        type: Number,
+        computed: 'computeCharacterCounter_(noteContent_)',
+      },
+
+      showPlaceholder_: {
+        type: Boolean,
+        computed: 'computeShowPlaceholder_(noteContent_)',
+      },
+
+      noteContent_: {
+        type: String,
+        value: '',
+      },
+    };
+  }
+
+  note?: Note;
+  private characterCounter_: string;
+  private editing_: boolean;
+  private noteContent_: string;
+  private showPlaceholder_: boolean;
+
+  private userNotesApi_: UserNotesApiProxy =
+      UserNotesApiProxyImpl.getInstance();
+
+  override ready() {
+    super.ready();
+    this.editing_ = this.note === undefined;
+  }
+
+  private onNoteContentInput_() {
+    this.noteContent_ = this.$.noteContent.textContent!;
+  }
+
+  private computeCharacterCounter_(): number {
+    return this.noteContent_.length;
+  }
+
+  private computeShowPlaceholder_(): boolean {
+    return this.noteContent_.length === 0;
+  }
+
+  private onNoteChanged_() {
+    if (this.note) {
+      this.$.noteContent.textContent = this.note.text;
+    }
+    this.onNoteContentInput_();
+  }
+
+  private getContentEditable_() {
+    return this.editing_ ? 'true' : 'false';
+  }
+
+  private clearInput_() {
+    this.$.noteContent.textContent = '';
+    this.onNoteContentInput_();
+  }
+
+  private onCancelClick_() {
+    this.clearInput_();
+  }
+
+  private async onAddClick_() {
+    if (this.note === undefined) {
+      await this.userNotesApi_.newNoteFinished(this.$.noteContent.textContent!);
+      this.clearInput_();
+    } else {
+      // TODO(crbug.com/1394044): implement editing of an existing note.
+    }
+  }
+
+  private onMenuButtonClick_() {
+    // TODO(crbug.com/1394044): add menu with edit and delete options.
+    assert(this.note);
+    this.userNotesApi_.deleteNote(this.note.guid);
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'user-note': UserNoteElement;
+  }
+}
+
+customElements.define(UserNoteElement.is, UserNoteElement);
diff --git a/chrome/browser/resources/side_panel/user_notes/user_notes.html b/chrome/browser/resources/side_panel/user_notes/user_notes.html
index 60b881b..2ef5c8cf 100644
--- a/chrome/browser/resources/side_panel/user_notes/user_notes.html
+++ b/chrome/browser/resources/side_panel/user_notes/user_notes.html
@@ -3,7 +3,10 @@
   <head>
     <meta charset="utf-8">
     <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
+    <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
   </head>
   <body>
+    <user-notes-app></user-notes-app>
+    <script type="module" src="app.js"></script>
   </body>
 </html>
\ No newline at end of file
diff --git a/chrome/browser/safe_browsing/incident_reporting/platform_state_store_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/platform_state_store_win_unittest.cc
index c4cfc95..79de25c 100644
--- a/chrome/browser/safe_browsing/incident_reporting/platform_state_store_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/platform_state_store_win_unittest.cc
@@ -118,7 +118,7 @@
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 const wchar_t PlatformStateStoreWinTest::kStoreKeyName_[] =
     L"Software\\Google\\Chrome\\IncidentsSent";
-#elif BUILDFLAG(GOOGLE_CHROME_FOR_TESTING_BRANDING)
+#elif BUILDFLAG(USE_INTERNAL_CHROME_FOR_TESTING_ICONS)
 const wchar_t PlatformStateStoreWinTest::kStoreKeyName_[] =
     L"Software\\Google\\Chrome for Testing\\IncidentsSent";
 #else
diff --git a/chrome/browser/sharing/sharing_message_model_type_controller.cc b/chrome/browser/sharing/sharing_message_model_type_controller.cc
index d395993..1386b96 100644
--- a/chrome/browser/sharing/sharing_message_model_type_controller.cc
+++ b/chrome/browser/sharing/sharing_message_model_type_controller.cc
@@ -6,40 +6,33 @@
 
 #include <utility>
 
-#include "components/sync/driver/sync_service.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-
 SharingMessageModelTypeController::SharingMessageModelTypeController(
-    syncer::SyncService* sync_service,
     std::unique_ptr<syncer::ModelTypeControllerDelegate>
         delegate_for_full_sync_mode,
     std::unique_ptr<syncer::ModelTypeControllerDelegate>
         delegate_for_transport_mode)
     : syncer::ModelTypeController(syncer::SHARING_MESSAGE,
                                   std::move(delegate_for_full_sync_mode),
-                                  std::move(delegate_for_transport_mode)),
-      sync_service_(sync_service) {
-  sync_service_->AddObserver(this);
-}
+                                  std::move(delegate_for_transport_mode)) {}
 
-SharingMessageModelTypeController::~SharingMessageModelTypeController() {
-  sync_service_->RemoveObserver(this);
-}
+SharingMessageModelTypeController::~SharingMessageModelTypeController() =
+    default;
 
-syncer::DataTypeController::PreconditionState
-SharingMessageModelTypeController::GetPreconditionState() const {
+void SharingMessageModelTypeController::Stop(
+    syncer::ShutdownReason shutdown_reason,
+    StopCallback callback) {
   DCHECK(CalledOnValidThread());
-  // TODO(crbug.com/1156584): No need to handle IsPersistentError() once the
-  // feature toggle is cleaned up and sync gets paused for all persistent auth
-  // errors. Instead, consider forcing DISABLE_SYNC_AND_CLEAR_DATA in Stop().
-  return sync_service_->GetAuthError().IsPersistentError()
-             ? PreconditionState::kMustStopAndClearData
-             : PreconditionState::kPreconditionsMet;
-}
-
-void SharingMessageModelTypeController::OnStateChanged(
-    syncer::SyncService* sync) {
-  DCHECK(CalledOnValidThread());
-  // Most of these calls will be no-ops but SyncService handles that just fine.
-  sync_service_->DataTypePreconditionChanged(type());
+  switch (shutdown_reason) {
+    case syncer::ShutdownReason::STOP_SYNC_AND_KEEP_DATA:
+      // Clear sync metadata even when sync gets paused (e.g. persistent auth
+      // error). This is needed because SharingMessageBridgeImpl uses the
+      // processor's IsTrackingMetadata() bit to determine whether sharing
+      // messages can be sent (they can't if sync is paused).
+      shutdown_reason = syncer::ShutdownReason::DISABLE_SYNC_AND_CLEAR_DATA;
+      break;
+    case syncer::ShutdownReason::DISABLE_SYNC_AND_CLEAR_DATA:
+    case syncer::ShutdownReason::BROWSER_SHUTDOWN_AND_KEEP_DATA:
+      break;
+  }
+  ModelTypeController::Stop(shutdown_reason, std::move(callback));
 }
diff --git a/chrome/browser/sharing/sharing_message_model_type_controller.h b/chrome/browser/sharing/sharing_message_model_type_controller.h
index 195fd45..374fead 100644
--- a/chrome/browser/sharing/sharing_message_model_type_controller.h
+++ b/chrome/browser/sharing/sharing_message_model_type_controller.h
@@ -7,23 +7,14 @@
 
 #include <memory>
 
-#include "base/memory/raw_ptr.h"
 #include "components/sync/driver/model_type_controller.h"
-#include "components/sync/driver/sync_service_observer.h"
-
-namespace syncer {
-class SyncService;
-}  // namespace syncer
 
 // Controls syncing of SHARING_MESSAGE.
-class SharingMessageModelTypeController : public syncer::ModelTypeController,
-                                          public syncer::SyncServiceObserver {
+class SharingMessageModelTypeController : public syncer::ModelTypeController {
  public:
-  // The |delegate_for_full_sync_mode|, |delegate_for_transport_mode| and
-  // |sync_service| must not be null. Furthermore, |sync_service| must outlive
-  // this object.
+  // |delegate_for_full_sync_mode| and |delegate_for_transport_mode| must not be
+  // null.
   SharingMessageModelTypeController(
-      syncer::SyncService* sync_service,
       std::unique_ptr<syncer::ModelTypeControllerDelegate>
           delegate_for_full_sync_mode,
       std::unique_ptr<syncer::ModelTypeControllerDelegate>
@@ -35,13 +26,8 @@
       const SharingMessageModelTypeController&) = delete;
 
   // DataTypeController overrides.
-  PreconditionState GetPreconditionState() const override;
-
-  // SyncServiceObserver implementation.
-  void OnStateChanged(syncer::SyncService* sync) override;
-
- private:
-  const raw_ptr<syncer::SyncService> sync_service_;
+  void Stop(syncer::ShutdownReason shutdown_reason,
+            StopCallback callback) override;
 };
 
 #endif  // CHROME_BROWSER_SHARING_SHARING_MESSAGE_MODEL_TYPE_CONTROLLER_H_
diff --git a/chrome/browser/signin/signin_ui_util.cc b/chrome/browser/signin/signin_ui_util.cc
index 2e5bb916..2b6eebe 100644
--- a/chrome/browser/signin/signin_ui_util.cc
+++ b/chrome/browser/signin/signin_ui_util.cc
@@ -53,6 +53,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "components/account_manager_core/account_manager_facade.h"
 #include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
+#include "components/user_manager/user.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service_impl.cc b/chrome/browser/supervised_user/child_accounts/child_account_service_impl.cc
index 3c59d78..a0f02bf 100644
--- a/chrome/browser/supervised_user/child_accounts/child_account_service_impl.cc
+++ b/chrome/browser/supervised_user/child_accounts/child_account_service_impl.cc
@@ -42,6 +42,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_type.h"
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chromeos/startup/browser_params_proxy.h"
 #else
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index bf6a672..e66fea5 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -49,7 +49,6 @@
 #include "content/public/browser/storage_partition.h"
 #include "extensions/buildflags/buildflags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !BUILDFLAG(IS_ANDROID)
@@ -543,9 +542,7 @@
 
   if (use_online_check != url_filter_.HasAsyncURLChecker()) {
     if (use_online_check) {
-      url_filter_.InitAsyncURLChecker(
-          profile_->GetDefaultStoragePartition()
-              ->GetURLLoaderFactoryForBrowserProcess());
+      url_filter_.InitAsyncURLChecker();
     } else {
       url_filter_.ClearAsyncURLChecker();
     }
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter.cc b/chrome/browser/supervised_user/supervised_user_url_filter.cc
index 3047c087..396b4b0 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter.cc
+++ b/chrome/browser/supervised_user/supervised_user_url_filter.cc
@@ -12,21 +12,18 @@
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/supervised_user/kids_management_url_checker_client.h"
 #include "chrome/browser/supervised_user/supervised_user_denylist.h"
 #include "chrome/browser/supervised_user/supervised_user_service.h"
-#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/common/url_constants.h"
 #include "components/url_matcher/url_util.h"
 #include "components/variations/service/variations_service.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/buildflags/buildflags.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
@@ -524,8 +521,7 @@
   url_map_ = std::move(url_map);
 }
 
-void SupervisedUserURLFilter::InitAsyncURLChecker(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+void SupervisedUserURLFilter::InitAsyncURLChecker() {
   std::string country;
   variations::VariationsService* variations_service =
       g_browser_process->variations_service();
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter.h b/chrome/browser/supervised_user/supervised_user_url_filter.h
index 494e728..6ea1f963 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter.h
+++ b/chrome/browser/supervised_user/supervised_user_url_filter.h
@@ -15,7 +15,6 @@
 #include "base/observer_list.h"
 #include "base/sequence_checker.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/supervised_user/supervised_users.h"
 #include "components/safe_search_api/url_checker.h"
 #include "components/supervised_user/core/browser/supervised_user_error_page.h"
 
@@ -30,10 +29,6 @@
 class WebContents;
 }  // namespace content
 
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
 // This class manages the filtering behavior for URLs, i.e. it tells callers
 // if a URL should be allowed or blocked. It uses information
 // from multiple sources:
@@ -214,8 +209,7 @@
   void SetManualURLs(std::map<GURL, bool> url_map);
 
   // Initializes the experimental asynchronous checker.
-  void InitAsyncURLChecker(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+  void InitAsyncURLChecker();
 
   // Clears any asynchronous checker.
   void ClearAsyncURLChecker();
diff --git a/chrome/browser/support_tool/ash/chrome_user_logs_data_collector.cc b/chrome/browser/support_tool/ash/chrome_user_logs_data_collector.cc
index c4c85132..7361115 100644
--- a/chrome/browser/support_tool/ash/chrome_user_logs_data_collector.cc
+++ b/chrome/browser/support_tool/ash/chrome_user_logs_data_collector.cc
@@ -32,6 +32,7 @@
 #include "components/feedback/pii_types.h"
 #include "components/feedback/redaction_tool.h"
 #include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 3d2d0fa..e19f62a4 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -405,7 +405,6 @@
     syncer::ModelTypeControllerDelegate* sharing_message_delegate =
         GetControllerDelegateForModelType(syncer::SHARING_MESSAGE).get();
     controllers.push_back(std::make_unique<SharingMessageModelTypeController>(
-        sync_service,
         /*delegate_for_full_sync_mode=*/
         std::make_unique<syncer::ForwardingModelTypeControllerDelegate>(
             sharing_message_delegate),
diff --git a/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id.cc b/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id.cc
deleted file mode 100644
index 93e518b..0000000
--- a/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id.cc
+++ /dev/null
@@ -1,32 +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 "chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id.h"
-
-#include "base/strings/stringprintf.h"
-#include "components/gcm_driver/instance_id/instance_id.h"
-
-FakeSyncInstanceID::FakeSyncInstanceID(const std::string& app_id,
-                                       gcm::GCMDriver* gcm_driver)
-    : instance_id::InstanceID(app_id, gcm_driver),
-      token_(GenerateNextToken()) {}
-
-void FakeSyncInstanceID::GetToken(const std::string& authorized_entity,
-                                  const std::string& scope,
-                                  base::TimeDelta time_to_live,
-                                  std::set<Flags> flags,
-                                  GetTokenCallback callback) {
-  std::move(callback).Run(token_, instance_id::InstanceID::Result::SUCCESS);
-}
-
-// Deleting the InstanceID also clears any associated token.
-void FakeSyncInstanceID::DeleteIDImpl(DeleteIDCallback callback) {
-  token_ = GenerateNextToken();
-  std::move(callback).Run(instance_id::InstanceID::Result::SUCCESS);
-}
-
-std::string FakeSyncInstanceID::GenerateNextToken() {
-  static int next_token_id_ = 1;
-  return base::StringPrintf("token %d", next_token_id_++);
-}
diff --git a/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id.h b/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id.h
deleted file mode 100644
index 58802f47..0000000
--- a/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id.h
+++ /dev/null
@@ -1,59 +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 CHROME_BROWSER_SYNC_TEST_INTEGRATION_INVALIDATIONS_FAKE_SYNC_INSTANCE_ID_H_
-#define CHROME_BROWSER_SYNC_TEST_INTEGRATION_INVALIDATIONS_FAKE_SYNC_INSTANCE_ID_H_
-
-#include <set>
-#include <string>
-
-#include "base/time/time.h"
-#include "components/gcm_driver/instance_id/instance_id.h"
-
-namespace gcm {
-class GCMDriver;
-}  // namespace gcm
-
-class FakeSyncInstanceID : public instance_id::InstanceID {
- public:
-  FakeSyncInstanceID(const std::string& app_id, gcm::GCMDriver* gcm_driver);
-
-  FakeSyncInstanceID(const FakeSyncInstanceID&) = delete;
-  FakeSyncInstanceID& operator=(const FakeSyncInstanceID&) = delete;
-
-  ~FakeSyncInstanceID() override = default;
-
-  void GetID(GetIDCallback callback) override {}
-
-  void GetCreationTime(GetCreationTimeCallback callback) override {}
-
-  void GetToken(const std::string& authorized_entity,
-                const std::string& scope,
-                base::TimeDelta time_to_live,
-                std::set<Flags> flags,
-                GetTokenCallback callback) override;
-
-  void ValidateToken(const std::string& authorized_entity,
-                     const std::string& scope,
-                     const std::string& token,
-                     ValidateTokenCallback callback) override {}
-
-  void DeleteToken(const std::string& authorized_entity,
-                   const std::string& scope,
-                   DeleteTokenCallback callback) override {}
-
- protected:
-  void DeleteTokenImpl(const std::string& authorized_entity,
-                       const std::string& scope,
-                       DeleteTokenCallback callback) override {}
-
-  void DeleteIDImpl(DeleteIDCallback callback) override;
-
- private:
-  static std::string GenerateNextToken();
-
-  std::string token_;
-};
-
-#endif  // CHROME_BROWSER_SYNC_TEST_INTEGRATION_INVALIDATIONS_FAKE_SYNC_INSTANCE_ID_H_
diff --git a/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.cc b/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.cc
deleted file mode 100644
index 106a4eb..0000000
--- a/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.cc
+++ /dev/null
@@ -1,28 +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 "chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.h"
-
-#include "chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id.h"
-#include "components/gcm_driver/gcm_driver.h"
-#include "components/gcm_driver/instance_id/instance_id.h"
-
-FakeSyncInstanceIDDriver::FakeSyncInstanceIDDriver(gcm::GCMDriver* gcm_driver)
-    : instance_id::InstanceIDDriver(gcm_driver), gcm_driver_(gcm_driver) {}
-
-FakeSyncInstanceIDDriver::~FakeSyncInstanceIDDriver() = default;
-
-instance_id::InstanceID* FakeSyncInstanceIDDriver::GetInstanceID(
-    const std::string& app_id) {
-  if (!fake_instance_ids_.count(app_id)) {
-    fake_instance_ids_[app_id] =
-        std::make_unique<FakeSyncInstanceID>(app_id, gcm_driver_);
-  }
-  return fake_instance_ids_[app_id].get();
-}
-
-bool FakeSyncInstanceIDDriver::ExistsInstanceID(
-    const std::string& app_id) const {
-  return fake_instance_ids_.count(app_id);
-}
diff --git a/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.h b/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.h
deleted file mode 100644
index 9878535..0000000
--- a/chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.h
+++ /dev/null
@@ -1,40 +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 CHROME_BROWSER_SYNC_TEST_INTEGRATION_INVALIDATIONS_FAKE_SYNC_INSTANCE_ID_DRIVER_H_
-#define CHROME_BROWSER_SYNC_TEST_INTEGRATION_INVALIDATIONS_FAKE_SYNC_INSTANCE_ID_DRIVER_H_
-
-#include <memory>
-#include <string>
-
-#include "components/gcm_driver/instance_id/instance_id_driver.h"
-
-namespace gcm {
-class GCMDriver;
-}  // namespace gcm
-
-namespace instance_id {
-class InstanceID;
-}  // namespace instance_id
-
-class FakeSyncInstanceID;
-
-class FakeSyncInstanceIDDriver : public instance_id::InstanceIDDriver {
- public:
-  explicit FakeSyncInstanceIDDriver(gcm::GCMDriver* gcm_driver);
-
-  FakeSyncInstanceIDDriver(const FakeSyncInstanceIDDriver&) = delete;
-  FakeSyncInstanceIDDriver& operator=(const FakeSyncInstanceIDDriver&) = delete;
-
-  ~FakeSyncInstanceIDDriver() override;
-  instance_id::InstanceID* GetInstanceID(const std::string& app_id) override;
-  void RemoveInstanceID(const std::string& app_id) override {}
-  bool ExistsInstanceID(const std::string& app_id) const override;
-
- private:
-  raw_ptr<gcm::GCMDriver, DanglingUntriaged> gcm_driver_;
-  std::map<std::string, std::unique_ptr<FakeSyncInstanceID>> fake_instance_ids_;
-};
-
-#endif  // CHROME_BROWSER_SYNC_TEST_INTEGRATION_INVALIDATIONS_FAKE_SYNC_INSTANCE_ID_DRIVER_H_
diff --git a/chrome/browser/sync/test/integration/single_client_arc_package_sync_test.cc b/chrome/browser/sync/test/integration/single_client_arc_package_sync_test.cc
index f7a73dab..960ba3d5 100644
--- a/chrome/browser/sync/test/integration/single_client_arc_package_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_arc_package_sync_test.cc
@@ -5,6 +5,7 @@
 #include "ash/constants/ash_features.h"
 #include "chrome/browser/ash/app_list/arc/arc_package_syncable_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/test/integration/fake_server_match_status_checker.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/sync_arc_package_helper.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
@@ -16,15 +17,14 @@
 #include "components/sync/protocol/entity_specifics.pb.h"
 #include "components/sync/test/fake_server.h"
 #include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
 
 namespace arc {
 
 namespace {
 
-bool AllProfilesHaveSameArcPackageDetails() {
-  return SyncArcPackageHelper::GetInstance()
-      ->AllProfilesHaveSamePackageDetails();
-}
+using testing::IsEmpty;
+using testing::SizeIs;
 
 class ArcPackagesCountChecker : public SingleClientStatusChangeChecker {
  public:
@@ -49,21 +49,61 @@
   const size_t expected_count_;
 };
 
+class FakeServerArcPackageMatchChecker
+    : public fake_server::FakeServerMatchStatusChecker {
+ public:
+  explicit FakeServerArcPackageMatchChecker(
+      const std::vector<sync_pb::EntitySpecifics>& expected_entities)
+      : expected_entities_(expected_entities) {}
+  ~FakeServerArcPackageMatchChecker() override = default;
+
+  bool IsExitConditionSatisfied(std::ostream* os) override {
+    *os << "Waiting for server-side Arc packages to match expected.";
+
+    std::vector<sync_pb::SyncEntity> server_entities =
+        fake_server()->GetSyncEntitiesByModelType(syncer::ARC_PACKAGE);
+    if (server_entities.size() != expected_entities_.size()) {
+      return false;
+    }
+
+    for (const auto& server_entity : server_entities) {
+      const sync_pb::ArcPackageSpecifics& server_specifics =
+          server_entity.specifics().arc_package();
+
+      bool matched = false;
+      for (const auto& expected_entity : expected_entities_) {
+        const sync_pb::ArcPackageSpecifics& expected_specifics =
+            expected_entity.arc_package();
+
+        if (server_specifics.SerializeAsString() ==
+            expected_specifics.SerializeAsString()) {
+          matched = true;
+          break;
+        }
+      }
+      if (!matched) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+ private:
+  const std::vector<sync_pb::EntitySpecifics> expected_entities_;
+};
+
 class SingleClientArcPackageSyncTest : public SyncTest {
  public:
   SingleClientArcPackageSyncTest() : SyncTest(SINGLE_CLIENT) {}
   ~SingleClientArcPackageSyncTest() override = default;
-
-  bool UseVerifier() override {
-    // TODO(crbug.com/1137774): rewrite tests to not use verifier.
-    return true;
-  }
 };
 
 IN_PROC_BROWSER_TEST_F(SingleClientArcPackageSyncTest, ArcPackageEmpty) {
   ASSERT_TRUE(SetupSync());
 
-  ASSERT_TRUE(AllProfilesHaveSameArcPackageDetails());
+  EXPECT_THAT(ArcAppListPrefs::Get(GetProfile(0))->GetPackagesFromPrefs(),
+              IsEmpty());
 }
 
 IN_PROC_BROWSER_TEST_F(SingleClientArcPackageSyncTest,
@@ -71,21 +111,24 @@
   ASSERT_TRUE(SetupSync());
 
   constexpr size_t kNumPackages = 5;
+  std::vector<sync_pb::EntitySpecifics> expected_specifics;
   for (size_t i = 0; i < kNumPackages; ++i) {
     sync_arc_helper()->InstallPackageWithIndex(GetProfile(0), i);
-    sync_arc_helper()->InstallPackageWithIndex(verifier(), i);
+    expected_specifics.push_back(sync_arc_helper()->GetTestSpecifics(i));
   }
 
-  ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
-  ASSERT_TRUE(AllProfilesHaveSameArcPackageDetails());
+  ASSERT_THAT(ArcAppListPrefs::Get(GetProfile(0))->GetPackagesFromPrefs(),
+              SizeIs(kNumPackages));
+  EXPECT_TRUE(FakeServerArcPackageMatchChecker(expected_specifics).Wait());
 }
 
 // Regression test for crbug.com/978837.
 IN_PROC_BROWSER_TEST_F(SingleClientArcPackageSyncTest, DisableAndReenable) {
   ASSERT_TRUE(SetupSync());
 
-  sync_arc_helper()->InstallPackageWithIndex(verifier(), 0);
-  sync_pb::EntitySpecifics specifics = sync_arc_helper()->GetTestSpecifics(0);
+  const size_t kTestPackageId = 0;
+  sync_pb::EntitySpecifics specifics =
+      sync_arc_helper()->GetTestSpecifics(kTestPackageId);
 
   // Disable ARC++ to verify sync resumes correctly when it gets reenabled
   // later. Note that the sync datatype itself is not disabled.
@@ -110,10 +153,11 @@
   // happening late, after sync has started.
   sync_arc_helper()->SendRefreshPackageList(GetProfile(0));
 
-  ASSERT_TRUE(ArcPackagesCountChecker(GetProfile(0), GetSyncService(0),
+  EXPECT_TRUE(ArcPackagesCountChecker(GetProfile(0), GetSyncService(0),
                                       /*expected_count=*/1)
                   .Wait());
-  ASSERT_TRUE(AllProfilesHaveSameArcPackageDetails());
+  EXPECT_TRUE(
+      sync_arc_helper()->HasOnlyTestPackages(GetProfile(0), {kTestPackageId}));
 }
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/sync_arc_package_helper.cc b/chrome/browser/sync/test/integration/sync_arc_package_helper.cc
index 7e3d61c..6e55771 100644
--- a/chrome/browser/sync/test/integration/sync_arc_package_helper.cc
+++ b/chrome/browser/sync/test/integration/sync_arc_package_helper.cc
@@ -99,6 +99,32 @@
   }
 }
 
+bool SyncArcPackageHelper::HasOnlyTestPackages(Profile* profile,
+                                               const std::vector<size_t>& ids) {
+  const ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile);
+  DCHECK(prefs);
+  if (prefs->GetPackagesFromPrefs().size() != ids.size()) {
+    return false;
+  }
+
+  for (const size_t id : ids) {
+    std::unique_ptr<ArcAppListPrefs::PackageInfo> package_info =
+        prefs->GetPackage(GetTestPackageName(id));
+    if (!package_info) {
+      return false;
+    }
+    // See InstallPackageWithIndex().
+    if (package_info->package_version != static_cast<int32_t>(id) ||
+        package_info->last_backup_android_id != static_cast<int64_t>(id) ||
+        package_info->last_backup_time != static_cast<int64_t>(id) ||
+        !package_info->should_sync) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 bool SyncArcPackageHelper::AllProfilesHaveSamePackages() {
   const std::vector<Profile*>& profiles = test_->GetAllProfiles();
   for (Profile* profile : profiles) {
diff --git a/chrome/browser/sync/test/integration/sync_arc_package_helper.h b/chrome/browser/sync/test/integration/sync_arc_package_helper.h
index dc06894..0bbb5b1 100644
--- a/chrome/browser/sync/test/integration/sync_arc_package_helper.h
+++ b/chrome/browser/sync/test/integration/sync_arc_package_helper.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 #include <unordered_map>
+#include <vector>
 
 #include "ash/components/arc/mojom/app.mojom-forward.h"
 #include "base/memory/singleton.h"
@@ -42,6 +43,8 @@
 
   void ClearPackages(Profile* profile);
 
+  bool HasOnlyTestPackages(Profile* profile, const std::vector<size_t>& ids);
+
   bool AllProfilesHaveSamePackages();
 
   bool AllProfilesHaveSamePackageDetails();
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index a80d61b..822a650 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -41,7 +41,6 @@
 #include "chrome/browser/sync/test/integration/committed_all_nudged_changes_checker.h"
 #include "chrome/browser/sync/test/integration/device_info_helper.h"
 #include "chrome/browser/sync/test/integration/fake_sync_gcm_driver_for_instance_id.h"
-#include "chrome/browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.h"
 #include "chrome/browser/sync/test/integration/session_hierarchy_match_checker.h"
 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
 #include "chrome/browser/sync/test/integration/sync_disabled_checker.h"
@@ -209,14 +208,6 @@
   return it != profile_to_fcm_network_handler_map->end() ? it->second : nullptr;
 }
 
-std::unique_ptr<KeyedService> CreateInstanceIDProfileService(
-    content::BrowserContext* context) {
-  Profile* profile = Profile::FromBrowserContext(context);
-  return instance_id::InstanceIDProfileService::CreateForTests(
-      std::make_unique<FakeSyncInstanceIDDriver>(
-          gcm::GCMProfileServiceFactory::GetForProfile(profile)->driver()));
-}
-
 }  // namespace
 
 #if !BUILDFLAG(IS_ANDROID)
@@ -982,13 +973,6 @@
                               &profile_to_fcm_network_handler_map_));
   gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactory(
       context, base::BindRepeating(&FakeSyncGCMDriver::Build));
-
-  // Used by SharingService, real InstanceIDProfileService returns a real
-  // InstanceIDDriver. This factory prevents network requests when obtaining FCM
-  // registration tokens from real InstanceID.
-  instance_id::InstanceIDProfileServiceFactory::GetInstance()
-      ->SetTestingFactory(context,
-                          base::BindRepeating(&CreateInstanceIDProfileService));
 }
 
 // static
diff --git a/chrome/browser/sync/test/integration/sync_test.h b/chrome/browser/sync/test/integration/sync_test.h
index 419da4d1..2baa64a 100644
--- a/chrome/browser/sync/test/integration/sync_test.h
+++ b/chrome/browser/sync/test/integration/sync_test.h
@@ -36,6 +36,7 @@
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/test/base/android/android_browser_test.h"
+#include "components/gcm_driver/instance_id/scoped_use_fake_instance_id_android.h"
 #else
 #include "chrome/browser/extensions/install_verifier.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -476,6 +477,11 @@
       model_updater_factory_;
 #endif
 
+#if BUILDFLAG(IS_ANDROID)
+  instance_id::ScopedUseFakeInstanceIDAndroid
+      scoped_use_fake_instance_id_android_;
+#endif
+
   std::unique_ptr<fake_server::FakeServerSyncInvalidationSender>
       fake_server_sync_invalidation_sender_;
 };
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 0cf008e..b17fbedd 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3213,7 +3213,6 @@
       "//chrome/browser/ui/webui/ash/parent_access:proto",
       "//chrome/browser/ui/webui/ash/vm:mojo_bindings",
       "//chrome/browser/ui/webui/nearby_share:mojom",
-      "//chrome/browser/ui/webui/nearby_share/public/mojom",
       "//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom",
       "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings",
       "//chrome/browser/web_applications",
@@ -3282,6 +3281,7 @@
       "//chromeos/ash/services/multidevice_setup/public/cpp:android_sms_app_helper_delegate",
       "//chromeos/ash/services/multidevice_setup/public/cpp:prefs",
       "//chromeos/ash/services/multidevice_setup/public/cpp:url_provider",
+      "//chromeos/ash/services/nearby/public/mojom",
       "//chromeos/ash/services/recording/public/mojom",
       "//chromeos/components/onc",
       "//chromeos/components/quick_answers",
diff --git a/chrome/browser/ui/ash/cast_config_controller_media_router.cc b/chrome/browser/ui/ash/cast_config_controller_media_router.cc
index a39df8d..4eed6a8 100644
--- a/chrome/browser/ui/ash/cast_config_controller_media_router.cc
+++ b/chrome/browser/ui/ash/cast_config_controller_media_router.cc
@@ -61,10 +61,6 @@
   return router;
 }
 
-// "Cast for Education" extension uses this string and expects the client to
-// interpret it as "signed-in user's domain".
-constexpr char const kDefaultDomain[] = "default";
-
 }  // namespace
 
 // This class caches the values that the observers give us so we can query them
@@ -127,14 +123,6 @@
     if (sink.name().empty())
       continue;
 
-    // Hide all sinks which have a non-default domain (ie, castouts) to meet
-    // privacy requirements. This will be enabled once UI can display the
-    // domain. See crbug.com/624016.
-    if (sink.domain() && !sink.domain()->empty() &&
-        sink.domain() != kDefaultDomain) {
-      continue;
-    }
-
     sinks_.push_back(sink);
   }
 
@@ -229,7 +217,6 @@
     ash::SinkAndRoute device;
     device.sink.id = sink.id();
     device.sink.name = sink.name();
-    device.sink.domain = sink.domain().value_or(std::string());
     device.sink.sink_icon_type =
         static_cast<ash::SinkIconType>(sink.icon_type());
     devices_.push_back(std::move(device));
@@ -293,7 +280,6 @@
     ash::SinkAndRoute device;
     device.sink.id = "fake_sink_id_" + base::NumberToString(i);
     device.sink.name = "Fake Sink " + base::NumberToString(i);
-    device.sink.domain = "example.com";
     device.sink.sink_icon_type = ash::SinkIconType::kCast;
     devices_.push_back(std::move(device));
   }
diff --git a/chrome/browser/ui/ash/feature_discovery_duration_reporter_browsertest.cc b/chrome/browser/ui/ash/feature_discovery_duration_reporter_browsertest.cc
index bf2929fb..fbb54d3 100644
--- a/chrome/browser/ui/ash/feature_discovery_duration_reporter_browsertest.cc
+++ b/chrome/browser/ui/ash/feature_discovery_duration_reporter_browsertest.cc
@@ -12,6 +12,8 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/test/browser_test.h"
 
 namespace ash {
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc b/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc
index 96511a0..6b432836 100644
--- a/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc
+++ b/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc
@@ -21,6 +21,8 @@
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
index 1207e642..c02abe6d 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
@@ -54,6 +54,7 @@
 #include "chrome/browser/ui/ash/holding_space/holding_space_util.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "components/download/public/common/mock_download_item.h"
+#include "components/user_manager/user.h"
 #include "content/public/browser/download_item_utils.h"
 #include "content/public/browser/download_manager_delegate.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_util.cc b/chrome/browser/ui/ash/holding_space/holding_space_util.cc
index de8d33e..72c73cd5 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_util.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_util.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ui/ash/thumbnail_loader.h"
 #include "components/account_id/account_id.h"
+#include "components/user_manager/user.h"
 #include "storage/browser/file_system/file_system_context.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_operations.h"
diff --git a/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc b/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc
index 11f4c57..e11cc04 100644
--- a/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc
+++ b/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc
@@ -90,8 +90,11 @@
     CHECK(test_profile_manager_.SetUp());
     user_manager_ = std::make_unique<FakeChromeUserManager>();
     user_manager_->Initialize();
-    ProfileHelper::Get()->Initialize();
     task_environment_.RunUntilIdle();
+
+    // Initialize ProfileHelper.
+    // TODO(crbug.com/1325210): Migrate it into BrowserContextHelper.
+    ProfileHelper::Get();
   }
 
   void TearDown() override {
diff --git a/chrome/browser/ui/ash/projector/projector_utils.cc b/chrome/browser/ui/ash/projector/projector_utils.cc
index bf3a77f..24512e8 100644
--- a/chrome/browser/ui/ash/projector/projector_utils.cc
+++ b/chrome/browser/ui/ash/projector/projector_utils.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/web_applications/web_app_launch_queue.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index dd8b71ab..b48b4cd 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -97,8 +97,7 @@
 
 void AutofillPopupControllerImpl::Show(
     const std::vector<Suggestion>& suggestions,
-    AutoselectFirstSuggestion autoselect_first_suggestion,
-    PopupType popup_type) {
+    AutoselectFirstSuggestion autoselect_first_suggestion) {
   if (IsMouseLocked()) {
     Hide(PopupHidingReason::kMouseLocked);
     return;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
index ad8ec93a..1dc79b5 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
@@ -69,8 +69,7 @@
 
   // Shows the popup, or updates the existing popup with the given values.
   virtual void Show(const std::vector<Suggestion>& suggestions,
-                    AutoselectFirstSuggestion autoselect_first_suggestion,
-                    PopupType popup_type);
+                    AutoselectFirstSuggestion autoselect_first_suggestion);
 
   // Updates the data list values currently shown with the popup.
   virtual void UpdateDataListValues(const std::vector<std::u16string>& values,
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.h
index f11454b..5e2aa3f6 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.h
@@ -30,8 +30,7 @@
   // If the popup contains credit card items, find and set
   // |touchBarController_| and show the credit card autofill touch bar.
   void Show(const std::vector<autofill::Suggestion>& suggestions,
-            AutoselectFirstSuggestion autoselect_first_suggestion,
-            PopupType popup_type) override;
+            AutoselectFirstSuggestion autoselect_first_suggestion) override;
 
   // Updates the data list values currently shown with the popup. Calls
   // -invalidateTouchBar from |touchBarController_|.
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm
index 7bb6cf30..c48a905 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl_mac.mm
@@ -54,16 +54,14 @@
 
 void AutofillPopupControllerImplMac::Show(
     const std::vector<autofill::Suggestion>& suggestions,
-    AutoselectFirstSuggestion autoselect_first_suggestion,
-    PopupType popup_type) {
+    AutoselectFirstSuggestion autoselect_first_suggestion) {
   if (!suggestions.empty() && is_credit_card_popup_) {
     touch_bar_controller_ = [WebTextfieldTouchBarController
         controllerForWindow:[container_view().GetNativeNSView() window]];
     [touch_bar_controller_ showCreditCardAutofillWithController:this];
   }
 
-  AutofillPopupControllerImpl::Show(suggestions, autoselect_first_suggestion,
-                                    popup_type);
+  AutofillPopupControllerImpl::Show(suggestions, autoselect_first_suggestion);
   // No code below this line!
   // |Show| may hide the popup and destroy |this|, so |Show| should be the last
   // line.
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
index 9db07f7..72151e1 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
@@ -361,8 +361,8 @@
   std::vector<Suggestion> suggestions;
   suggestions.push_back(Suggestion("", "", "", 0));
   suggestions.push_back(Suggestion("", "", "", 0));
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
 
   EXPECT_FALSE(autofill_popup_controller_->selected_line());
   // Check that there are at least 2 values so that the first and last selection
@@ -384,8 +384,8 @@
   std::vector<Suggestion> suggestions;
   suggestions.push_back(Suggestion("", "", "", 0));
   suggestions.push_back(Suggestion("", "", "", 0));
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
 
   // Make sure that when a new line is selected, it is invalidated so it can
   // be updated to show it is selected.
@@ -412,8 +412,8 @@
   suggestions.push_back(Suggestion("", "", "", 1));
   suggestions.push_back(Suggestion("", "", "", 1));
   suggestions.push_back(Suggestion("", "", "", POPUP_ITEM_ID_AUTOFILL_OPTIONS));
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
 
   // Generate a popup, so it can be hidden later. It doesn't matter what the
   // external_delegate thinks is being shown in the process, since we are just
@@ -455,8 +455,8 @@
   // Set up the popup.
   std::vector<Suggestion> suggestions;
   suggestions.push_back(Suggestion("", "", "", 1));
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
 
   // Generate a popup.
   test::GenerateTestAutofillPopup(external_delegate_.get());
@@ -485,8 +485,8 @@
   suggestions.push_back(Suggestion("", "", "", 1));
   suggestions.push_back(Suggestion("", "", "", POPUP_ITEM_ID_SEPARATOR));
   suggestions.push_back(Suggestion("", "", "", POPUP_ITEM_ID_AUTOFILL_OPTIONS));
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
 
   autofill_popup_controller_->SetSelectedLine(0);
 
@@ -505,8 +505,8 @@
   suggestions.push_back(Suggestion("", "", "", POPUP_ITEM_ID_SEPARATOR));
   suggestions.push_back(Suggestion(
       "", "", "", POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE));
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
 
   // Make sure previous skips the unselectable form warning when there is no
   // selection.
@@ -525,8 +525,8 @@
 TEST_F(AutofillPopupControllerUnitTest, UpdateDataListValues) {
   std::vector<Suggestion> suggestions;
   suggestions.push_back(Suggestion("", "", "", 1));
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
 
   // Add one data list entry.
   std::u16string value1 = u"data list value 1";
@@ -609,8 +609,8 @@
   // Create the popup with a single datalist element.
   std::vector<Suggestion> suggestions;
   suggestions.push_back(Suggestion("", "", "", POPUP_ITEM_ID_DATALIST_ENTRY));
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
 
   // Replace the datalist element with a new one.
   std::u16string value1 = u"data list value 1";
@@ -705,8 +705,7 @@
   std::vector<Suggestion> suggestions;
   suggestions.push_back(Suggestion("", "", "", 0));
   suggestions.push_back(Suggestion("", "", "", 0));
-  popup_controller()->Show(suggestions, AutoselectFirstSuggestion(false),
-                           PopupType::kUnspecified);
+  popup_controller()->Show(suggestions, AutoselectFirstSuggestion(false));
   popup_controller()->SetSelectedLine(0);
 
   // Now show a new popup with the same controller, but with fewer items.
@@ -882,8 +881,8 @@
       Suggestion("value", "", "", 1),
       Suggestion("", "", "", POPUP_ITEM_ID_SEPARATOR),
       Suggestion("", "", "", POPUP_ITEM_ID_AUTOFILL_OPTIONS)};
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
   // Select the autofill suggestion.
   autofill_popup_controller_->SetSelectedLine(0);
 
@@ -905,8 +904,8 @@
       Suggestion("value", "", "", 1),
       Suggestion("", "", "", POPUP_ITEM_ID_SEPARATOR),
       Suggestion("", "", "", POPUP_ITEM_ID_AUTOFILL_OPTIONS)};
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
   // Select the POPUP_ITEM_ID_AUTOFILL_OPTIONS line.
   autofill_popup_controller_->SetSelectedLine(2);
 
@@ -926,8 +925,8 @@
       Suggestion("value", "", "", 1),
       Suggestion("", "", "", POPUP_ITEM_ID_SEPARATOR),
       Suggestion("", "", "", POPUP_ITEM_ID_AUTOFILL_OPTIONS)};
-  autofill_popup_controller_->Show(
-      suggestions, AutoselectFirstSuggestion(false), PopupType::kUnspecified);
+  autofill_popup_controller_->Show(suggestions,
+                                   AutoselectFirstSuggestion(false));
 
   // autofill_popup_controller_->SetSelectedLine(...); is not called here to
   // produce the edge case.
@@ -943,8 +942,7 @@
 TEST_F(AutofillPopupControllerUnitTest, SelectInvalidSuggestion) {
   // Set up the popup.
   std::vector<Suggestion> suggestions = {Suggestion("value", "", "", 1)};
-  popup_controller()->Show(suggestions, AutoselectFirstSuggestion(false),
-                           PopupType::kUnspecified);
+  popup_controller()->Show(suggestions, AutoselectFirstSuggestion(false));
 
   EXPECT_CALL(*delegate(), DidAcceptSuggestion).Times(0);
 
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 0046d9b..6d7b0c4 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -777,8 +777,7 @@
       open_args.text_direction);
 
   popup_controller_->Show(open_args.suggestions,
-                          open_args.autoselect_first_suggestion,
-                          open_args.popup_type);
+                          open_args.autoselect_first_suggestion);
 
   // When testing, try to keep popup open when the reason to hide is from an
   // external browser frame resize that is extraneous to our testing goals.
@@ -820,8 +819,7 @@
       screen_space_independent_bounds,
       controller->IsRTL() ? base::i18n::RIGHT_TO_LEFT
                           : base::i18n::LEFT_TO_RIGHT,
-      controller->GetSuggestions(), AutoselectFirstSuggestion(false),
-      controller->GetPopupType());
+      controller->GetSuggestions(), AutoselectFirstSuggestion(false));
 }
 
 void ChromeAutofillClient::UpdatePopup(
@@ -842,8 +840,7 @@
   }
 
   // Calling show will reuse the existing view automatically
-  popup_controller_->Show(suggestions, AutoselectFirstSuggestion(false),
-                          popup_type);
+  popup_controller_->Show(suggestions, AutoselectFirstSuggestion(false));
 }
 
 void ChromeAutofillClient::HideAutofillPopup(PopupHidingReason reason) {
diff --git a/chrome/browser/ui/media_router/media_router_ui.cc b/chrome/browser/ui/media_router/media_router_ui.cc
index 535eb0b..1865115f 100644
--- a/chrome/browser/ui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/media_router/media_router_ui.cc
@@ -62,15 +62,6 @@
           issue.info().route_id == ui_sink.route->media_route_id());
 }
 
-std::u16string GetSinkFriendlyName(const MediaSink& sink) {
-  // Use U+2010 (HYPHEN) instead of ASCII hyphen to avoid problems with RTL
-  // languages.
-  const char* separator = " \u2010 ";
-  return base::UTF8ToUTF16(sink.description() ? sink.name() + separator +
-                                                    sink.description().value()
-                                              : sink.name());
-}
-
 void MaybeReportCastingSource(MediaCastMode cast_mode,
                               const RouteRequestResult& result) {
   if (result.result_code() == mojom::RouteRequestResultCode::OK)
@@ -424,7 +415,7 @@
     const MediaSink::Id& sink_id) {
   for (const MediaSinkWithCastModes& sink : GetEnabledSinks()) {
     if (sink.sink.id() == sink_id) {
-      return GetSinkFriendlyName(sink.sink);
+      return base::UTF8ToUTF16(sink.sink.name());
     }
   }
   return std::u16string(u"Device");
@@ -595,7 +586,7 @@
                                            const absl::optional<Issue>& issue) {
   UIMediaSink ui_sink{sink.sink.provider_id()};
   ui_sink.id = sink.sink.id();
-  ui_sink.friendly_name = GetSinkFriendlyName(sink.sink);
+  ui_sink.friendly_name = base::UTF8ToUTF16(sink.sink.name());
   ui_sink.icon_type = sink.sink.icon_type();
   ui_sink.cast_modes = sink.cast_modes;
 
diff --git a/chrome/browser/ui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
index 2ed86dc..2cc243d1 100644
--- a/chrome/browser/ui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
@@ -56,7 +56,6 @@
 namespace {
 
 constexpr char kRouteId[] = "route1";
-constexpr char kSinkDescription[] = "description";
 constexpr char kSinkId[] = "sink1";
 constexpr char kSinkName[] = "sink name";
 constexpr char kSourceId[] = "source1";
@@ -304,13 +303,10 @@
   NiceMock<MockControllerObserver> observer(ui_.get());
 
   MediaSink sink{CreateCastSink(kSinkId, kSinkName)};
-  sink.set_description(kSinkDescription);
   MediaSinkWithCastModes sink_with_cast_modes(sink);
-  const char* separator = " \u2010 ";
   EXPECT_CALL(observer, OnModelUpdated(_))
       .WillOnce(Invoke([&](const CastDialogModel& model) {
-        EXPECT_EQ(base::UTF8ToUTF16(sink.name() + separator +
-                                    sink.description().value()),
+        EXPECT_EQ(base::UTF8ToUTF16(sink.name()),
                   model.media_sinks()[0].friendly_name);
       }));
   NotifyUiOnSinksUpdated({sink_with_cast_modes});
diff --git a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
index 7c6e2f5d..4fa1c54 100644
--- a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
@@ -368,8 +368,8 @@
 
   auto cvc_image = std::make_unique<views::ImageView>();
   cvc_image->SetImage(rb.GetImageSkiaNamed(controller_->GetCvcImageRid()));
-  cvc_image->SetTooltipText(l10n_util::GetStringUTF16(
-      IDS_AUTOFILL_CARD_UNMASK_CVC_IMAGE_DESCRIPTION));
+  cvc_image->SetTooltipText(
+      l10n_util::GetStringUTF16(controller_->GetCvcTooltipResourceId()));
   input_row->AddChildView(std::move(cvc_image));
   input_row_ = input_container->AddChildView(std::move(input_row));
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
index 90e15e9..9e68b488 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
@@ -73,6 +73,8 @@
                                               bool show) override;
   void OnAppRegistrarDestroyed() override;
 
+  gfx::Insets GetCaptionButtonInsets() const;
+
  protected:
   // views::View:
   void OnPaint(gfx::Canvas* canvas) override;
@@ -86,21 +88,18 @@
   FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewMacTest,
                            GetCaptionButtonPlaceholderBounds);
 
-  static gfx::Rect GetCenteredTitleBounds(int frame_width,
-                                          int frame_height,
-                                          int left_inset_x,
-                                          int right_inset_x,
-                                          int title_width);
+  static gfx::Rect GetCenteredTitleBounds(gfx::Rect frame,
+                                          gfx::Rect available_space,
+                                          int preferred_title_width);
 
   static gfx::Rect GetWebAppFrameToolbarAvailableBounds(
       bool is_rtl,
       const gfx::Size& frame,
       int y,
       int caption_button_container_width);
-  static gfx::Rect GetCaptionButtonPlaceholderBounds(bool is_rtl,
-                                                     const gfx::Size& frame,
-                                                     int y,
-                                                     int width);
+  static gfx::Rect GetCaptionButtonPlaceholderBounds(
+      const gfx::Rect& frame,
+      const gfx::Insets& caption_button_insets);
 
   void PaintThemedFrame(gfx::Canvas* canvas);
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
index 925bf50c..856df00 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
@@ -44,8 +44,6 @@
 
 namespace {
 
-constexpr int kFrameExtraPaddingForWindowControlsOverlay = 10;
-constexpr int kFramePaddingLeft = 75;
 // Keep in sync with web_app_frame_toolbar_browsertest.cc
 constexpr double kTitlePaddingWidthFraction = 0.1;
 
@@ -143,6 +141,9 @@
 }
 
 bool BrowserNonClientFrameViewMac::CaptionButtonsOnLeadingEdge() const {
+  // TODO(https://crbug.com/860627): In "partial" RTL mode (where the OS is in
+  // LTR mode while Chrome is in RTL mode, or vice versa) this should return
+  // false rather than true.
   return true;
 }
 
@@ -158,11 +159,7 @@
 
   // Do not draw caption buttons on fullscreen.
   if (!frame()->IsFullscreen()) {
-    const int kCaptionWidth = base::mac::IsAtMostOS10_15() ? 70 : 85;
-    if (CaptionButtonsOnLeadingEdge())
-      bounds.Inset(gfx::Insets::TLBR(0, kCaptionWidth, 0, 0));
-    else
-      bounds.Inset(gfx::Insets::TLBR(0, 0, 0, kCaptionWidth));
+    bounds.Inset(GetCaptionButtonInsets());
   }
 
   return bounds;
@@ -449,6 +446,15 @@
     BrowserNonClientFrameView::PaintChildren(info);
 }
 
+gfx::Insets BrowserNonClientFrameViewMac::GetCaptionButtonInsets() const {
+  const int kCaptionWidth = base::mac::IsAtMostOS10_15() ? 70 : 85;
+  if (CaptionButtonsOnLeadingEdge()) {
+    return gfx::Insets::TLBR(0, kCaptionWidth, 0, 0);
+  } else {
+    return gfx::Insets::TLBR(0, 0, 0, kCaptionWidth);
+  }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // BrowserNonClientFrameViewMac, protected:
 
@@ -487,24 +493,16 @@
 // BrowserNonClientFrameViewMac, private:
 
 gfx::Rect BrowserNonClientFrameViewMac::GetCenteredTitleBounds(
-    int frame_width,
-    int frame_height,
-    int left_inset_x,
-    int right_inset_x,
-    int title_width) {
+    gfx::Rect frame,
+    gfx::Rect available_space,
+    int preferred_title_width) {
   // Center in container.
-  int title_x = (frame_width - title_width) / 2;
+  frame.ClampToCenteredSize(gfx::Size(preferred_title_width, frame.height()));
 
-  // Align right side to right inset if overlapping.
-  title_x = std::min(title_x, right_inset_x - title_width);
+  // Make it fit in available space.
+  frame.AdjustToFit(available_space);
 
-  // Align left side to left inset if overlapping.
-  title_x = std::max(title_x, left_inset_x);
-
-  // Clip width to right inset if overlapping.
-  title_width = std::min(title_width, right_inset_x - title_x);
-
-  return gfx::Rect(title_x, 0, title_width, frame_height);
+  return frame;
 }
 
 void BrowserNonClientFrameViewMac::PaintThemedFrame(gfx::Canvas* canvas) {
@@ -556,27 +554,18 @@
   if (!web_app_frame_toolbar())
     return;
 
-  const int available_height = GetTopInset(true);
-  int leading_x = kFramePaddingLeft;
-  int trailing_x = width();
-
-  if (CaptionButtonsOnLeadingEdge() && base::i18n::IsRTL()) {
-    leading_x = 0;
-    trailing_x = width() - kFramePaddingLeft;
-  }
-
-  std::pair<int, int> remaining_bounds =
-      web_app_frame_toolbar()->LayoutInContainer(leading_x, trailing_x, 0,
-                                                 available_height);
-  leading_x = remaining_bounds.first;
-  trailing_x = remaining_bounds.second;
+  gfx::Rect title_bar_bounds(0, 0, width(), GetTopInset(true));
+  gfx::Rect available_space = title_bar_bounds;
+  available_space.Inset(GetCaptionButtonInsets());
+  available_space = web_app_frame_toolbar()->LayoutInContainer(available_space);
 
   const int title_padding =
       base::checked_cast<int>(std::round(width() * kTitlePaddingWidthFraction));
-  window_title_->SetBoundsRect(GetCenteredTitleBounds(
-      width(), available_height, leading_x + title_padding,
-      trailing_x - title_padding,
-      window_title_->CalculatePreferredSize().width()));
+  available_space.Inset(gfx::Insets::TLBR(0, title_padding, 0, title_padding));
+
+  window_title_->SetBoundsRect(
+      GetCenteredTitleBounds(title_bar_bounds, available_space,
+                             window_title_->CalculatePreferredSize().width()));
 }
 
 gfx::Rect BrowserNonClientFrameViewMac::GetWebAppFrameToolbarAvailableBounds(
@@ -595,67 +584,51 @@
 }
 
 gfx::Rect BrowserNonClientFrameViewMac::GetCaptionButtonPlaceholderBounds(
-    bool is_rtl,
-    const gfx::Size& frame,
-    int y,
-    int width) {
-  gfx::Rect bounds(0, y, width, frame.height());
-  if (is_rtl)
-    bounds.set_x(frame.width() - bounds.width());
-
+    const gfx::Rect& frame,
+    const gfx::Insets& caption_button_insets) {
+  DCHECK(caption_button_insets.left() == 0 ||
+         caption_button_insets.right() == 0);
+  gfx::Rect non_caption_bounds = frame;
+  non_caption_bounds.Inset(caption_button_insets);
+  gfx::Rect bounds = frame;
+  bounds.Subtract(non_caption_bounds);
   return bounds;
 }
 
 void BrowserNonClientFrameViewMac::LayoutWindowControlsOverlay() {
-  const bool is_rtl = CaptionButtonsOnLeadingEdge() && base::i18n::IsRTL();
-  const gfx::Size frame(width(), GetTopInset(false));
+  gfx::Rect frame_available_bounds(0, 0, width(), GetTopInset(false));
 
   // Pad the width of caption_button_placeholder_container so the button on the
   // inner edge doesn't look like it's touching the overlay, but rather has a
   // little bit of space between them.
+  gfx::Insets caption_button_insets = GetCaptionButtonInsets();
   gfx::Rect caption_button_container_bounds = GetCaptionButtonPlaceholderBounds(
-      is_rtl, frame, 0,
-      kFramePaddingLeft + kFrameExtraPaddingForWindowControlsOverlay);
-
-  gfx::Rect web_app_frame_toolbar_available_bounds =
-      GetWebAppFrameToolbarAvailableBounds(
-          is_rtl, frame, 0, caption_button_container_bounds.width());
+      frame_available_bounds, caption_button_insets);
 
   // Layout CaptionButtonPlaceholderContainer which would have the traffic
   // lights.
   caption_button_placeholder_container_->SetBoundsRect(
       caption_button_container_bounds);
 
-  // Layout WebAppFrameToolbarView.
+  // Remove caption buttons from remaining available bounds, and layout
+  // WebAppFrameToolbarView.
+  frame_available_bounds.Inset(caption_button_insets);
   web_app_frame_toolbar()->LayoutForWindowControlsOverlay(
-      web_app_frame_toolbar_available_bounds);
+      frame_available_bounds);
 
   content::WebContents* web_contents = browser_view()->GetActiveWebContents();
   // WebContents can be null when an app window is first launched.
   if (web_contents) {
-    const int overlay_width =
-        width() - (caption_button_placeholder_container_->size().width() +
-                   web_app_frame_toolbar()->size().width());
-    const int overlay_height = GetTopInset(false);
-    gfx::Rect bounding_rect;
+    // Subtract WebAppFrameToolbarView from remaining available bounds to
+    // determine space available for web contents.
+    frame_available_bounds.Subtract(web_app_frame_toolbar()->bounds());
 
-    if (is_rtl) {
-      bounding_rect =
-          gfx::Rect(caption_button_placeholder_container_->size().width() +
-                        web_app_frame_toolbar()->size().width(),
-                    0, overlay_width, overlay_height);
+    if (frame_available_bounds.IsEmpty()) {
+      web_contents->UpdateWindowControlsOverlay(gfx::Rect());
     } else {
-      bounding_rect = GetMirroredRect(
-          gfx::Rect(caption_button_placeholder_container_->size().width(), 0,
-                    overlay_width, overlay_height));
+      web_contents->UpdateWindowControlsOverlay(
+          GetMirroredRect(frame_available_bounds));
     }
-
-    // In the case where ShouldHideTopUIForFullscreen() returns true, height
-    // goes to 0 so the rest of bounding_rect values need to be reset as well.
-    if (bounding_rect.height() == 0)
-      bounding_rect = gfx::Rect();
-
-    web_contents->UpdateWindowControlsOverlay(bounding_rect);
   }
 
   // WebAppFrameToolbarView visible property needs to be explicitly shown based
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac_unittest.mm b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac_unittest.mm
index 95795aea..9179d8b 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac_unittest.mm
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac_unittest.mm
@@ -28,11 +28,13 @@
   int index = 0;
   for (const auto& test_case : test_cases) {
     SCOPED_TRACE(base::StringPrintf("\nTest case index: %d", index));
+    gfx::Rect frame(0, 0, test_case.frame_width, test_case.frame_height);
+    gfx::Rect available_space(test_case.left_inset_x, 0,
+                              test_case.right_inset_x - test_case.left_inset_x,
+                              test_case.frame_height);
     gfx::Rect title_bounds =
         BrowserNonClientFrameViewMac::GetCenteredTitleBounds(
-            test_case.frame_width, test_case.frame_height,
-            test_case.left_inset_x, test_case.right_inset_x,
-            test_case.title_width);
+            frame, available_space, test_case.title_width);
     gfx::Rect expected_title_bounds =
         gfx::Rect(test_case.expected_title_x, 0, test_case.expected_title_width,
                   test_case.frame_height);
@@ -42,23 +44,20 @@
 }
 
 TEST(BrowserNonClientFrameViewMacTest, GetCaptionButtonPlaceholderBounds) {
-  const gfx::Size frame(800, 40);
+  const gfx::Rect frame(0, 0, 800, 40);
   const int width = 85;  // 75 + 10 (padding)
-  const int y = 0;
 
-  const gfx::Rect ltr_bounds =
+  const gfx::Rect leading_bounds =
       BrowserNonClientFrameViewMac::GetCaptionButtonPlaceholderBounds(
-          false /* is_rtl */, frame, y, width);
-  const gfx::Rect expected_ltr_bounds = gfx::Rect(0, 0, 85, 40);
+          frame, gfx::Insets::TLBR(0, width, 0, 0));
+  const gfx::Rect expected_leading_bounds = gfx::Rect(0, 0, 85, 40);
+  EXPECT_EQ(leading_bounds, expected_leading_bounds);
 
-  EXPECT_EQ(ltr_bounds, expected_ltr_bounds);
-
-  const gfx::Rect rtl_bounds =
+  const gfx::Rect trailing_bounds =
       BrowserNonClientFrameViewMac::GetCaptionButtonPlaceholderBounds(
-          true /* is_rtl */, frame, y, width);
-  const gfx::Rect expected_rtl_bounds = gfx::Rect(715, 0, 85, 40);
-
-  EXPECT_EQ(rtl_bounds, expected_rtl_bounds);
+          frame, gfx::Insets::TLBR(0, 0, 0, width));
+  const gfx::Rect expected_trailing_bounds = gfx::Rect(715, 0, 85, 40);
+  EXPECT_EQ(trailing_bounds, expected_trailing_bounds);
 }
 
 TEST(BrowserNonClientFrameViewMacTest, GetWebAppFrameToolbarAvailableBounds) {
diff --git a/chrome/browser/ui/views/side_panel/customize_chrome/OWNERS b/chrome/browser/ui/views/side_panel/customize_chrome/OWNERS
new file mode 100644
index 0000000..51e7853
--- /dev/null
+++ b/chrome/browser/ui/views/side_panel/customize_chrome/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/resources/new_tab_page/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
index c9ef122e..b0fdebe7 100644
--- a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
@@ -105,7 +105,7 @@
       SidePanelEntry::Id::kUserNote,
       l10n_util::GetStringUTF16(IDS_USER_NOTE_TITLE),
       ui::ImageModel::FromVectorIcon(kInkHighlighterIcon, ui::kColorIcon),
-      base::BindRepeating(&UserNoteUICoordinator::CreateUserNotesView,
+      base::BindRepeating(&UserNoteUICoordinator::CreateUserNotesWebUIView,
                           base::Unretained(this)));
   entry->AddObserver(this);
   global_registry->Register(std::move(entry));
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc
index 06122c49..063a9ad6 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc
@@ -136,31 +136,33 @@
     int trailing_x,
     int y,
     int available_height) {
+  gfx::Rect center_bounds = LayoutInContainer(gfx::Rect(
+      leading_x, y, base::ClampSub(trailing_x, leading_x), available_height));
+  return std::pair<int, int>(center_bounds.x(), center_bounds.right());
+}
+
+gfx::Rect WebAppFrameToolbarView::LayoutInContainer(gfx::Rect available_space) {
   DCHECK(!browser_view_->IsWindowControlsOverlayEnabled());
 
-  SetVisible(available_height > 0);
-
-  if (available_height == 0) {
+  SetVisible(!available_space.IsEmpty());
+  if (available_space.IsEmpty()) {
     SetSize(gfx::Size());
-    return std::pair<int, int>(0, 0);
+    return gfx::Rect();
   }
 
-  gfx::Size preferred_size = GetPreferredSize();
-  const int width = std::max(trailing_x - leading_x, 0);
-  const int height = preferred_size.height();
-  DCHECK_LE(height, available_height);
-  SetBounds(leading_x, y, width, available_height);
+  DCHECK_LE(GetPreferredSize().height(), available_space.height());
+  SetBoundsRect(available_space);
   Layout();
 
-  if (!center_container_->GetVisible())
-    return std::pair<int, int>(0, 0);
+  if (!center_container_->GetVisible()) {
+    return gfx::Rect();
+  }
 
   // Bounds for remaining inner space, in parent container coordinates.
   gfx::Rect center_bounds = center_container_->bounds();
   DCHECK(center_bounds.x() == 0 || left_container_);
   center_bounds.Offset(bounds().OffsetFromOrigin());
-
-  return std::pair<int, int>(center_bounds.x(), center_bounds.right());
+  return center_bounds;
 }
 
 void WebAppFrameToolbarView::LayoutForWindowControlsOverlay(
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h
index 2ad80a0..a6c8031 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h
@@ -55,6 +55,7 @@
                                         int trailing_x,
                                         int y,
                                         int available_height);
+  gfx::Rect LayoutInContainer(gfx::Rect available_space);
 
   // Sets own bounds within the available_space.
   void LayoutForWindowControlsOverlay(gfx::Rect available_space);
diff --git a/chrome/browser/ui/web_applications/DEPS b/chrome/browser/ui/web_applications/DEPS
new file mode 100644
index 0000000..2240ada
--- /dev/null
+++ b/chrome/browser/ui/web_applications/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "web_app_interactive_uitest\.cc": [
+    "+ash/shell.h",
+  ],
+}
diff --git a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
index 5ada2fb..32bbeed2 100644
--- a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
+++ b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
@@ -61,6 +61,7 @@
 #include "components/omnibox/browser/omnibox_edit_model.h"
 #include "components/omnibox/browser/omnibox_view.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ui/web_applications/web_app_interactive_uitest.cc b/chrome/browser/ui/web_applications/web_app_interactive_uitest.cc
new file mode 100644
index 0000000..288c2f2
--- /dev/null
+++ b/chrome/browser/ui/web_applications/web_app_interactive_uitest.cc
@@ -0,0 +1,129 @@
+// 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 <string>
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/scoped_display_for_new_windows.h"
+#include "ui/display/screen.h"
+#include "ui/display/screen_base.h"
+#include "ui/display/test/scoped_screen_override.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/shell.h"
+#include "ui/display/test/display_manager_test_api.h"
+#endif
+
+#if BUILDFLAG(IS_MAC)
+#include "ui/display/mac/test/virtual_display_mac_util.h"
+#endif
+
+namespace web_app {
+namespace {
+constexpr const char kExampleURL[] = "http://example.org/";
+}
+
+class WebAppInteractiveUiTest : public WebAppControllerBrowserTest {};
+
+// Disabled everywhere except ChromeOS and Mac because those are the only
+// platforms with functional display mocking at the moment. While a partial
+// solution is possible using display::Screen::SetScreenInstance on other
+// platforms, window placement doesn't work right with a faked Screen
+// instance.
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
+#define MAYBE_TabOpensOnCorrectDisplay TabOpensOnCorrectDisplay
+#else
+#define MAYBE_TabOpensOnCorrectDisplay DISABLED_TabOpensOnCorrectDisplay
+#endif
+// Tests that PWAs that open in a tab open tabs on the correct display.
+IN_PROC_BROWSER_TEST_F(WebAppInteractiveUiTest,
+                       MAYBE_TabOpensOnCorrectDisplay) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("801x802,802x802");
+#elif BUILDFLAG(IS_MAC)
+  if (!display::test::VirtualDisplayMacUtil::IsAPIAvailable()) {
+    GTEST_SKIP() << "Skipping test for unsupported MacOS version.";
+  }
+  display::test::VirtualDisplayMacUtil virtual_display_mac_util;
+  virtual_display_mac_util.AddDisplay(
+      1, display::test::VirtualDisplayMacUtil::k1920x1080);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+  // Install test app.
+  const AppId app_id = InstallPWA(GURL(kExampleURL));
+
+  // Figure out what display the original tabbed browser was created on, as well
+  // as what the display Id is for a second display.
+  Browser* original_browser = browser();
+  const std::vector<display::Display>& displays =
+      display::Screen::GetScreen()->GetAllDisplays();
+  display::Display original_display =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(
+          original_browser->window()->GetNativeWindow());
+  display::Display other_display;
+  for (const auto& d : displays) {
+    if (d.id() != original_display.id()) {
+      other_display = d;
+      break;
+    }
+  }
+  ASSERT_TRUE(other_display.is_valid());
+
+  // By default opening a PWA in a tab should open on the same display as the
+  // existing tabbed browser, and thus in the same browser window.
+  Browser* app_browser = LaunchBrowserForWebAppInTab(app_id);
+  EXPECT_EQ(app_browser, original_browser);
+  EXPECT_EQ(2, original_browser->tab_strip_model()->count());
+
+  {
+    // Setting the display for new windows only effects what display a browser
+    // window is opened on on Chrome OS. On other platforms a new tab should
+    // be opened in the existing window regardless of what display we set for
+    // new windows.
+    display::ScopedDisplayForNewWindows scoped_display(other_display.id());
+    app_browser = LaunchBrowserForWebAppInTab(app_id);
+
+#if BUILDFLAG(IS_CHROMEOS)
+    EXPECT_NE(app_browser, original_browser);
+    EXPECT_EQ(
+        other_display.id(),
+        display::Screen::GetScreen()
+            ->GetDisplayNearestWindow(app_browser->window()->GetNativeWindow())
+            .id());
+    EXPECT_EQ(2, original_browser->tab_strip_model()->count());
+    EXPECT_EQ(1, app_browser->tab_strip_model()->count());
+
+    // A second launch should re-use the same browser window.
+    Browser* app_browser2 = LaunchBrowserForWebAppInTab(app_id);
+    EXPECT_EQ(app_browser, app_browser2);
+    EXPECT_EQ(2, original_browser->tab_strip_model()->count());
+    EXPECT_EQ(2, app_browser->tab_strip_model()->count());
+#else
+    EXPECT_EQ(app_browser, original_browser);
+    EXPECT_EQ(3, original_browser->tab_strip_model()->count());
+#endif
+  }
+
+  {
+    // Forcing the app to launch on the original display should open a new tab
+    // in the original browser.
+    display::ScopedDisplayForNewWindows scoped_display(original_display.id());
+    app_browser = LaunchBrowserForWebAppInTab(app_id);
+    EXPECT_EQ(app_browser, original_browser);
+#if BUILDFLAG(IS_CHROMEOS)
+    EXPECT_EQ(3, original_browser->tab_strip_model()->count());
+#else
+    EXPECT_EQ(4, original_browser->tab_strip_model()->count());
+#endif
+  }
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_launch_process.cc b/chrome/browser/ui/web_applications/web_app_launch_process.cc
index 198b150..f836737 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_process.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_process.cc
@@ -88,7 +88,10 @@
   }
 
   // Place new windows on the specified display.
-  display::ScopedDisplayForNewWindows scoped_display(params_->display_id);
+  absl::optional<display::ScopedDisplayForNewWindows> scoped_display;
+  if (params_->display_id != display::kInvalidDisplayId) {
+    scoped_display.emplace(params_->display_id);
+  }
 
   const apps::ShareTarget* share_target = MaybeGetShareTarget();
   auto [launch_url, is_file_handling] = GetLaunchUrl(share_target);
@@ -252,14 +255,21 @@
 
 Browser* WebAppLaunchProcess::MaybeFindBrowserForLaunch() const {
   if (params_->container == apps::LaunchContainer::kLaunchContainerTab) {
-    // If launching the app in the current tab, find the most recently used
-    // browser for the current profile, rather than limiting the search to
-    // windows on whatever screen we would want to open new windows.
+    // In general, when opening a web application in a tab, we want to open the
+    // application in a tab in the most recently used browser window.
+    // Chrome OS however prefers opening new tabs in windows on a specific
+    // display, specifically the one returned by GetDisplayForNewWindows(), even
+    // if no browser windows are currently open on that display (except when
+    // we're specifically opening the app in the current tab, rather than a new
+    // tab).
+    int64_t display_id = display::kInvalidDisplayId;
+#if BUILDFLAG(IS_CHROMEOS)
+    if (params_->disposition != WindowOpenDisposition::CURRENT_TAB) {
+      display_id = display::Screen::GetScreen()->GetDisplayForNewWindows().id();
+    }
+#endif
     return chrome::FindTabbedBrowser(
-        &profile_.get(), /*match_original_profiles=*/false,
-        params_->disposition == WindowOpenDisposition::CURRENT_TAB
-            ? display::kInvalidDisplayId
-            : display::Screen::GetScreen()->GetDisplayForNewWindows().id());
+        &profile_.get(), /*match_original_profiles=*/false, display_id);
   }
 
   if (!registrar_->IsTabbedWindowModeEnabled(params_->app_id) &&
diff --git a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
index 689ecbec..41197c14 100644
--- a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -23,6 +23,7 @@
 #include "components/history_clusters/core/features.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/search/ntp_features.h"
+#include "components/user_notes/user_notes_features.h"
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/notification_registrar.h"
@@ -166,6 +167,7 @@
     enabled_features.push_back(
         password_manager::features::kPasswordManagerRedesign);
     enabled_features.push_back(features::kReadAnything);
+    enabled_features.push_back(user_notes::kUserNotes);
 
 #if !BUILDFLAG(IS_CHROMEOS)
     if (GetParam() == std::string("chrome://welcome"))
diff --git a/chrome/browser/ui/webui/nearby_share/BUILD.gn b/chrome/browser/ui/webui/nearby_share/BUILD.gn
index 8bd899a..14686d4 100644
--- a/chrome/browser/ui/webui/nearby_share/BUILD.gn
+++ b/chrome/browser/ui/webui/nearby_share/BUILD.gn
@@ -15,7 +15,7 @@
 
   public_deps = [
     ":share_type",
-    "//chromeos/ash/services/nearby/public/mojom:nearby_share_target_types",
+    "//chromeos/ash/services/nearby/public/mojom:nearby_share_settings",
     "//mojo/public/mojom/base",
     "//url/mojom:url_mojom_gurl",
   ]
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
index 46ddd5f..127770f 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h
@@ -11,8 +11,8 @@
 #include "chrome/browser/nearby_sharing/attachment.h"
 #include "chrome/browser/sharesheet/sharesheet_controller.h"
 #include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/webui_config.h"
 #include "content/public/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/nearby_share/public/mojom/BUILD.gn b/chrome/browser/ui/webui/nearby_share/public/mojom/BUILD.gn
deleted file mode 100644
index f4d5705..0000000
--- a/chrome/browser/ui/webui/nearby_share/public/mojom/BUILD.gn
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright 2020 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("mojom") {
-  sources = [ "nearby_share_settings.mojom" ]
-
-  public_deps = [
-    "//mojo/public/mojom/base",
-    "//url/mojom:url_mojom_gurl",
-  ]
-}
diff --git a/chrome/browser/ui/webui/nearby_share/public/mojom/OWNERS b/chrome/browser/ui/webui/nearby_share/public/mojom/OWNERS
deleted file mode 100644
index 08850f4..0000000
--- a/chrome/browser/ui/webui/nearby_share/public/mojom/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc b/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
index d553833c..5a075a7 100644
--- a/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
@@ -55,6 +55,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h"
+#include "components/user_manager/user.h"
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chromeos/crosapi/mojom/drive_integration_service.mojom.h"
 #include "chromeos/crosapi/mojom/holding_space_service.mojom.h"
diff --git a/chrome/browser/ui/webui/settings/ash/BUILD.gn b/chrome/browser/ui/webui/settings/ash/BUILD.gn
index ffc2ee35..105f9c7 100644
--- a/chrome/browser/ui/webui/settings/ash/BUILD.gn
+++ b/chrome/browser/ui/webui/settings/ash/BUILD.gn
@@ -8,10 +8,10 @@
 
 group("mojom_js") {
   public_deps = [
-    "//chrome/browser/ui/webui/nearby_share/public/mojom:mojom_js",
     "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js",
     "//chrome/browser/ui/webui/settings/chromeos/constants:mojom_js",
-    "//chromeos/ash/components/audio/public/mojom/:mojom_js",
+    "//chromeos/ash/components/audio/public/mojom:mojom_js",
+    "//chromeos/ash/services/nearby/public/mojom:mojom_js",
   ]
 }
 
diff --git a/chrome/browser/ui/webui/settings/ash/internet_handler.cc b/chrome/browser/ui/webui/settings/ash/internet_handler.cc
index c5bce99..ff94b1f 100644
--- a/chrome/browser/ui/webui/settings/ash/internet_handler.cc
+++ b/chrome/browser/ui/webui/settings/ash/internet_handler.cc
@@ -27,6 +27,7 @@
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "components/onc/onc_constants.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
diff --git a/chrome/browser/ui/webui/settings/ash/multidevice_section.h b/chrome/browser/ui/webui/settings/ash/multidevice_section.h
index 41b61713..3cdd1d2 100644
--- a/chrome/browser/ui/webui/settings/ash/multidevice_section.h
+++ b/chrome/browser/ui/webui/settings/ash/multidevice_section.h
@@ -7,9 +7,9 @@
 
 #include "ash/webui/eche_app_ui/eche_app_manager.h"
 #include "base/values.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "chrome/browser/ui/webui/settings/ash/os_settings_section.h"
 #include "chromeos/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "components/prefs/pref_change_registrar.h"
 
 class PrefService;
diff --git a/chrome/browser/ui/webui/settings/ash/os_settings_ui.h b/chrome/browser/ui/webui/settings/ash/os_settings_ui.h
index 2ac73d5..408e646 100644
--- a/chrome/browser/ui/webui/settings/ash/os_settings_ui.h
+++ b/chrome/browser/ui/webui/settings/ash/os_settings_ui.h
@@ -12,7 +12,6 @@
 #include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
 #include "chrome/browser/ui/webui/app_management/app_management_page_handler_factory.h"
 #include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h"
-#include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom-forward.h"
 #include "chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom-forward.h"
 #include "chrome/browser/ui/webui/webui_load_timer.h"
@@ -23,6 +22,7 @@
 #include "chromeos/ash/services/cellular_setup/public/mojom/cellular_setup.mojom-forward.h"
 #include "chromeos/ash/services/cellular_setup/public/mojom/esim_manager.mojom-forward.h"
 #include "chromeos/ash/services/hotspot_config/public/mojom/cros_hotspot_config.mojom-forward.h"
+#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom-forward.h"
 #include "content/public/browser/webui_config.h"
 #include "content/public/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.cc b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.cc
index 9fa8eef..0234514 100644
--- a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.h"
 
+#include <utility>
+#include <vector>
+
+#include "base/functional/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/power_bookmarks/power_bookmark_service_factory.h"
@@ -102,6 +106,11 @@
   DCHECK(browser_);
   browser_->tab_strip_model()->AddObserver(this);
   Observe(browser_->tab_strip_model()->GetActiveWebContents());
+  if (browser_->tab_strip_model()->GetActiveWebContents()) {
+    current_tab_url_ = browser_->tab_strip_model()
+                           ->GetActiveWebContents()
+                           ->GetLastCommittedURL();
+  }
 }
 
 UserNotesPageHandler::~UserNotesPageHandler() {
diff --git a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_side_panel_ui.cc b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_side_panel_ui.cc
index 267da73..0d53ade 100644
--- a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_side_panel_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_side_panel_ui.cc
@@ -11,8 +11,10 @@
 #include "chrome/browser/ui/webui/side_panel/user_notes/user_notes_page_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/generated_resources.h"
 #include "chrome/grit/side_panel_user_notes_resources.h"
 #include "chrome/grit/side_panel_user_notes_resources_map.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_data_source.h"
 
@@ -21,6 +23,14 @@
   content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
       web_ui->GetWebContents()->GetBrowserContext(),
       chrome::kChromeUIUserNotesSidePanelHost);
+  static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"add", IDS_ADD},
+      {"addANote", IDS_ADD_NEW_USER_NOTE_PLACEHOLDER_TEXT},
+      {"cancel", IDS_CANCEL},
+      {"title", IDS_USER_NOTE_TITLE},
+      {"tooltipClose", IDS_CLOSE},
+  };
+  source->AddLocalizedStrings(kLocalizedStrings);
 
   webui::SetupWebUIDataSource(source,
                               base::make_span(kSidePanelUserNotesResources,
diff --git a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_side_panel_ui.h b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_side_panel_ui.h
index 967cee85..a05a89f 100644
--- a/chrome/browser/ui/webui/side_panel/user_notes/user_notes_side_panel_ui.h
+++ b/chrome/browser/ui/webui/side_panel/user_notes/user_notes_side_panel_ui.h
@@ -40,6 +40,8 @@
       page_factory_receiver_{this};
 
   std::unique_ptr<UserNotesPageHandler> user_notes_page_handler_;
+  mojo::Receiver<side_panel::mojom::UserNotesPageHandlerFactory>
+      user_notes_page_factory_receiver_{this};
 
   raw_ptr<Browser> browser_ = nullptr;
 
diff --git a/chrome/browser/ui/webui/signin/inline_login_ui.cc b/chrome/browser/ui/webui/signin/inline_login_ui.cc
index 789b7d5e..ab496631 100644
--- a/chrome/browser/ui/webui/signin/inline_login_ui.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_ui.cc
@@ -54,6 +54,8 @@
 #include "chrome/grit/supervision_resources_map.h"
 #include "components/account_manager_core/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/chromeos/devicetype_utils.h"
 #include "ui/strings/grit/ui_strings.h"
diff --git a/chrome/browser/usb/web_usb_detector_unittest.cc b/chrome/browser/usb/web_usb_detector_unittest.cc
index f2249c2..ff0664eb 100644
--- a/chrome/browser/usb/web_usb_detector_unittest.cc
+++ b/chrome/browser/usb/web_usb_detector_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/notifications/system_notification_helper.h"
@@ -78,7 +77,6 @@
     auto* user =
         GetFakeUserManager()->AddUser(AccountId::FromUserEmail(kProfileName));
     GetFakeUserManager()->LoginUser(user->GetAccountId());
-    ash::ProfileHelper::Get()->ActiveUserHashChanged(user->username_hash());
 #endif
     BrowserList::SetLastActive(browser());
     TestingBrowserProcess::GetGlobal()->SetSystemNotificationHelper(
diff --git a/chrome/browser/web_applications/preinstalled_app_install_features.cc b/chrome/browser/web_applications/preinstalled_app_install_features.cc
index 24cb863..32644ca 100644
--- a/chrome/browser/web_applications/preinstalled_app_install_features.cc
+++ b/chrome/browser/web_applications/preinstalled_app_install_features.cc
@@ -103,11 +103,6 @@
   return false;
 }
 
-// TODO(crbug.com/1406709): Retire this method.
-bool IsAnyChromeAppToWebAppMigrationEnabled(const Profile& profile) {
-  return true;
-}
-
 base::AutoReset<bool>
 SetPreinstalledAppInstallFeatureAlwaysEnabledForTesting() {
   return base::AutoReset<bool>(&g_always_enabled_for_testing, true);
diff --git a/chrome/browser/web_applications/preinstalled_app_install_features.h b/chrome/browser/web_applications/preinstalled_app_install_features.h
index 0b6c131..aff064f 100644
--- a/chrome/browser/web_applications/preinstalled_app_install_features.h
+++ b/chrome/browser/web_applications/preinstalled_app_install_features.h
@@ -26,9 +26,6 @@
 bool IsPreinstalledAppInstallFeatureEnabled(base::StringPiece feature_name,
                                             const Profile& profile);
 
-// Checks if migration flag is enabled on all OS.
-bool IsAnyChromeAppToWebAppMigrationEnabled(const Profile& profile);
-
 base::AutoReset<bool> SetPreinstalledAppInstallFeatureAlwaysEnabledForTesting();
 
 }  // namespace web_app
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index b47a45c4..d1f823051 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -536,6 +536,8 @@
 }
 
 void AuthenticatorRequestDialogModel::OnAuthenticatorMissingLargeBlob() {
+  // TODO(nsatragno): on Windows we should have a more accurate message if large
+  // blob is missing.
   SetCurrentStep(Step::kMissingCapability);
 }
 
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index e20b6a4..0cc010b 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1673956108-8ff5783c496025867c4c4efdfbfa536559fd6aaa.profdata
+chrome-mac-arm-main-1673978375-5c8e5d44fd658e8bb140047fb819c0d72955f58d.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 35c6e28e..2267d14 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1673933740-1df848108ba0df2d096ed62b16e622295fc9473a.profdata
+chrome-mac-main-1673956108-1245eefb80c9997c53be748cea851730cf67c819.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index e52b97f..1634d18 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1673945880-3efe5142207106891c062bd7dc04b965d293755b.profdata
+chrome-win32-main-1673967225-e3244d8cbb3cde814cdf92607d14b838eea0a928.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index ccadbc5..b5cc608 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1673945880-f2f3c63f05809f32c50bf84f8c7702799fd2a4fb.profdata
+chrome-win64-main-1673967225-466766319c8ac646139cec5592baba7b37c5aeb5.profdata
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 83baf815..37601fa 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2830,6 +2830,11 @@
 // This setting resides in local state.
 const char kKioskMetrics[] = "kiosk-metrics";
 
+// A boolean pref which determines whether kiosk troubleshooting tools are
+// enabled.
+const char kKioskTroubleshootingToolsEnabled[] =
+    "kiosk_troubleshooting_tools_enabled";
+
 // A boolean pref which determines whether a Web Kiosk can open more than one
 // browser window.
 const char kNewWindowsInKioskAllowed[] = "new_windows_in_kiosk_allowed";
@@ -3325,8 +3330,10 @@
 // Discards/Reloads since last daily report.
 const char kTabStatsDiscardsExternal[] = "tab_stats.discards_external";
 const char kTabStatsDiscardsUrgent[] = "tab_stats.discards_urgent";
+const char kTabStatsDiscardsProactive[] = "tab_stats.discards_proactive";
 const char kTabStatsReloadsExternal[] = "tab_stats.reloads_external";
 const char kTabStatsReloadsUrgent[] = "tab_stats.reloads_urgent";
+const char kTabStatsReloadsProactive[] = "tab_stats.reloads_proactive";
 
 // A list of origins (URLs) to treat as "secure origins" for debugging purposes.
 const char kUnsafelyTreatInsecureOriginAsSecure[] =
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index f9b08d9..a0ee4d23 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -958,6 +958,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
 extern const char kKioskMetrics[];
+extern const char kKioskTroubleshootingToolsEnabled[];
 extern const char kNewWindowsInKioskAllowed[];
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
@@ -1127,8 +1128,10 @@
 extern const char kTabStatsDailySample[];
 extern const char kTabStatsDiscardsExternal[];
 extern const char kTabStatsDiscardsUrgent[];
+extern const char kTabStatsDiscardsProactive[];
 extern const char kTabStatsReloadsExternal[];
 extern const char kTabStatsReloadsUrgent[];
+extern const char kTabStatsReloadsProactive[];
 
 extern const char kUnsafelyTreatInsecureOriginAsSecure[];
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 35f0ddf..069d708 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1493,6 +1493,7 @@
       "//components/user_education/common",
       "//components/user_education/test",
       "//components/user_manager",
+      "//components/user_notes:features",
       "//components/user_prefs",
       "//components/variations/service",
       "//components/version_info",
@@ -3981,7 +3982,6 @@
         "../browser/ash/policy/status_collector/child_status_collector_browsertest.cc",
         "../browser/ash/policy/status_collector/device_status_collector_browsertest.cc",
         "../browser/ash/preferences_browsertest.cc",
-        "../browser/ash/profiles/profile_helper_browsertest.cc",
         "../browser/ash/remote_apps/remote_apps_manager_browsertest.cc",
         "../browser/ash/scoped_test_system_nss_key_slot_mixin.cc",
         "../browser/ash/scoped_test_system_nss_key_slot_mixin.h",
@@ -7641,7 +7641,6 @@
       "//chrome/browser/ui/ash/holding_space:test_support",
       "//chrome/browser/ui/quick_answers",
       "//chrome/browser/ui/webui/nearby_share:mojom",
-      "//chrome/browser/ui/webui/nearby_share/public/mojom",
       "//chrome/services/printing:pdf_thumbnailer_test",
       "//chrome/services/sharing:unit_tests",
       "//chrome/services/sharing/nearby:unit_tests",
@@ -7685,6 +7684,7 @@
       "//chromeos/ash/services/multidevice_setup/public/cpp:test_support",
       "//chromeos/ash/services/nearby/public/cpp:tcp_server_socket_port",
       "//chromeos/ash/services/nearby/public/cpp:test_support",
+      "//chromeos/ash/services/nearby/public/mojom",
       "//chromeos/ash/services/secure_channel/public/cpp/client:test_support",
       "//chromeos/components/onc",
       "//chromeos/components/quick_answers:quick_answers",
@@ -8542,17 +8542,6 @@
         [ "//components/safe_browsing/android:real_time_url_checks_allowlist" ]
   }
 
-  if (is_mac) {
-    sources += [ "../browser/enterprise/connectors/device_trust/browser/mac_device_trust_connector_service_unittest.cc" ]
-
-    deps += [
-      "../browser/enterprise/connectors/device_trust:features",
-      "../browser/enterprise/connectors/device_trust:prefs",
-      "../browser/enterprise/connectors/device_trust/key_management/browser:test_support",
-      "../browser/enterprise/connectors/device_trust/key_management/core/mac",
-    ]
-  }
-
   if (is_win) {
     deps += [ "../browser/enterprise/connectors/device_trust/signals/decorators/browser/win:unit_tests" ]
   }
@@ -8601,6 +8590,7 @@
       "../browser/browser_switcher/mock_alternative_browser_driver.cc",
       "../browser/browser_switcher/mock_alternative_browser_driver.h",
       "../browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service_unittest.cc",
+      "../browser/enterprise/connectors/device_trust/browser/browser_device_trust_connector_service_unittest.cc",
       "../browser/enterprise/idle/browser_closer_unittest.cc",
       "../browser/enterprise/remote_commands/rotate_attestation_credential_job_unittest.cc",
       "../browser/enterprise/signals/user_delegate_impl_unittest.cc",
@@ -8924,7 +8914,6 @@
       "../browser/ui/views/side_panel/read_anything/read_anything_coordinator_unittest.cc",
       "../browser/ui/views/side_panel/read_anything/read_anything_model_unittest.cc",
       "../browser/ui/views/side_panel/side_panel_coordinator_unittest.cc",
-      "../browser/ui/views/side_panel/user_note/user_note_ui_coordinator_unittest.cc",
       "../browser/ui/views/site_data/page_specific_site_data_dialog_unittest.cc",
       "../browser/ui/views/tab_contents/chrome_web_contents_view_delegate_views_unittest.cc",
       "../browser/ui/views/tabs/color_picker_view_unittest.cc",
@@ -9570,6 +9559,7 @@
       "../browser/ui/views/side_panel/read_anything/read_anything_toolbar_view_browsertest.cc",
       "../browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc",
       "../browser/ui/views/web_dialog_view_browsertest.cc",
+      "../browser/ui/web_applications/web_app_interactive_uitest.cc",
       "../browser/ui/webui/access_code_cast/access_code_cast_handler_browsertest.cc",
       "../browser/ui/webui/settings/performance_settings_interactive_uitest.cc",
       "../browser/ui/webui/settings/settings_interactive_uitest.cc",
@@ -10218,6 +10208,7 @@
       "//chrome/android:chrome_all_java",
       "//chrome/browser/profiles/android:java",
       "//chrome/test/android:chrome_java_integration_test_support",
+      "//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
       "//components/signin/public/android:java",
       "//components/sync/android:sync_java",
       "//content/public/test/android:content_java_test_support",
@@ -10259,10 +10250,6 @@
     "../browser/sync/test/integration/fake_sync_gcm_driver_for_instance_id.h",
     "../browser/sync/test/integration/invalidations/fake_server_sync_invalidation_sender.cc",
     "../browser/sync/test/integration/invalidations/fake_server_sync_invalidation_sender.h",
-    "../browser/sync/test/integration/invalidations/fake_sync_instance_id.cc",
-    "../browser/sync/test/integration/invalidations/fake_sync_instance_id.h",
-    "../browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.cc",
-    "../browser/sync/test/integration/invalidations/fake_sync_instance_id_driver.h",
     "../browser/sync/test/integration/invalidations/invalidations_status_checker.cc",
     "../browser/sync/test/integration/invalidations/invalidations_status_checker.h",
     "../browser/sync/test/integration/multi_client_status_change_checker.cc",
@@ -10408,7 +10395,10 @@
     ]
   }
   if (is_android) {
-    deps += [ ":sync_integration_test_support_jni_headers" ]
+    deps += [
+      ":sync_integration_test_support_jni_headers",
+      ":test_support_jni_headers",
+    ]
   } else {
     deps += [
       "//chrome/browser/apps/app_service",
diff --git a/chrome/test/DEPS b/chrome/test/DEPS
index ca6d109..f5f06296 100644
--- a/chrome/test/DEPS
+++ b/chrome/test/DEPS
@@ -74,6 +74,7 @@
   "+components/update_client",
   "+components/user_education",
   "+components/user_manager",
+  "+components/user_notes",
   "+components/user_prefs",
   "+components/version_info",
   "+components/web_modal",
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 0e47b96..39dd9cd 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -21065,43 +21065,7 @@
     ]
   },
   "FileSystemSyncAccessHandleAsyncInterfaceEnabled": {
-    "os": [
-      "win",
-      "linux",
-      "mac",
-      "chromeos_ash",
-      "chromeos_lacros",
-      "android",
-      "fuchsia"
-    ],
-    "policy_pref_mapping_tests": [
-      {
-        "policies": {},
-        "prefs": {
-          "policy.file_system_sync_access_handle_async_interface_enabled": {
-            "default_value": false
-          }
-        }
-      },
-      {
-        "policies": {
-          "FileSystemSyncAccessHandleAsyncInterfaceEnabled": false
-        },
-        "prefs": {
-          "policy.file_system_sync_access_handle_async_interface_enabled": {
-            "value": false
-          }
-        }
-      },
-      {
-        "policies": { "FileSystemSyncAccessHandleAsyncInterfaceEnabled": true },
-        "prefs": {
-          "policy.file_system_sync_access_handle_async_interface_enabled": {
-            "value": true
-          }
-        }
-      }
-    ]
+    "reason_for_missing_test": "Policy was removed"
   },
   "DefaultHandlersForFileExtensions": {
     "os": ["chromeos_ash"],
@@ -21771,5 +21735,47 @@
         }
       }
     ]
+  },
+  "KioskTroubleshootingToolsEnabled": {
+    "os": [
+      "chromeos_ash",
+      "chromeos_lacros"
+    ],
+    "policy_pref_mapping_tests": [
+      {
+        "note": "Check default value (no policies set)",
+        "policies": { },
+        "prefs": {
+          "kiosk_troubleshooting_tools_enabled": {
+            "location": "user_profile",
+            "default_value": false
+          }
+        }
+      },
+      {
+        "note": "Check true policy value",
+        "policies": {
+          "KioskTroubleshootingToolsEnabled": true
+        },
+        "prefs": {
+          "kiosk_troubleshooting_tools_enabled": {
+            "location": "user_profile",
+            "value": true
+          }
+        }
+      },
+      {
+        "note": "Check false policy value",
+        "policies": {
+          "KioskTroubleshootingToolsEnabled": false
+        },
+        "prefs": {
+          "kiosk_troubleshooting_tools_enabled": {
+            "location": "user_profile",
+            "value": false
+          }
+        }
+      }
+    ]
   }
 }
diff --git a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
index 121d991d..fbe7b6f 100644
--- a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
+++ b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
@@ -2217,7 +2217,8 @@
 
         assertEquals(
             matchEls[1]!.$.icon.$.image.getAttribute('src'),
-            `//image?${matches[1]!.imageUrl}`);
+            `//image?staticEncode=true&encodeType=webp&url=${
+                matches[1]!.imageUrl}`);
 
         // Mock image finishing loading, which should remove the temporary
         // background color.
diff --git a/chrome/test/data/webui/settings/cookies_page_test.ts b/chrome/test/data/webui/settings/cookies_page_test.ts
index fa6cab00..71780d4 100644
--- a/chrome/test/data/webui/settings/cookies_page_test.ts
+++ b/chrome/test/data/webui/settings/cookies_page_test.ts
@@ -36,7 +36,6 @@
   }
 
   suiteSetup(function() {
-    loadTimeData.overrideValues({firstPartySetsUIEnabled: false});
     settingsPrefs = document.createElement('settings-prefs');
     return CrSettingsPrefs.initialized;
   });
@@ -301,53 +300,7 @@
     assertFalse(page.$.toast.open);
   });
 
-  test('Block third-party cookies in Incognito bullet point', function() {
-    // Confirm the correct string is set.
-    const cookiePageBlockThirdIncognitoBulTwoLabel =
-        page.shadowRoot!
-            .querySelector<HTMLElement>(
-                '#blockThirdPartyIncognitoBulTwo')!.innerText.trim();
-    assertEquals(
-        loadTimeData.getString('thirdPartyCookiesPageBlockIncognitoBulTwo'),
-        cookiePageBlockThirdIncognitoBulTwoLabel);
-  });
-});
-
-suite('CrSettingsCookiesPageTest_FirstPartySetsUIEnabled', function() {
-  let page: SettingsCookiesPageElement;
-  let settingsPrefs: SettingsPrefsElement;
-
-  function blockThirdParty(): SettingsCollapseRadioButtonElement {
-    return page.shadowRoot!.querySelector('#blockThirdParty')!;
-  }
-
-  function blockThirdPartyIncognito(): SettingsCollapseRadioButtonElement {
-    return page.shadowRoot!.querySelector('#blockThirdPartyIncognito')!;
-  }
-
-  function allowThirdParty(): SettingsCollapseRadioButtonElement {
-    return page.shadowRoot!.querySelector('#allowThirdParty')!;
-  }
-
-  suiteSetup(function() {
-    loadTimeData.overrideValues({firstPartySetsUIEnabled: true});
-    settingsPrefs = document.createElement('settings-prefs');
-    return CrSettingsPrefs.initialized;
-  });
-
-  setup(function() {
-    document.body.innerHTML = window.trustedTypes!.emptyHTML;
-    page = document.createElement('settings-cookies-page');
-    page.prefs = settingsPrefs.prefs!;
-    document.body.appendChild(page);
-    flush();
-  });
-
-  teardown(function() {
-    page.remove();
-  });
-
-  test('Disabled Toggle', function() {
+  test('disabledFPSToggle', function() {
     // Confirm that when the user has not selected the block 3PC setting, the
     // FPS toggle is disabled.
     const firstPartySetsToggle =
@@ -374,20 +327,15 @@
     assertTrue(firstPartySetsToggle.disabled, 'expect toggle to be disabled');
   });
 
-  // TODO(crbug.com/1378703): Both paths should be tested.
-  test('Block third-party cookies in Incognito FPS bullet point', function() {
+  test('blockThirdPartyIncognitoSecondBulletPointText', function() {
     // Confirm the correct string is set.
-    const cookiePageBlockThirdIncognitoBulTwoId =
-        loadTimeData.getBoolean('isPrivacySandboxSettings4') ?
-        '#blockThirdPartyIncognitoBulTwo' :
-        '#cookiePageBlockThirdIncognitoBulTwo';
-    const cookiePageBlockThirdIncognitoBulTwoLabel =
+    const cookiesPageBlockThirdPartyIncognitoBulTwoLabel =
         page.shadowRoot!
             .querySelector<HTMLElement>(
-                cookiePageBlockThirdIncognitoBulTwoId)!.innerText.trim();
+                '#blockThirdPartyIncognitoBulTwo')!.innerText.trim();
     assertEquals(
         loadTimeData.getString('cookiePageBlockThirdIncognitoBulTwoFps'),
-        cookiePageBlockThirdIncognitoBulTwoLabel);
+        cookiesPageBlockThirdPartyIncognitoBulTwoLabel);
   });
 });
 
@@ -420,7 +368,6 @@
       isSecondaryUser: false,
       // </if>
       isPrivacySandboxSettings4: false,
-      firstPartySetsUIEnabled: false,
     });
     settingsPrefs = document.createElement('settings-prefs');
     return CrSettingsPrefs.initialized;
@@ -692,18 +639,95 @@
     assertFalse(page.$.toast.open);
   });
 
-  test('Block third-party cookies in Incognito bullet point', function() {
+  test('blockThirdPartyIncognitoSecondBulletPointText', function() {
     // Confirm the correct string is set.
-    const cookiePageBlockThirdIncognitoBulTwoLabel =
+    const cookiesPageBlockThirdPartyIncognitoBulTwoLabel =
         page.shadowRoot!
             .querySelector<HTMLElement>(
-                '#cookiePageBlockThirdIncognitoBulTwo')!.innerText.trim();
+                '#cookiesPageBlockThirdPartyIncognitoBulTwo')!.innerText.trim();
     assertEquals(
-        loadTimeData.getString('cookiePageBlockThirdIncognitoBulTwo'),
-        cookiePageBlockThirdIncognitoBulTwoLabel);
+        loadTimeData.getString('cookiePageBlockThirdIncognitoBulTwoFps'),
+        cookiesPageBlockThirdPartyIncognitoBulTwoLabel);
   });
 });
 
+// TODO(crbug/1349370): Remove after crbug/1349370 is launched.
+suite('CrSettingsCookiesPageTest_FirstPartySetsUIDisabled', function() {
+  let page: SettingsCookiesPageElement;
+  let settingsPrefs: SettingsPrefsElement;
+
+  suiteSetup(function() {
+    loadTimeData.overrideValues({firstPartySetsUIEnabled: false});
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
+  setup(function() {
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    page = document.createElement('settings-cookies-page');
+    page.prefs = settingsPrefs.prefs!;
+    document.body.appendChild(page);
+    flush();
+  });
+
+  teardown(function() {
+    page.remove();
+  });
+
+  test('blockThirdPartyIncognitoSecondBulletPointText', function() {
+    // Confirm the correct string is set.
+    const cookiesPageBlockThirdPartyIncognitoBulTwoLabel =
+        page.shadowRoot!
+            .querySelector<HTMLElement>(
+                '#blockThirdPartyIncognitoBulTwo')!.innerText.trim();
+    assertEquals(
+        loadTimeData.getString('thirdPartyCookiesPageBlockIncognitoBulTwo'),
+        cookiesPageBlockThirdPartyIncognitoBulTwoLabel);
+  });
+});
+
+// TODO(crbug/1378703): Remove after crbug/1378703 or crbug/1349370 are
+// launched.
+suite(
+    'CrSettingsCookiesPageTest_PrivacySandboxSettings4Disabled_FirstPartySetsUIDisabled',
+    function() {
+      let page: SettingsCookiesPageElement;
+      let settingsPrefs: SettingsPrefsElement;
+
+      suiteSetup(function() {
+        loadTimeData.overrideValues({
+          isPrivacySandboxSettings4: false,
+          firstPartySetsUIEnabled: false,
+        });
+        settingsPrefs = document.createElement('settings-prefs');
+        return CrSettingsPrefs.initialized;
+      });
+
+      setup(function() {
+        document.body.innerHTML = window.trustedTypes!.emptyHTML;
+        page = document.createElement('settings-cookies-page');
+        page.prefs = settingsPrefs.prefs!;
+        document.body.appendChild(page);
+        flush();
+      });
+
+      teardown(function() {
+        page.remove();
+      });
+
+      test('blockThirdPartyIncognitoSecondBulletPointText', function() {
+        // Confirm the correct string is set.
+        const cookiesPageBlockThirdPartyIncognitoBulTwoLabel =
+            page.shadowRoot!
+                .querySelector<HTMLElement>(
+                    '#cookiesPageBlockThirdPartyIncognitoBulTwo')!.innerText
+                .trim();
+        assertEquals(
+            loadTimeData.getString('cookiePageBlockThirdIncognitoBulTwo'),
+            cookiesPageBlockThirdPartyIncognitoBulTwoLabel);
+      });
+    });
+
 // <if expr="chromeos_lacros">
 // TODO(crbug/1378703): Remove after crbug/1378703 launched.
 suite('CrSettingsCookiesPageTest_lacrosSecondaryProfile', function() {
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index bcc4ec1..9267854 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -676,6 +676,7 @@
     return {
       enabled: [
         'privacy_sandbox::kPrivacySandboxSettings4',
+        'privacy_sandbox::kPrivacySandboxFirstPartySetsUI',
         'features::kPreloadingDesktopSettingsSubPage',
       ],
     };
@@ -686,8 +687,8 @@
   runMochaSuite('CrSettingsCookiesPageTest');
 });
 
-TEST_F('CrSettingsCookiesPageTest', 'FirstPartySetsUIEnabled', function() {
-  runMochaSuite('CrSettingsCookiesPageTest_FirstPartySetsUIEnabled');
+TEST_F('CrSettingsCookiesPageTest', 'FirstPartySetsUIDisabled', function() {
+  runMochaSuite('CrSettingsCookiesPageTest_FirstPartySetsUIDisabled');
 });
 
 GEN('#if BUILDFLAG(IS_CHROMEOS_LACROS)');
@@ -823,8 +824,14 @@
   runMochaSuite('SiteSettingsPage');
 });
 
+// TODO(crbug.com/1401833): Flaky.
+GEN('#if BUILDFLAG(IS_LINUX) && !defined(NDEBUG)');
+GEN('#define MAYBE_PrivacySandboxSettings4Disabled DISABLED_PrivacySandboxSettings4Disabled');
+GEN('#else');
+GEN('#define MAYBE_PrivacySandboxSettings4Disabled PrivacySandboxSettings4Disabled');
+GEN('#endif');
 TEST_F(
-    'CrSettingsSiteSettingsPageTest', 'PrivacySandboxSettings4Disabled',
+    'CrSettingsSiteSettingsPageTest', 'MAYBE_PrivacySandboxSettings4Disabled',
     function() {
       runMochaSuite('PrivacySandboxSettings4Disabled');
     });
diff --git a/chrome/test/data/webui/side_panel/BUILD.gn b/chrome/test/data/webui/side_panel/BUILD.gn
index a9f5056..9a795ac 100644
--- a/chrome/test/data/webui/side_panel/BUILD.gn
+++ b/chrome/test/data/webui/side_panel/BUILD.gn
@@ -20,6 +20,9 @@
     "chrome://read-anything-side-panel.top-chrome/*|" + rebase_path(
             "$root_gen_dir/chrome/browser/resources/side_panel/read_anything/tsc/*",
             target_gen_dir),
+    "chrome://user-notes-side-panel.top-chrome/*|" + rebase_path(
+            "$root_gen_dir/chrome/browser/resources/side_panel/user_notes/tsc/*",
+            target_gen_dir),
     "chrome://webui-test/*|" +
         rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
                     target_gen_dir),
@@ -36,6 +39,8 @@
     "reading_list/reading_list_app_test.ts",
     "reading_list/test_reading_list_api_proxy.ts",
     "read_anything/read_anything_app_test.ts",
+    "user_notes/app_test.ts",
+    "user_notes/test_user_notes_api_proxy.ts",
   ]
   definitions = [
     "//chrome/browser/resources/side_panel/read_anything/read_anything.d.ts",
@@ -48,6 +53,7 @@
     "//chrome/browser/resources/side_panel/bookmarks:build_ts",
     "//chrome/browser/resources/side_panel/read_anything:build_ts",
     "//chrome/browser/resources/side_panel/reading_list:build_ts",
+    "//chrome/browser/resources/side_panel/user_notes:build_ts",
   ]
 }
 
diff --git a/chrome/test/data/webui/side_panel/side_panel_browsertest.js b/chrome/test/data/webui/side_panel/side_panel_browsertest.js
index 93e494b..94cb61e 100644
--- a/chrome/test/data/webui/side_panel/side_panel_browsertest.js
+++ b/chrome/test/data/webui/side_panel/side_panel_browsertest.js
@@ -8,6 +8,8 @@
 
 GEN('#include "content/public/test/browser_test.h"');
 GEN('#include "ui/accessibility/accessibility_features.h"');
+GEN('#include "components/user_notes/user_notes_features.h"');
+GEN('#include "components/power_bookmarks/core/power_bookmark_features.h"');
 
 class SidePanelBrowserTest extends PolymerTest {
   /** @override */
@@ -105,3 +107,22 @@
 TEST_F('ReadAnythingAppTest', 'All', function() {
   mocha.run();
 });
+
+var UserNotesAppTest = class extends SidePanelBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://user-notes-side-panel.top-chrome/test_loader.html?module=side_panel/user_notes/app_test.js';
+  }
+
+  /** @override */
+  get featureList() {
+    return {
+      enabled:
+          ['user_notes::kUserNotes', 'power_bookmarks::kPowerBookmarkBackend']
+    };
+  }
+};
+
+TEST_F('UserNotesAppTest', 'All', function() {
+  mocha.run();
+});
\ No newline at end of file
diff --git a/chrome/test/data/webui/side_panel/user_notes/app_test.ts b/chrome/test/data/webui/side_panel/user_notes/app_test.ts
new file mode 100644
index 0000000..0103376
--- /dev/null
+++ b/chrome/test/data/webui/side_panel/user_notes/app_test.ts
@@ -0,0 +1,83 @@
+// 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 'chrome://webui-test/mojo_webui_test_support.js';
+import 'chrome://user-notes-side-panel.top-chrome/app.js';
+
+import {UserNotesAppElement} from 'chrome://user-notes-side-panel.top-chrome/app.js';
+import {Note} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
+import {UserNotesApiProxyImpl} from 'chrome://user-notes-side-panel.top-chrome/user_notes_api_proxy.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+
+import {TestUserNotesApiProxy} from './test_user_notes_api_proxy.js';
+
+suite('UserNotesAppTest', () => {
+  let userNotesApp: UserNotesAppElement;
+  let testProxy: TestUserNotesApiProxy;
+
+  const notes: Note[] = [
+    {
+      guid: '1234',
+      url: {url: 'www.google.com'},
+      lastModificationTime: {internalValue: 5n},
+      lastModificationTimeText: '5:00 AM - Oct 5',
+      text: 'sample note text',
+    },
+    {
+      guid: '1235',
+      url: {url: 'www.google.com'},
+      lastModificationTime: {internalValue: 20n},
+      lastModificationTimeText: '7:00 AM - Oct 6',
+      text: 'sample second note text',
+    },
+  ];
+
+  function queryNotes() {
+    return userNotesApp.shadowRoot!.querySelectorAll('user-note');
+  }
+
+  setup(async () => {
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+
+    testProxy = new TestUserNotesApiProxy();
+    UserNotesApiProxyImpl.setInstance(testProxy);
+    testProxy.setNotes(notes);
+
+    userNotesApp = document.createElement('user-notes-app');
+    document.body.appendChild(userNotesApp);
+    await flushTasks();
+  });
+
+  test('all entries shown', () => {
+    const notesElements = queryNotes();
+    assertEquals(notesElements.length, 3);
+    for (let i = 0; i < notes.length; i++) {
+      assertEquals(
+          'false',
+          notesElements[i]!.$.noteContent.getAttribute('contenteditable'));
+      assertEquals(notesElements[i]!.$.noteContent.textContent, notes[i]!.text);
+    }
+    const entryNote = notesElements[(notesElements.length - 1)]!;
+    assertEquals(
+        'true', entryNote.$.noteContent.getAttribute('contenteditable'));
+    assertEquals(entryNote.$.noteContent.textContent, '');
+  });
+
+  test('note entry adding a new note', async () => {
+    const notesElements = queryNotes();
+    const entryNote = notesElements[2]!;
+    const sampleNoteContent = 'sample note content';
+    entryNote.$.noteContent.textContent = sampleNoteContent;
+    assertEquals(
+        'true', entryNote.$.noteContent.getAttribute('contenteditable'));
+    entryNote.$.noteContent.focus();
+    const notesAddButton =
+        entryNote.shadowRoot!.querySelector('#addButton')! as HTMLButtonElement;
+    notesAddButton.click();
+    const text = await testProxy.whenCalled('newNoteFinished');
+    assertEquals(text, sampleNoteContent);
+    assertEquals('', entryNote.$.noteContent.textContent);
+  });
+});
\ No newline at end of file
diff --git a/chrome/test/data/webui/side_panel/user_notes/test_user_notes_api_proxy.ts b/chrome/test/data/webui/side_panel/user_notes/test_user_notes_api_proxy.ts
new file mode 100644
index 0000000..393aaa9f
--- /dev/null
+++ b/chrome/test/data/webui/side_panel/user_notes/test_user_notes_api_proxy.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 {Note, UserNotesPageCallbackRouter} from 'chrome://user-notes-side-panel.top-chrome/user_notes.mojom-webui.js';
+import {UserNotesApiProxy} from 'chrome://user-notes-side-panel.top-chrome/user_notes_api_proxy.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+
+export class TestUserNotesApiProxy extends TestBrowserProxy implements
+    UserNotesApiProxy {
+  private callbackRouter_: UserNotesPageCallbackRouter =
+      new UserNotesPageCallbackRouter();
+  private notes_: Note[];
+
+  constructor() {
+    super([
+      'deleteNote',
+      'getNotesForCurrentTab',
+      'newNoteFinished',
+      'showUi',
+    ]);
+
+    this.notes_ = [];
+  }
+
+  deleteNote(guid: string) {
+    this.methodCalled('deleteNote', guid);
+    return Promise.resolve({success: true});
+  }
+
+  getNotesForCurrentTab() {
+    this.methodCalled('getNotesForCurrentTab');
+    return Promise.resolve({notes: this.notes_.slice()});
+  }
+
+  newNoteFinished(text: string) {
+    this.methodCalled('newNoteFinished', text);
+    return Promise.resolve({success: true});
+  }
+
+  showUi() {
+    this.methodCalled('showUi');
+  }
+
+  getCallbackRouter() {
+    return this.callbackRouter_;
+  }
+
+  setNotes(notes: Note[]) {
+    this.notes_ = notes;
+  }
+}
\ No newline at end of file
diff --git a/chrome/test/enterprise/e2e/connector/common/realtime_reporting_ui_test.py b/chrome/test/enterprise/e2e/connector/common/realtime_reporting_ui_test.py
index f37b21020..747f4fe 100644
--- a/chrome/test/enterprise/e2e/connector/common/realtime_reporting_ui_test.py
+++ b/chrome/test/enterprise/e2e/connector/common/realtime_reporting_ui_test.py
@@ -2,16 +2,19 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import logging
+import json
 import time
 from absl import app
 from pywinauto.application import Application
 from selenium import webdriver
+from selenium.webdriver.support.ui import WebDriverWait
 
 from test_util import create_chrome_webdriver
 from test_util import getElementFromShadowRoot
 
-UnsafePageLink = "http://testsafebrowsing.appspot.com/s/malware.html"
-UnsafeDownloadLink = "http://testsafebrowsing.appspot.com/s/badrep.exe"
+UnsafePageLink = 'http://testsafebrowsing.appspot.com/s/malware.html'
+UnsafeDownloadLink = 'http://testsafebrowsing.appspot.com/s/badrep.exe'
 
 
 def visit(window, url):
@@ -20,19 +23,19 @@
   SafeBrowsing intercepts HTTP requests & hangs WebDriver.get(), which prevents
   us from getting the page source. Using pywinauto to visit the pages instead.
   """
-  window.Edit.set_edit_text(url).type_keys("%{ENTER}")
+  window.Edit.set_edit_text(url).type_keys('%{ENTER}')
   time.sleep(10)
 
 
 def main(argv):
-  exclude_switches = ["disable-background-networking"]
+  exclude_switches = ['disable-background-networking']
   chrome_options = webdriver.ChromeOptions()
-  chrome_options.add_experimental_option("excludeSwitches", exclude_switches)
+  chrome_options.add_experimental_option('excludeSwitches', exclude_switches)
 
   driver = create_chrome_webdriver(chrome_options=chrome_options)
 
   try:
-    app = Application(backend="uia")
+    app = Application(backend='uia')
     app.connect(title_re='.*Chrome|.*Chromium')
     window = app.top_window()
 
@@ -40,26 +43,47 @@
     # There's no trigger to force this operation or synchronize on it, but quick
     # experiments have shown 3-4 minutes in most cases, so 5 should be plenty.
     time.sleep(60 * 5)
+    # Open chrome://safe-browsing
+    safe_browsing_url = 'chrome://safe-browsing'
+    driver.get(safe_browsing_url)
+    WebDriverWait(driver=driver, timeout=10)
+    # navigate to "reporting events" tab
+    driver.find_element_by_css_selector('#reporting').click()
 
     # Verify Policy status legend in chrome://policy page
-    policy_url = "chrome://policy"
-    driver.get(policy_url)
-    driver.find_element_by_id('reload-policies').click
+    # Switch to the new window
+    policy_url = 'chrome://policy'
+    logging.info('Navigating to chrome://policy')
+    # use visit so it will open policy app in a different tab
+    visit(window, policy_url)
+    driver.switch_to.window(driver.window_handles[1])
+    WebDriverWait(driver=driver, timeout=10)
+
+    driver.find_element_by_xpath('//*[@id="reload-policies"]').click
     # Give the page 2 seconds to render the legend
     time.sleep(2)
-    status_box = driver.find_element_by_css_selector("status-box")
-    el = getElementFromShadowRoot(driver, status_box, "fieldset")
+    status_box = driver.find_element_by_css_selector('status-box')
+    el = getElementFromShadowRoot(driver, status_box, 'fieldset')
 
     deviceId = el.find_element_by_class_name(
         'machine-enrollment-device-id').text
 
+    logging.info('Navigating to %s' % UnsafePageLink)
     visit(window, UnsafePageLink)
 
+    logging.info('Navigating to %s' % UnsafeDownloadLink)
     visit(window, UnsafeDownloadLink)
-    print('\nDeviceId:' + deviceId.strip())
 
-  except Exception as error:
-    print(error)
+    # print events logged at safe-browsing app
+    driver.switch_to.window(driver.window_handles[0])
+    WebDriverWait(driver=driver, timeout=10)
+    msgs = json.loads(
+        driver.find_element_by_css_selector('#reporting-events > div').text)
+    logging.info('ReportedEvent:%s' % msgs)
+    logging.info('\nDeviceId:' + deviceId.strip())
+
+  except Exception as e:
+    logging.critical(e, exc_info=True)
   finally:
     driver.quit()
 
diff --git a/chrome/test/enterprise/e2e/connector/reporting_connector_pan/pan_api_service.py b/chrome/test/enterprise/e2e/connector/reporting_connector_pan/pan_api_service.py
index 59a8675..4b15ae52 100644
--- a/chrome/test/enterprise/e2e/connector/reporting_connector_pan/pan_api_service.py
+++ b/chrome/test/enterprise/e2e/connector/reporting_connector_pan/pan_api_service.py
@@ -114,8 +114,9 @@
     headers = self._generate_headers()
     parameters = {}
     request_data = {}
-    request_data["query"] = (r"dataset=%s | fields event, time | limit %d" %
-                             (xdr_dataset, self._event_limit))
+    request_data["query"] = (
+        r"dataset=%s | fields event, time | sort desc _time | limit %d " %
+        (xdr_dataset, self._event_limit))
     request_data["tenants"] = ""
     # Only query for the last self.relative_time hours
     request_data["timeframe"] = {"relativeTime": self._relative_time}
diff --git a/chrome/test/enterprise/e2e/connector/reporting_connector_pan/reporting_connector_pan_test.py b/chrome/test/enterprise/e2e/connector/reporting_connector_pan/reporting_connector_pan_test.py
index 4cf9568..0daf105 100644
--- a/chrome/test/enterprise/e2e/connector/reporting_connector_pan/reporting_connector_pan_test.py
+++ b/chrome/test/enterprise/e2e/connector/reporting_connector_pan/reporting_connector_pan_test.py
@@ -49,10 +49,20 @@
     # trigger malware event & get device id from browser
     localDir = os.path.dirname(os.path.abspath(__file__))
     commonDir = os.path.dirname(localDir)
-    clientId = self.RunUITest(
-        self.win_config['client'],
-        os.path.join(commonDir, 'common', 'realtime_reporting_ui_test.py'),
-        timeout=600)
+    clientId = ''
+    retryCount = 0
+    # if not be able to find testsafebrowsing in logging, retry up to
+    # 3 times.
+    while retryCount < 3:
+      retryCount += 1
+      safeNet = ''
+      clientId = self.RunUITest(
+          self.win_config['client'],
+          os.path.join(commonDir, 'common', 'realtime_reporting_ui_test.py'),
+          timeout=600)
+      safeNet = re.search(r'testsafebrowsing', clientId.strip())
+      if safeNet:
+        break
     clientId = re.search(r'DeviceId:.*$',
                          clientId.strip()).group(0).replace('DeviceId:',
                                                             '').rstrip("\\rn'")
diff --git a/chrome/updater/auto_run_on_os_upgrade_task_unittest.cc b/chrome/updater/auto_run_on_os_upgrade_task_unittest.cc
index 84255fe..3ec58d1b 100644
--- a/chrome/updater/auto_run_on_os_upgrade_task_unittest.cc
+++ b/chrome/updater/auto_run_on_os_upgrade_task_unittest.cc
@@ -15,9 +15,11 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/bind.h"
 #include "base/test/test_timeouts.h"
 #include "chrome/updater/persisted_data.h"
 #include "chrome/updater/test_scope.h"
+#include "chrome/updater/util/unittest_util.h"
 #include "chrome/updater/util/unittest_util_win.h"
 #include "chrome/updater/util/win_util.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -114,10 +116,10 @@
   base::FilePath os_upgrade_file = current_directory.Append(os_upgrade_string);
   base::FilePath hardcoded_file = current_directory.Append(L"HardcodedFile");
 
-  base::WaitableEvent().TimedWait(TestTimeouts::tiny_timeout());
-  EXPECT_TRUE(base::PathExists(os_upgrade_file));
-  EXPECT_TRUE(base::PathExists(hardcoded_file));
-
+  EXPECT_TRUE(test::WaitFor(base::BindLambdaForTesting([&]() {
+    return base::PathExists(os_upgrade_file) &&
+           base::PathExists(hardcoded_file);
+  })));
   EXPECT_TRUE(base::DeleteFile(os_upgrade_file));
   EXPECT_TRUE(base::DeleteFile(hardcoded_file));
 }
diff --git a/chrome/updater/test/integration_tests_impl.cc b/chrome/updater/test/integration_tests_impl.cc
index 0c1efa8e..f706aba 100644
--- a/chrome/updater/test/integration_tests_impl.cc
+++ b/chrome/updater/test/integration_tests_impl.cc
@@ -41,7 +41,6 @@
 #include "base/task/thread_pool.h"
 #include "base/test/bind.h"
 #include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "base/version.h"
@@ -519,24 +518,6 @@
   ASSERT_TRUE(succeeded);
 }
 
-bool WaitFor(base::RepeatingCallback<bool()> predicate,
-             base::RepeatingClosure still_waiting) {
-  constexpr base::TimeDelta kOutputInterval = base::Seconds(10);
-  auto notify_next = base::TimeTicks::Now() + kOutputInterval;
-  const auto deadline = base::TimeTicks::Now() + TestTimeouts::action_timeout();
-  while (base::TimeTicks::Now() < deadline) {
-    if (predicate.Run()) {
-      return true;
-    }
-    if (notify_next < base::TimeTicks::Now()) {
-      still_waiting.Run();
-      notify_next += kOutputInterval;
-    }
-    base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
-  }
-  return false;
-}
-
 bool RequestMatcherRegex(const std::string& request_body_regex,
                          const std::string& request_body) {
   if (!re2::RE2::PartialMatch(request_body, request_body_regex)) {
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h
index 66985f4d..ed55c8c 100644
--- a/chrome/updater/test/integration_tests_impl.h
+++ b/chrome/updater/test/integration_tests_impl.h
@@ -11,8 +11,6 @@
 #include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/functional/callback_forward.h"
-#include "base/functional/callback_helpers.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -74,13 +72,6 @@
 // Copies the logs to a location where they can be retrieved by ResultDB.
 void CopyLog(const base::FilePath& src_dir);
 
-// Waits for a given `predicate` to become true. Invokes `still_waiting`
-// periodically to provide a indication of progress. Returns true if the
-// predicate becomes true before a timeout, otherwise returns false.
-[[nodiscard]] bool WaitFor(
-    base::RepeatingCallback<bool()> predicate,
-    base::RepeatingClosure still_waiting = base::DoNothing());
-
 // Returns the path to the updater data dir.
 absl::optional<base::FilePath> GetDataDirPath(UpdaterScope scope);
 
diff --git a/chrome/updater/test/integration_tests_linux.cc b/chrome/updater/test/integration_tests_linux.cc
index 864b042..bd28d7b 100644
--- a/chrome/updater/test/integration_tests_linux.cc
+++ b/chrome/updater/test/integration_tests_linux.cc
@@ -28,6 +28,7 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/linux_util.h"
 #include "chrome/updater/util/posix_util.h"
+#include "chrome/updater/util/unittest_util.h"
 #include "chrome/updater/util/util.h"
 #include "components/crx_file/crx_verifier.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/updater/test/integration_tests_mac.mm b/chrome/updater/test/integration_tests_mac.mm
index 01526d52..80b351d 100644
--- a/chrome/updater/test/integration_tests_mac.mm
+++ b/chrome/updater/test/integration_tests_mac.mm
@@ -34,6 +34,7 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/launchd_util.h"
 #import "chrome/updater/util/mac_util.h"
+#include "chrome/updater/util/unittest_util.h"
 #include "chrome/updater/util/util.h"
 #include "components/crx_file/crx_verifier.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/updater/util/unittest_util.cc b/chrome/updater/util/unittest_util.cc
index 0c7c1a5..3f56116 100644
--- a/chrome/updater/util/unittest_util.cc
+++ b/chrome/updater/util/unittest_util.cc
@@ -11,6 +11,8 @@
 #include "base/check.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/path_service.h"
@@ -23,6 +25,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "chrome/updater/constants.h"
@@ -422,4 +425,22 @@
 
 #endif  // BUILDFLAG(IS_WIN)
 
+bool WaitFor(base::RepeatingCallback<bool()> predicate,
+             base::RepeatingClosure still_waiting) {
+  constexpr base::TimeDelta kOutputInterval = base::Seconds(10);
+  auto notify_next = base::TimeTicks::Now() + kOutputInterval;
+  const auto deadline = base::TimeTicks::Now() + TestTimeouts::action_timeout();
+  while (base::TimeTicks::Now() < deadline) {
+    if (predicate.Run()) {
+      return true;
+    }
+    if (notify_next < base::TimeTicks::Now()) {
+      still_waiting.Run();
+      notify_next += kOutputInterval;
+    }
+    base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  }
+  return false;
+}
+
 }  // namespace updater::test
diff --git a/chrome/updater/util/unittest_util.h b/chrome/updater/util/unittest_util.h
index 671cd3fa..7db0d03 100644
--- a/chrome/updater/util/unittest_util.h
+++ b/chrome/updater/util/unittest_util.h
@@ -8,6 +8,8 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/process/process_iterator.h"
 #include "base/synchronization/waitable_event.h"
@@ -96,6 +98,13 @@
     const base::FilePath::StringType& executable_name);
 #endif
 
+// Waits for a given `predicate` to become true. Invokes `still_waiting`
+// periodically to provide a indication of progress. Returns true if the
+// predicate becomes true before a timeout, otherwise returns false.
+[[nodiscard]] bool WaitFor(
+    base::RepeatingCallback<bool()> predicate,
+    base::RepeatingClosure still_waiting = base::DoNothing());
+
 struct EventHolder {
   base::WaitableEvent event;
   std::wstring name;
diff --git a/chromecast/cast_core/runtime/browser/message_port_handler.cc b/chromecast/cast_core/runtime/browser/message_port_handler.cc
index 43afe449..e4b7c5b 100644
--- a/chromecast/cast_core/runtime/browser/message_port_handler.cc
+++ b/chromecast/cast_core/runtime/browser/message_port_handler.cc
@@ -184,9 +184,6 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!message.has_request() || !has_outstanding_request_);
   bool was_request = message.has_request();
-  if (was_request) {
-    DLOG_CHANNEL(INFO) << "Sending message: " << message.request().data();
-  }
 
   auto call = core_app_stub_->CreateCall<
       cast::v2::CoreMessagePortApplicationServiceStub::PostMessage>(
diff --git a/chromeos/ash/components/system/statistics_provider_impl.cc b/chromeos/ash/components/system/statistics_provider_impl.cc
index 6635e355..10a22f6 100644
--- a/chromeos/ash/components/system/statistics_provider_impl.cc
+++ b/chromeos/ash/components/system/statistics_provider_impl.cc
@@ -507,8 +507,14 @@
 }
 
 void StatisticsProviderImpl::LoadMachineInfoFile() {
-  if (!base::SysInfo::IsRunningOnChromeOS() &&
-      !base::PathExists(sources_.machine_info_filepath)) {
+  if (!base::PathExists(sources_.machine_info_filepath)) {
+    if (base::SysInfo::IsRunningOnChromeOS()) {
+      // This is unexpected, since the file is supposed to always be populated
+      // by write-machine-info script on ui start.
+      LOG(ERROR) << "Missing machine info: " << sources_.machine_info_filepath;
+      return;
+    }
+
     // Use time value to create an unique stub serial because clashes of the
     // same serial for the same domain invalidate earlier enrollments. Persist
     // to disk to keep it constant across restarts (required for re-enrollment
diff --git a/chromeos/ash/resources/BUILD.gn b/chromeos/ash/resources/BUILD.gn
index 6a5fde2..9a13b0a 100644
--- a/chromeos/ash/resources/BUILD.gn
+++ b/chromeos/ash/resources/BUILD.gn
@@ -36,5 +36,6 @@
     "//chromeos/ash/services/device_sync/public/mojom:mojom_js",
     "//chromeos/ash/services/hotspot_config/public/mojom:mojom_js",
     "//chromeos/ash/services/multidevice_setup/public/mojom:mojom_js",
+    "//chromeos/ash/services/nearby/public/mojom:mojom_js",
   ]
 }
diff --git a/chromeos/ash/resources/ash_resources.grd b/chromeos/ash/resources/ash_resources.grd
index f2797a7..e58d648 100644
--- a/chromeos/ash/resources/ash_resources.grd
+++ b/chromeos/ash/resources/ash_resources.grd
@@ -18,6 +18,7 @@
       <part file="hotspot_config_resources.grdp" />
       <part file="human_presence_resources.grdp" />
       <part file="multidevice_resources.grdp" />
+      <part file="nearby_resources.grdp" />
       <if expr="enable_cros_libassistant">
         <part file="assistant_resources.grdp" />
       </if>
diff --git a/chromeos/ash/resources/nearby_resources.grdp b/chromeos/ash/resources/nearby_resources.grdp
new file mode 100644
index 0000000..f3d7915
--- /dev/null
+++ b/chromeos/ash/resources/nearby_resources.grdp
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <include name="IDR_NEARBY_NEARBY_SHARE_SETTINGS_MOJOM_WEBUI_JS"
+      file="${root_gen_dir}/mojom-webui/chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-webui.js"
+      resource_path="mojo/chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-webui.js"
+      use_base_dir="false"
+      type="BINDATA" />
+  <include name="IDR_NEARBY_NEARBY_SHARE_TARGET_TYPES_MOJOM_WEBUI_JS"
+      file="${root_gen_dir}/mojom-webui/chromeos/ash/services/nearby/public/mojom/nearby_share_target_types.mojom-webui.js"
+      resource_path="mojo/chromeos/ash/services/nearby/public/mojom/nearby_share_target_types.mojom-webui.js"
+      use_base_dir="false"
+      type="BINDATA" />
+</grit-part>
diff --git a/chromeos/ash/services/auth_factor_config/auth_factor_config.cc b/chromeos/ash/services/auth_factor_config/auth_factor_config.cc
index 6cd6568..7fd28764 100644
--- a/chromeos/ash/services/auth_factor_config/auth_factor_config.cc
+++ b/chromeos/ash/services/auth_factor_config/auth_factor_config.cc
@@ -84,8 +84,8 @@
       CHECK(prefs);
       const mojom::ManagementType result =
           prefs->IsManagedPreference(prefs::kRecoveryFactorBehavior)
-              ? mojom::ManagementType::kDevice
-              : mojom::ManagementType::kUser;
+              ? mojom::ManagementType::kUser
+              : mojom::ManagementType::kNone;
 
       std::move(callback).Run(result);
       break;
diff --git a/chromeos/ash/services/nearby/public/mojom/BUILD.gn b/chromeos/ash/services/nearby/public/mojom/BUILD.gn
index ebd4500..41f7687 100644
--- a/chromeos/ash/services/nearby/public/mojom/BUILD.gn
+++ b/chromeos/ash/services/nearby/public/mojom/BUILD.gn
@@ -7,8 +7,19 @@
 
 assert(is_chromeos_ash)
 
-mojom("nearby_share_target_types") {
-  sources = [ "nearby_share_target_types.mojom" ]
+mojom("nearby_share_settings") {
+  sources = [
+    "nearby_share_settings.mojom",
+    "nearby_share_target_types.mojom",
+  ]
+
+  public_deps = [
+    "//mojo/public/mojom/base",
+    "//url/mojom:url_mojom_gurl",
+  ]
+
+  webui_module_path =
+      "chrome://resources/mojo/chromeos/ash/services/nearby/public/mojom"
 }
 
 mojom("mojom") {
@@ -28,7 +39,7 @@
   ]
 
   public_deps = [
-    ":nearby_share_target_types",
+    ":nearby_share_settings",
     "//chromeos/services/network_config/public/mojom",
     "//device/bluetooth/public/mojom:deprecated_experimental_interfaces",
     "//mojo/public/mojom/base",
diff --git a/chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom b/chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom
similarity index 100%
rename from chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom
rename to chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom
diff --git a/chromeos/ui/frame/caption_buttons/frame_size_button.cc b/chromeos/ui/frame/caption_buttons/frame_size_button.cc
index 1c85eeb..1d3dad66 100644
--- a/chromeos/ui/frame/caption_buttons/frame_size_button.cc
+++ b/chromeos/ui/frame/caption_buttons/frame_size_button.cc
@@ -253,6 +253,7 @@
   DCHECK(chromeos::wm::features::IsFloatWindowEnabled());
   DCHECK(!chromeos::TabletState::Get()->InTabletMode());
   if (!IsMultitaskMenuShown()) {
+    RecordMultitaskMenuEntryType(MultitaskMenuEntryType::kAccel);
     multitask_menu_ = new MultitaskMenu(
         /*anchor=*/this, GetWidget(),
         base::BindOnce(&FrameSizeButton::OnMultitaskMenuClosed,
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_metrics.h b/chromeos/ui/frame/multitask_menu/multitask_menu_metrics.h
index 33add5d..bf0d4c04 100644
--- a/chromeos/ui/frame/multitask_menu/multitask_menu_metrics.h
+++ b/chromeos/ui/frame/multitask_menu/multitask_menu_metrics.h
@@ -18,7 +18,8 @@
   kFrameSizeButtonLongTouch = 2,
   kGestureFling = 3,
   kGestureScroll = 4,
-  kMaxValue = kGestureScroll,
+  kAccel = 5,
+  kMaxValue = kAccel,
 };
 
 // These values are persisted to logs. Entries should not be renumbered and
diff --git a/components/android_autofill/browser/android_autofill_manager.cc b/components/android_autofill/browser/android_autofill_manager.cc
index b4ce915..48484ee 100644
--- a/components/android_autofill/browser/android_autofill_manager.cc
+++ b/components/android_autofill/browser/android_autofill_manager.cc
@@ -124,8 +124,6 @@
 
 bool AndroidAutofillManager::ShouldParseForms(
     const std::vector<FormData>& forms) {
-  if (auto* provider = GetAutofillProvider())
-    provider->OnFormsSeen(this, forms);
   // Need to parse the |forms| to FormStructure, so heuristic_type can be
   // retrieved later.
   return true;
diff --git a/components/android_autofill/browser/autofill_provider.h b/components/android_autofill/browser/autofill_provider.h
index bbf30f8e..d2dbba4 100644
--- a/components/android_autofill/browser/autofill_provider.h
+++ b/components/android_autofill/browser/autofill_provider.h
@@ -75,9 +75,6 @@
                                          const FormData& form,
                                          base::TimeTicks timestamp) = 0;
 
-  virtual void OnFormsSeen(AndroidAutofillManager* manager,
-                           const std::vector<FormData>& forms) = 0;
-
   virtual void OnHidePopup(AndroidAutofillManager* manager) = 0;
 
   virtual void OnServerPredictionsAvailable(
diff --git a/components/android_autofill/browser/autofill_provider_android.cc b/components/android_autofill/browser/autofill_provider_android.cc
index 1e56c05..b3944fa 100644
--- a/components/android_autofill/browser/autofill_provider_android.cc
+++ b/components/android_autofill/browser/autofill_provider_android.cc
@@ -360,9 +360,6 @@
   Java_AutofillProvider_onDidFillAutofillFormData(env, obj);
 }
 
-void AutofillProviderAndroid::OnFormsSeen(AndroidAutofillManager* manager,
-                                          const std::vector<FormData>& forms) {}
-
 void AutofillProviderAndroid::OnHidePopup(AndroidAutofillManager* manager) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (manager == manager_.get()) {
diff --git a/components/android_autofill/browser/autofill_provider_android.h b/components/android_autofill/browser/autofill_provider_android.h
index 9cff6dd..4d95d6c 100644
--- a/components/android_autofill/browser/autofill_provider_android.h
+++ b/components/android_autofill/browser/autofill_provider_android.h
@@ -80,8 +80,6 @@
   void OnDidFillAutofillFormData(AndroidAutofillManager* manager,
                                  const FormData& form,
                                  base::TimeTicks timestamp) override;
-  void OnFormsSeen(AndroidAutofillManager* manager,
-                   const std::vector<FormData>& forms) override;
   void OnHidePopup(AndroidAutofillManager* manager) override;
   void OnServerPredictionsAvailable(AndroidAutofillManager* manager) override;
   void OnServerQueryRequestError(AndroidAutofillManager* manager,
diff --git a/components/android_autofill/browser/test_autofill_provider.h b/components/android_autofill/browser/test_autofill_provider.h
index f46da8c..6930ff1 100644
--- a/components/android_autofill/browser/test_autofill_provider.h
+++ b/components/android_autofill/browser/test_autofill_provider.h
@@ -53,8 +53,6 @@
   void OnDidFillAutofillFormData(AndroidAutofillManager* manager,
                                  const FormData& form,
                                  base::TimeTicks timestamp) override {}
-  void OnFormsSeen(AndroidAutofillManager* manager,
-                   const std::vector<FormData>& forms) override {}
   void OnHidePopup(AndroidAutofillManager* manager) override {}
   void OnServerPredictionsAvailable(AndroidAutofillManager* manager) override {}
   void OnServerQueryRequestError(AndroidAutofillManager* manager,
diff --git a/components/autofill/core/browser/autofill_client.cc b/components/autofill/core/browser/autofill_client.cc
index 64b0f81..c3c09c7d 100644
--- a/components/autofill/core/browser/autofill_client.cc
+++ b/components/autofill/core/browser/autofill_client.cc
@@ -21,13 +21,11 @@
     const gfx::RectF& element_bounds,
     base::i18n::TextDirection text_direction,
     std::vector<Suggestion> suggestions,
-    AutoselectFirstSuggestion autoselect_first_suggestion,
-    PopupType popup_type)
+    AutoselectFirstSuggestion autoselect_first_suggestion)
     : element_bounds(element_bounds),
       text_direction(text_direction),
       suggestions(std::move(suggestions)),
-      autoselect_first_suggestion(autoselect_first_suggestion),
-      popup_type(popup_type) {}
+      autoselect_first_suggestion(autoselect_first_suggestion) {}
 AutofillClient::PopupOpenArgs::PopupOpenArgs(
     const AutofillClient::PopupOpenArgs&) = default;
 AutofillClient::PopupOpenArgs::PopupOpenArgs(AutofillClient::PopupOpenArgs&&) =
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 9ecbc50..7035196 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -272,8 +272,7 @@
     PopupOpenArgs(const gfx::RectF& element_bounds,
                   base::i18n::TextDirection text_direction,
                   std::vector<Suggestion> suggestions,
-                  AutoselectFirstSuggestion autoselect_first_suggestion,
-                  PopupType popup_type);
+                  AutoselectFirstSuggestion autoselect_first_suggestion);
     PopupOpenArgs(const PopupOpenArgs&);
     PopupOpenArgs(PopupOpenArgs&&);
     ~PopupOpenArgs();
@@ -285,7 +284,6 @@
         base::i18n::TextDirection::UNKNOWN_DIRECTION;
     std::vector<Suggestion> suggestions;
     AutoselectFirstSuggestion autoselect_first_suggestion{false};
-    PopupType popup_type = PopupType::kUnspecified;
   };
 
   // Callback to run after local credit card save is offered. Sends whether the
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index 96ace34..97ab38b 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -151,7 +151,7 @@
   if (query_field_.is_focusable && driver_->CanShowAutofillUi()) {
     AutofillClient::PopupOpenArgs open_args(
         element_bounds_, query_field_.text_direction, suggestions,
-        AutoselectFirstSuggestion(autoselect_first_suggestion), popup_type_);
+        AutoselectFirstSuggestion(autoselect_first_suggestion));
     manager_->client()->ShowAutofillPopup(open_args, GetWeakPtr());
   }
 }
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index 61babf2..f93ceb8 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -263,7 +263,6 @@
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 
   EXPECT_CALL(*browser_autofill_manager_,
               FillOrPreviewForm(mojom::RendererFormDataAction::kFill, _, _, _));
@@ -307,7 +306,6 @@
                    "Signin_Impression_FromAutofillDropdown"));
   EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 
   EXPECT_CALL(*browser_autofill_manager_,
               FillOrPreviewForm(mojom::RendererFormDataAction::kFill, _, _, _));
@@ -346,7 +344,6 @@
                    "Signin_Impression_FromAutofillDropdown"));
   EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 
   EXPECT_CALL(autofill_client_,
               ExecuteCommand(autofill::POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO));
@@ -395,7 +392,6 @@
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 
   // Try calling OnSuggestionsReturned with no Autofill values and ensure
   // the datalist items are still shown.
@@ -411,7 +407,6 @@
               SuggestionVectorIdsAre(testing::ElementsAre(
                   static_cast<int>(POPUP_ITEM_ID_DATALIST_ENTRY))));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 // Test that datalist values can get updated while a popup is showing.
@@ -454,7 +449,6 @@
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 
   // This would normally get called from ShowAutofillPopup, but it is mocked so
   // we need to call OnPopupShown ourselves.
@@ -512,7 +506,6 @@
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 // Test that we de-dupe autocomplete values against datalist values, keeping the
@@ -557,7 +550,6 @@
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions, SuggestionVectorIdsAre(element_ids));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 // Test that the Autofill popup is able to display warnings explaining why
@@ -585,7 +577,6 @@
   EXPECT_EQ(open_args.element_bounds, gfx::RectF());
   EXPECT_EQ(open_args.text_direction, base::i18n::UNKNOWN_DIRECTION);
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 // Test that Autofill warnings are removed if there are also autocomplete
@@ -615,7 +606,6 @@
               SuggestionVectorIdsAre(testing::ElementsAre(
                   static_cast<int>(POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY))));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 // Test that the Autofill delegate doesn't try and fill a form with a
@@ -1013,7 +1003,6 @@
               SuggestionVectorStoreIndicatorIconsAre(element_icons));
 #endif
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 TEST_F(AutofillExternalDelegateUnitTest,
@@ -1038,7 +1027,6 @@
       field_id_, autofill_item, AutoselectFirstSuggestion(false), false);
   EXPECT_THAT(open_args.suggestions, SuggestionVectorIconsAre(element_icons));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 TEST_F(AutofillExternalDelegateUnitTest, ShouldUseNewSettingName) {
@@ -1066,7 +1054,6 @@
   EXPECT_THAT(open_args.suggestions,
               SuggestionVectorMainTextsAre(element_main_texts));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 // Test that browser autofill manager will handle the unmasking request for the
@@ -1120,7 +1107,6 @@
   EXPECT_THAT(open_args.suggestions,
               SuggestionVectorMainTextsAre(element_main_texts));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 // Tests that the prompt to show account cards shows up when the corresponding
@@ -1142,7 +1128,6 @@
   EXPECT_THAT(open_args.suggestions,
               SuggestionVectorMainTextsAre(element_main_texts));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPersonalInformation);
 }
 
 #if BUILDFLAG(IS_IOS)
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.cc b/components/autofill/core/browser/autofill_suggestion_generator.cc
index 04ff8fd2..a185315f 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -254,7 +254,7 @@
 
 // static
 std::vector<Suggestion> AutofillSuggestionGenerator::GetSuggestionsForIBANs(
-    const std::vector<IBAN*>& ibans) {
+    const std::vector<const IBAN*>& ibans) {
   std::vector<Suggestion> suggestions;
   for (const IBAN* iban : ibans) {
     Suggestion& suggestion = suggestions.emplace_back(iban->value());
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.h b/components/autofill/core/browser/autofill_suggestion_generator.h
index 592befa..24bfb16 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.h
+++ b/components/autofill/core/browser/autofill_suggestion_generator.h
@@ -81,7 +81,7 @@
 
   // Generates suggestions for all available IBANs.
   static std::vector<Suggestion> GetSuggestionsForIBANs(
-      const std::vector<IBAN*>& ibans);
+      const std::vector<const IBAN*>& ibans);
 
   // Converts the vector of promo code offers that is passed in to a vector of
   // suggestions that can be displayed to the user for a promo code field.
diff --git a/components/autofill/core/browser/iban_manager.cc b/components/autofill/core/browser/iban_manager.cc
index 03206ae..ce208625 100644
--- a/components/autofill/core/browser/iban_manager.cc
+++ b/components/autofill/core/browser/iban_manager.cc
@@ -70,10 +70,19 @@
     return;
   }
 
+  // Only return IBAN-based suggestions whose prefix match `prefix_`.
+  std::vector<const IBAN*> suggested_ibans;
+  suggested_ibans.reserve(ibans.size());
+  for (const auto* iban : ibans) {
+    if (base::StartsWith(iban->value(), query_handler.prefix_)) {
+      suggested_ibans.push_back(iban);
+    }
+  }
+
   // Return suggestions to query handler.
   query_handler.handler_->OnSuggestionsReturned(
       query_handler.field_id_, query_handler.autoselect_first_suggestion_,
-      AutofillSuggestionGenerator::GetSuggestionsForIBANs(ibans));
+      AutofillSuggestionGenerator::GetSuggestionsForIBANs(suggested_ibans));
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/iban_manager_unittest.cc b/components/autofill/core/browser/iban_manager_unittest.cc
index 3ea9641..2dea5260 100644
--- a/components/autofill/core/browser/iban_manager_unittest.cc
+++ b/components/autofill/core/browser/iban_manager_unittest.cc
@@ -19,6 +19,13 @@
 
 namespace autofill {
 
+constexpr char16_t kIbanValue_0[] = u"IE12 BOFI 9000 0112 3456 78";
+constexpr char16_t kIbanValue_1[] = u"CH56 0483 5012 3456 7800 9";
+constexpr char16_t kIbanValue_2[] = u"CH56 9483 5012 3456 7800 9";
+
+constexpr char16_t kNickname_0[] = u"Nickname 0";
+constexpr char16_t kNickname_1[] = u"Nickname 1";
+
 namespace {
 
 class MockSuggestionsHandler : public IBANManager::SuggestionsHandler {
@@ -79,9 +86,9 @@
 
 TEST_F(IBANManagerTest, ShowsIBANSuggestions) {
   Suggestion iban_suggestion_0 =
-      SetUpIBANAndSuggestion(u"IE12 BOFI 9000 0112 3456 78", u"Nickname 0");
+      SetUpIBANAndSuggestion(kIbanValue_0, kNickname_0);
   Suggestion iban_suggestion_1 =
-      SetUpIBANAndSuggestion(u"CH56 0483 5012 3456 7800 9", u"Nickname 1");
+      SetUpIBANAndSuggestion(kIbanValue_1, kNickname_1);
 
   SuggestionsContext context;
   FormFieldData test_field;
@@ -106,15 +113,15 @@
       /*context=*/context));
 }
 
-TEST_F(IBANManagerTest, ShowsIBANSuggestions_OnlyPrefixMatch) {
-  base::StringPiece16 value_0 = u"IE12 BOFI 9000 0112 3456 78";
-  Suggestion iban_suggestion_0 = SetUpIBANAndSuggestion(value_0, u"Nickname 0");
+TEST_F(IBANManagerTest, ShowsIBANSuggestions_NoSuggestion) {
+  Suggestion iban_suggestion_0 =
+      SetUpIBANAndSuggestion(kIbanValue_0, kNickname_0);
   Suggestion iban_suggestion_1 =
-      SetUpIBANAndSuggestion(u"CH56 0483 5012 3456 7800 9", u"Nickname 1");
+      SetUpIBANAndSuggestion(kIbanValue_1, kNickname_1);
 
   SuggestionsContext context;
   FormFieldData test_field;
-  test_field.value = std::u16string(value_0);
+  test_field.value = std::u16string(kIbanValue_0);
 
   // Setting up mock to verify that the handler is not returned any iban-based
   // suggestions as the field already contains an iban.
@@ -135,8 +142,79 @@
       /*context=*/context));
 }
 
+TEST_F(IBANManagerTest, ShowsIBANSuggestions_OnlyPrefixMatch) {
+  Suggestion iban_suggestion_0 =
+      SetUpIBANAndSuggestion(kIbanValue_1, kNickname_0);
+  Suggestion iban_suggestion_1 =
+      SetUpIBANAndSuggestion(kIbanValue_2, kNickname_1);
+
+  SuggestionsContext context;
+  FormFieldData test_field;
+  test_field.value = u"CH56";
+
+  // Setting up mock to verify that the handler is returned a list of
+  // iban-based suggestions whose prefixes match `prefix_`. Both values should
+  // be returned because they both start with CH56.
+  EXPECT_CALL(
+      suggestions_handler_,
+      OnSuggestionsReturned(
+          test_field.global_id(), AutoselectFirstSuggestion(false),
+          UnorderedElementsAre(
+              Field(&Suggestion::main_text, iban_suggestion_0.main_text),
+              Field(&Suggestion::main_text, iban_suggestion_1.main_text))))
+      .Times(1);
+
+  // Simulate request for suggestions.
+  // Because all criteria are met to trigger returning to the handler,
+  // the handler should be triggered and this should return true.
+  EXPECT_TRUE(iban_manager_.OnGetSingleFieldSuggestions(
+      AutoselectFirstSuggestion(false), test_field, autofill_client_,
+      suggestions_handler_.GetWeakPtr(),
+      /*context=*/context));
+
+  test_field.value = u"CH56 04";
+
+  // Setting up mock to verify that the handler is returned only one
+  // iban-based suggestion whose prefix matches `prefix_`. Only one of the two
+  // IBANs should stay because the other will be filtered out.
+  EXPECT_CALL(suggestions_handler_,
+              OnSuggestionsReturned(
+                  test_field.global_id(), AutoselectFirstSuggestion(false),
+                  UnorderedElementsAre(Field(&Suggestion::main_text,
+                                             iban_suggestion_0.main_text))))
+      .Times(1);
+
+  // Simulate request for suggestions.
+  // Because all criteria are met to trigger returning to the handler,
+  // the handler should be triggered and this should return true.
+  EXPECT_TRUE(iban_manager_.OnGetSingleFieldSuggestions(
+      AutoselectFirstSuggestion(false), test_field, autofill_client_,
+      suggestions_handler_.GetWeakPtr(),
+      /*context=*/context));
+
+  test_field.value = u"AB56";
+
+  // Setting up mock to verify that the handler does not return any
+  // iban-based suggestion as no prefix matches `prefix_`.
+  EXPECT_CALL(suggestions_handler_,
+              OnSuggestionsReturned(
+                  _, _,
+                  testing::Truly(
+                      [](const std::vector<Suggestion>& returned_suggestions) {
+                        return returned_suggestions.empty();
+                      })));
+
+  // Simulate request for suggestions.
+  // Because all criteria are met to trigger returning to the handler,
+  // the handler should be triggered and this should return true.
+  EXPECT_TRUE(iban_manager_.OnGetSingleFieldSuggestions(
+      AutoselectFirstSuggestion(false), test_field, autofill_client_,
+      suggestions_handler_.GetWeakPtr(),
+      /*context=*/context));
+}
+
 TEST_F(IBANManagerTest, DoesNotShowIBANsForOffTheRecord) {
-  IBAN iban_0 = SetUpIBAN(u"IE12 BOFI 9000 0112 3456 78", u"Nickname 0");
+  IBAN iban_0 = SetUpIBAN(kIbanValue_0, kNickname_0);
   iban_manager_.SetOffTheRecordForTesting(true);
   SuggestionsContext context;
   FormFieldData test_field;
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h
index 38166a67..4428c73 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h
@@ -40,6 +40,9 @@
   virtual base::TimeDelta GetSuccessMessageDuration() const = 0;
   virtual AutofillClient::PaymentsRpcResult GetVerificationResult() const = 0;
   virtual bool IsVirtualCard() const = 0;
+#if !BUILDFLAG(IS_IOS)
+  virtual int GetCvcTooltipResourceId() = 0;
+#endif
 
   // Utilities.
   virtual bool InputCvcIsValid(const std::u16string& input_text) const = 0;
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc
index 2166c191..ceb0615 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc
@@ -378,6 +378,14 @@
   return card_.record_type() == CreditCard::VIRTUAL_CARD;
 }
 
+#if !BUILDFLAG(IS_IOS)
+int CardUnmaskPromptControllerImpl::GetCvcTooltipResourceId() {
+  return IsCvcInFront()
+             ? IDS_AUTOFILL_CARD_UNMASK_CVC_IMAGE_DESCRIPTION_FOR_AMEX
+             : IDS_AUTOFILL_CARD_UNMASK_CVC_IMAGE_DESCRIPTION;
+}
+#endif
+
 bool CardUnmaskPromptControllerImpl::AllowsRetry(
     AutofillClient::PaymentsRpcResult result) {
   if (result == AutofillClient::PaymentsRpcResult::kNetworkError ||
@@ -391,6 +399,19 @@
   return true;
 }
 
+bool CardUnmaskPromptControllerImpl::IsCvcInFront() {
+  // Rely on the challenge option to inform us whether the CVC to be entered is
+  // on the front or back of the card.
+  if (IsChallengeOptionPresent()) {
+    return card_unmask_prompt_options_.challenge_option->cvc_position ==
+           CvcPosition::kFrontOfCard;
+  }
+
+  // For normal CVC unmask case, depend on the network to inform where the CVC
+  // is on the card.
+  return card_.network() == kAmericanExpressCard;
+}
+
 bool CardUnmaskPromptControllerImpl::ShouldDismissUnmaskPromptUponResult(
     AutofillClient::PaymentsRpcResult result) {
 #if BUILDFLAG(IS_ANDROID)
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h
index 46778ad..80ea7ca0 100644
--- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h
+++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h
@@ -74,6 +74,9 @@
   base::TimeDelta GetSuccessMessageDuration() const override;
   AutofillClient::PaymentsRpcResult GetVerificationResult() const override;
   bool IsVirtualCard() const override;
+#if !BUILDFLAG(IS_IOS)
+  int GetCvcTooltipResourceId() override;
+#endif
 
  protected:
   // Exposed for testing.
@@ -81,6 +84,7 @@
 
  private:
   bool AllowsRetry(AutofillClient::PaymentsRpcResult result);
+  bool IsCvcInFront();
   bool ShouldDismissUnmaskPromptUponResult(
       AutofillClient::PaymentsRpcResult result);
   void LogOnCloseEvents();
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index 895dbe40..49196d11 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -337,6 +337,9 @@
     <message name="IDS_AUTOFILL_CARD_UNMASK_CVC_IMAGE_DESCRIPTION" desc="Accessible description for the CVC image. It should describe where to find the CVC on a credit card.">
       The CVC is located behind your card.
     </message>
+    <message name="IDS_AUTOFILL_CARD_UNMASK_CVC_IMAGE_DESCRIPTION_FOR_AMEX" desc="Accessible description for the CVC image. It should describe where to find the CVC on an American Express credit card.">
+      The CVC is located on the front of your card.
+    </message>
   </if>
   <if expr="is_ios">
     <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE" desc="Title for the credit card unmasking dialog.">
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_CVC_IMAGE_DESCRIPTION_FOR_AMEX.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_CVC_IMAGE_DESCRIPTION_FOR_AMEX.png.sha1
new file mode 100644
index 0000000..559ed6fe
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_CVC_IMAGE_DESCRIPTION_FOR_AMEX.png.sha1
@@ -0,0 +1 @@
+dd1d970565ea18220a29cb1df2a4f4d515af7d7c
\ No newline at end of file
diff --git a/components/browser_ui/site_settings/android/BUILD.gn b/components/browser_ui/site_settings/android/BUILD.gn
index 115f246..a9b64c2 100644
--- a/components/browser_ui/site_settings/android/BUILD.gn
+++ b/components/browser_ui/site_settings/android/BUILD.gn
@@ -222,7 +222,7 @@
     "java/res/layout/clear_data_dialog.xml",
     "java/res/layout/clear_reset_dialog.xml",
     "java/res/layout/clear_storage.xml",
-    "java/res/layout/edit_site_dialog_radio_group.xml",
+    "java/res/layout/edit_site_dialog_content.xml",
     "java/res/layout/four_state_cookie_settings_preference.xml",
     "java/res/layout/storage_preferences_view.xml",
     "java/res/layout/tri_state_cookie_settings_preference.xml",
diff --git a/components/browser_ui/site_settings/android/java/res/layout/edit_site_dialog_content.xml b/components/browser_ui/site_settings/android/java/res/layout/edit_site_dialog_content.xml
new file mode 100644
index 0000000..d649d4f
--- /dev/null
+++ b/components/browser_ui/site_settings/android/java/res/layout/edit_site_dialog_content.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"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/message"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingStart="@dimen/dialog_message_padding_start"
+        android:paddingEnd="@dimen/dialog_message_padding_end"
+        android:paddingBottom="@dimen/dialog_message_padding_bottom"
+        android:paddingTop="@dimen/dialog_message_padding_top"
+        android:textAppearance="@style/TextAppearance.TextLarge.Secondary"
+        android:textAlignment="viewStart" />
+
+    <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
+        android:id="@+id/radio_button_group"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
+            android:id="@+id/allow"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/radio_button_with_description_height" />
+
+        <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
+            android:id="@+id/block"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/radio_button_with_description_height" />
+
+    </org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
+
+</LinearLayout>
diff --git a/components/browser_ui/site_settings/android/java/res/layout/edit_site_dialog_radio_group.xml b/components/browser_ui/site_settings/android/java/res/layout/edit_site_dialog_radio_group.xml
deleted file mode 100644
index b5030b2d..0000000
--- a/components/browser_ui/site_settings/android/java/res/layout/edit_site_dialog_radio_group.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-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.
--->
-
-<org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:paddingTop="@dimen/radio_button_with_description_layout_padding_top"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
-        android:id="@+id/allow"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/radio_button_with_description_height" />
-
-    <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
-        android:id="@+id/block"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/radio_button_with_description_height" />
-
-</org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
diff --git a/components/browser_ui/site_settings/android/java/res/values/dimens.xml b/components/browser_ui/site_settings/android/java/res/values/dimens.xml
index 5a429c8..54fb471 100644
--- a/components/browser_ui/site_settings/android/java/res/values/dimens.xml
+++ b/components/browser_ui/site_settings/android/java/res/values/dimens.xml
@@ -6,6 +6,9 @@
 -->
 
 <resources>
-    <dimen name="radio_button_with_description_layout_padding_top">8dp</dimen>
+    <dimen name="dialog_message_padding_top">16dp</dimen>
+    <dimen name="dialog_message_padding_bottom">8dp</dimen>
+    <dimen name="dialog_message_padding_start">24dp</dimen>
+    <dimen name="dialog_message_padding_end">18dp</dimen>
     <dimen name="radio_button_with_description_height">48dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
index bfe8aa2..98bcb983 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
@@ -27,6 +27,8 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.IntDef;
@@ -1377,10 +1379,8 @@
 
         AlertDialog alertDialog =
                 new AlertDialog.Builder(getContext(), R.style.ThemeOverlay_BrowserUI_AlertDialog)
-                        .setTitle(String.format(
-                                getContext().getString(
-                                        R.string.website_settings_edit_site_dialog_title),
-                                site.getTitleForPreferenceRow()))
+                        .setTitle(getContext().getString(
+                                R.string.website_settings_edit_site_dialog_title))
                         .setPositiveButton(R.string.cancel, null)
                         .setNegativeButton(R.string.remove,
                                 (dialog, which) -> {
@@ -1400,12 +1400,19 @@
                                 })
                         .create();
 
-        // Set custom radio button group layout that uses RadioButtonWithDescriptionLayout.
+        // Set a custom view with description text and a radio button group that uses
+        // RadioButtonWithDescriptionLayout.
         var inflater =
                 (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        var radioGroup = (RadioButtonWithDescriptionLayout) inflater.inflate(
-                R.layout.edit_site_dialog_radio_group, null);
+        var contentView = (LinearLayout) inflater.inflate(R.layout.edit_site_dialog_content, null);
 
+        TextView messageView = contentView.findViewById(R.id.message);
+        messageView.setText(
+                getContext().getString(R.string.website_settings_edit_site_dialog_description,
+                        site.getTitleForPreferenceRow()));
+
+        RadioButtonWithDescriptionLayout radioGroup =
+                contentView.findViewById(R.id.radio_button_group);
         RadioButtonWithDescription allowButton = radioGroup.findViewById(R.id.allow);
         allowButton.setPrimaryText(getString(ContentSettingsResources.getSiteSummary(
                 ContentSettingValues.ALLOW, contentSettingsType)));
@@ -1432,7 +1439,7 @@
             getInfoForOrigins();
             alertDialog.dismiss();
         });
-        alertDialog.setView(radioGroup);
+        alertDialog.setView(contentView);
         return alertDialog;
     }
 
diff --git a/components/browser_ui/strings/android/site_settings.grdp b/components/browser_ui/strings/android/site_settings.grdp
index 0c01c8e..29f9e1a 100644
--- a/components/browser_ui/strings/android/site_settings.grdp
+++ b/components/browser_ui/strings/android/site_settings.grdp
@@ -258,7 +258,10 @@
   <!-- Site settings Edit Exception dialog -->
 
   <message name="IDS_WEBSITE_SETTINGS_EDIT_SITE_DIALOG_TITLE" desc="The title of the dialog to edit permissions for a website from a list.">
-    Site preference for <ph name="SITE_NAME">%1$s<ex>google.com</ex></ph>
+    Site preference
+  </message>
+  <message name="IDS_WEBSITE_SETTINGS_EDIT_SITE_DIALOG_DESCRIPTION" desc="The description of the dialog to edit permissions for a website from a list.">
+    Select an option for <ph name="SITE_NAME">%1$s<ex>google.com</ex></ph>
   </message>
 
   <!-- Site settings Clear Data Dialog and other ways of resetting permissions -->
diff --git a/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_EDIT_SITE_DIALOG_DESCRIPTION.png.sha1 b/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_EDIT_SITE_DIALOG_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..2fe387e
--- /dev/null
+++ b/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_EDIT_SITE_DIALOG_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+ed2dcad3e43a94ea2c32f33b22e0cb5280411486
\ No newline at end of file
diff --git a/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_EDIT_SITE_DIALOG_TITLE.png.sha1 b/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_EDIT_SITE_DIALOG_TITLE.png.sha1
index 76a8836..2fe387e 100644
--- a/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_EDIT_SITE_DIALOG_TITLE.png.sha1
+++ b/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_EDIT_SITE_DIALOG_TITLE.png.sha1
@@ -1 +1 @@
-f6e3ee7c59b5b773b2fab73d47a3c8e94b6ed47f
\ No newline at end of file
+ed2dcad3e43a94ea2c32f33b22e0cb5280411486
\ No newline at end of file
diff --git a/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc b/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc
index eea26db0..c2b0c39 100644
--- a/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc
+++ b/components/cast_streaming/renderer/playback_command_forwarding_renderer.cc
@@ -260,9 +260,7 @@
 }
 
 void PlaybackCommandForwardingRenderer::OnFallback(
-    media::PipelineStatus status) {
-  NOTREACHED();
-}
+    media::PipelineStatus status) {}
 
 void PlaybackCommandForwardingRenderer::OnEnded() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
diff --git a/components/commerce/content/browser/web_contents_wrapper.cc b/components/commerce/content/browser/web_contents_wrapper.cc
index 9840fd79..2624d978 100644
--- a/components/commerce/content/browser/web_contents_wrapper.cc
+++ b/components/commerce/content/browser/web_contents_wrapper.cc
@@ -16,7 +16,7 @@
 
 const GURL& WebContentsWrapper::GetLastCommittedURL() {
   if (!web_contents_)
-    return std::move(GURL());
+    return GURL::EmptyGURL();
 
   return web_contents_->GetLastCommittedURL();
 }
diff --git a/components/commerce/core/commerce_feature_list.cc b/components/commerce/core/commerce_feature_list.cc
index ac25489..e8292ef 100644
--- a/components/commerce/core/commerce_feature_list.cc
+++ b/components/commerce/core/commerce_feature_list.cc
@@ -139,15 +139,10 @@
 BASE_FEATURE(kShoppingPDPMetrics,
              "ShoppingPDPMetrics",
              base::FEATURE_DISABLED_BY_DEFAULT);
-#if BUILDFLAG(IS_ANDROID)
+
 BASE_FEATURE(kShoppingPDPMetricsRegionLaunched,
              "ShoppingPDPMetricsRegionLaunched",
              base::FEATURE_ENABLED_BY_DEFAULT);
-#else
-BASE_FEATURE(kShoppingPDPMetricsRegionLaunched,
-             "ShoppingPDPMetricsRegionLaunched",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-#endif  // BUILDFLAG(IS_ANDROID)
 
 BASE_FEATURE(kRetailCoupons, "RetailCoupons", base::FEATURE_ENABLED_BY_DEFAULT);
 
diff --git a/components/commerce/core/subscriptions/subscriptions_manager.cc b/components/commerce/core/subscriptions/subscriptions_manager.cc
index b6ae5f2..36c6478 100644
--- a/components/commerce/core/subscriptions/subscriptions_manager.cc
+++ b/components/commerce/core/subscriptions/subscriptions_manager.cc
@@ -54,12 +54,8 @@
       account_checker_(account_checker),
       observers_(base::ObserverListPolicy::EXISTING_ONLY),
       weak_ptr_factory_(this) {
-// Avoid duplicate server calls on android. Remove this after we integrate
-// android implementation to shopping service.
-#if !BUILDFLAG(IS_ANDROID)
   SyncSubscriptions();
   scoped_identity_manager_observation_.Observe(identity_manager);
-#endif  // !BUILDFLAG(IS_ANDROID)
 }
 
 SubscriptionsManager::~SubscriptionsManager() = default;
diff --git a/components/commerce/ios/browser/web_state_wrapper.mm b/components/commerce/ios/browser/web_state_wrapper.mm
index 727e714..0770615e 100644
--- a/components/commerce/ios/browser/web_state_wrapper.mm
+++ b/components/commerce/ios/browser/web_state_wrapper.mm
@@ -22,7 +22,7 @@
 
 const GURL& WebStateWrapper::GetLastCommittedURL() {
   if (!web_state_)
-    return std::move(GURL());
+    return GURL::EmptyGURL();
 
   return web_state_->GetLastCommittedURL();
 }
diff --git a/components/embedder_support/android/BUILD.gn b/components/embedder_support/android/BUILD.gn
index c5a5768..90de5ed 100644
--- a/components/embedder_support/android/BUILD.gn
+++ b/components/embedder_support/android/BUILD.gn
@@ -137,6 +137,7 @@
     "//base:base_java",
     "//content/public/android:content_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//ui/accessibility:ax_base_java",
     "//ui/android:ui_no_recycler_view_java",
   ]
   sources = [
diff --git a/components/embedder_support/android/DEPS b/components/embedder_support/android/DEPS
index 4defb0b0..f874c12 100644
--- a/components/embedder_support/android/DEPS
+++ b/components/embedder_support/android/DEPS
@@ -10,6 +10,7 @@
   "+content/public/common",
   "+net/android/java/src/org/chromium/net",
   "+third_party/blink/public/common/context_menu_data/context_menu_data.h",
+  "+ui/accessibility",
   "+ui/android",
   "+ui/base",
   "+ui/gfx"
diff --git a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
index 0c7749e..6da0fff 100644
--- a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
+++ b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
@@ -34,6 +34,7 @@
 import org.chromium.content_public.browser.ViewEventSink;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsAccessibility;
+import org.chromium.ui.accessibility.AccessibilityState;
 import org.chromium.ui.base.EventForwarder;
 import org.chromium.ui.base.EventOffsetHandler;
 
@@ -424,8 +425,7 @@
     public boolean onHoverEvent(MotionEvent event) {
         EventForwarder forwarder = getEventForwarder();
         boolean consumed = forwarder != null ? forwarder.onHoverEvent(event) : false;
-        WebContentsAccessibility wcax = getWebContentsAccessibility();
-        if (wcax != null && !wcax.isTouchExplorationEnabled()) super.onHoverEvent(event);
+        if (!AccessibilityState.hasTouchExplorationEnabled()) super.onHoverEvent(event);
         return consumed;
     }
 
diff --git a/components/history/core/browser/sync/history_sync_bridge.cc b/components/history/core/browser/sync/history_sync_bridge.cc
index 03d2f648..f3519ae 100644
--- a/components/history/core/browser/sync/history_sync_bridge.cc
+++ b/components/history/core/browser/sync/history_sync_bridge.cc
@@ -12,6 +12,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "components/history/core/browser/history_types.h"
 #include "components/history/core/browser/sync/history_sync_metadata_database.h"
 #include "components/history/core/browser/sync/visit_id_remapper.h"
 #include "components/history/core/browser/url_row.h"
@@ -270,12 +271,14 @@
   return annotations;
 }
 
+// `included_visit_ids` may be nullptr.
 std::unique_ptr<syncer::EntityData> MakeEntityData(
     const std::string& local_cache_guid,
     const std::vector<AnnotatedVisit>& redirect_visits,
     bool redirect_chain_middle_trimmed,
     const GURL& referrer_url,
-    const std::vector<GURL>& favicon_urls) {
+    const std::vector<GURL>& favicon_urls,
+    std::vector<VisitID>* included_visit_ids) {
   DCHECK(!local_cache_guid.empty());
   DCHECK(!redirect_visits.empty());
 
@@ -300,6 +303,12 @@
   for (const AnnotatedVisit& annotated_visit : redirect_visits) {
     const URLRow& url = annotated_visit.url_row;
     const VisitRow& visit = annotated_visit.visit_row;
+
+    // Add the visit ID to the out param vector indicating it was included.
+    if (included_visit_ids) {
+      included_visit_ids->push_back(visit.visit_id);
+    }
+
     auto* redirect_entry = history->add_redirect_entries();
     redirect_entry->set_originator_visit_id(
         is_local_entity ? visit.visit_id : visit.originator_visit_id);
@@ -627,8 +636,11 @@
       continue;
     }
 
+    // Purposely don't mark visits as known to sync here, as this bit must have
+    // already been set before, when the visit was first seen by Sync.
     std::vector<std::unique_ptr<syncer::EntityData>> entity_data_list =
-        QueryRedirectChainAndMakeEntityData(final_visit);
+        QueryRedirectChainAndMakeEntityData(final_visit,
+                                            /*included_visit_ids=*/nullptr);
     // Typically, `entity_data_list` will have exactly one entry. In some error
     // cases (corrupted DB), it may be empty, and in some cases the redirect
     // chain may have been split into multiple entities. In that case, the last
@@ -893,10 +905,12 @@
     return;
   }
 
-  // All conditions are fulfilled - convert the visit into Sync's format and
-  // send it on.
+  // Attempt converting the the visit into Sync's format. In some cases, the
+  // conversion process catches additional un-syncable conditions, so early exit
+  // to account for that case as well.
+  std::vector<VisitID> included_visit_ids;
   std::vector<std::unique_ptr<syncer::EntityData>> entity_data_list =
-      QueryRedirectChainAndMakeEntityData(visit_row);
+      QueryRedirectChainAndMakeEntityData(visit_row, &included_visit_ids);
 
   std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
       CreateMetadataChangeList();
@@ -906,11 +920,17 @@
     change_processor()->Put(storage_key, std::move(entity_data),
                             metadata_change_list.get());
   }
+
+  // Mark these visits as sent in the database. They are sent in a few seconds.
+  for (auto visit_id : included_visit_ids) {
+    history_backend_->MarkVisitAsKnownToSync(visit_id);
+  }
 }
 
 std::vector<std::unique_ptr<syncer::EntityData>>
 HistorySyncBridge::QueryRedirectChainAndMakeEntityData(
-    const VisitRow& final_visit) {
+    const VisitRow& final_visit,
+    std::vector<VisitID>* included_visit_ids) {
   // Query the redirect chain that ended in this visit.
   std::vector<VisitRow> redirect_visits =
       history_backend_->GetRedirectChain(final_visit);
@@ -982,7 +1002,7 @@
     // Note: `favicon_urls` may legitimately be empty, that's fine.
     entities.push_back(MakeEntityData(GetLocalCacheGuid(), annotated_visits,
                                       chain_middle_trimmed, referrer_url,
-                                      favicon_urls));
+                                      favicon_urls, included_visit_ids));
   }
   return entities;
 }
diff --git a/components/history/core/browser/sync/history_sync_bridge.h b/components/history/core/browser/sync/history_sync_bridge.h
index 56acc24..8f55d16 100644
--- a/components/history/core/browser/sync/history_sync_bridge.h
+++ b/components/history/core/browser/sync/history_sync_bridge.h
@@ -101,9 +101,12 @@
   // and creates the corresponding EntityData(s). Typically returns a single
   // EntityData, but in some cases the redirect chain may have to be split up
   // into multiple entities. May return no entities at all in case of
-  // HistoryBackend failure (e.g. corrupted DB).
+  // HistoryBackend failure (e.g. corrupted DB). The local visit IDs that are
+  // included in the entity data will be appended to `included_visit_ids`, if it
+  // is not nullptr.
   std::vector<std::unique_ptr<syncer::EntityData>>
-  QueryRedirectChainAndMakeEntityData(const VisitRow& final_visit);
+  QueryRedirectChainAndMakeEntityData(const VisitRow& final_visit,
+                                      std::vector<VisitID>* included_visit_ids);
 
   GURL GetURLForVisit(VisitID visit_id);
 
diff --git a/components/history/core/browser/sync/history_sync_bridge_unittest.cc b/components/history/core/browser/sync/history_sync_bridge_unittest.cc
index e1afdac..2f75742 100644
--- a/components/history/core/browser/sync/history_sync_bridge_unittest.cc
+++ b/components/history/core/browser/sync/history_sync_bridge_unittest.cc
@@ -691,6 +691,12 @@
       ui::PAGE_TRANSITION_TYPED));
   EXPECT_FALSE(history.page_transition().forward_back());
   EXPECT_TRUE(history.page_transition().from_address_bar());
+
+  // Re-fetch the visit from the backend and verify we've marked it as
+  // `is_known_to_sync`.
+  VisitRow visit_from_backend;
+  ASSERT_TRUE(backend()->GetVisitByID(visit_row.visit_id, &visit_from_backend));
+  EXPECT_TRUE(visit_from_backend.is_known_to_sync);
 }
 
 TEST_F(HistorySyncBridgeTest, DoesNotUploadPreexistingData) {
@@ -732,6 +738,17 @@
 
   // The data should *not* have been uploaded to Sync.
   EXPECT_TRUE(processor()->GetEntities().empty());
+
+  // Re-fetch these visits from the backend and verify we've NOT marked them as
+  // `is_known_to_sync`.
+  VisitRow visit_from_backend_1;
+  ASSERT_TRUE(
+      backend()->GetVisitByID(visit_row1.visit_id, &visit_from_backend_1));
+  EXPECT_FALSE(visit_from_backend_1.is_known_to_sync);
+  VisitRow visit_from_backend_2;
+  ASSERT_TRUE(
+      backend()->GetVisitByID(visit_row2.visit_id, &visit_from_backend_2));
+  EXPECT_FALSE(visit_from_backend_2.is_known_to_sync);
 }
 
 TEST_F(HistorySyncBridgeTest, DoesNotUploadWhileSyncIsPaused) {
diff --git a/components/history_clusters/core/config.h b/components/history_clusters/core/config.h
index a2a0c82..ee0492e 100644
--- a/components/history_clusters/core/config.h
+++ b/components/history_clusters/core/config.h
@@ -99,13 +99,13 @@
 
   // No effect if `persist_clusters_in_history_db` is disabled. Determines how
   // soon to update clusters after startup in minutes. E.g., by default, will
-  // update clusters 5 minutes after startup.
-  int persist_clusters_in_history_db_after_startup_delay_minutes = 5;
+  // update clusters 60 minutes minutes after startup.
+  int persist_clusters_in_history_db_after_startup_delay_minutes = 60;
 
   // No effect if `persist_clusters_in_history_db` is disabled. Determines how
   // often to update clusters in minutes. E.g., by default, will update clusters
-  // every hour.
-  int persist_clusters_in_history_db_period_minutes = 60;
+  // every 12 hours.
+  int persist_clusters_in_history_db_period_minutes = 12 * 60;
 
   // No effect if `persist_clusters_in_history_db` is disabled. If disabled,
   // persistence occurs on a timer (see the above 2 params). If enabled, will
@@ -132,7 +132,7 @@
   // clusters. E.g., if set to 2, and clusters up to 1/10 have been persisted,
   // then the next request will include visits from clusters from 1/8 and 1/9,
   // and unclustered visits from 1/10.
-  size_t persist_clusters_recluster_window_days = 2;
+  size_t persist_clusters_recluster_window_days = 0;
 
   // The `kOmniboxAction` feature and child params.
 
diff --git a/components/history_clusters/core/features.cc b/components/history_clusters/core/features.cc
index 1486f8ec..7ea9dd7 100644
--- a/components/history_clusters/core/features.cc
+++ b/components/history_clusters/core/features.cc
@@ -39,7 +39,7 @@
 
 BASE_FEATURE(kPersistedClusters,
              "HistoryClustersPersistedClusters",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kOmniboxAction,
              "JourneysOmniboxAction",
diff --git a/components/media_router/common/media_sink.cc b/components/media_router/common/media_sink.cc
index e4157686..49505967 100644
--- a/components/media_router/common/media_sink.cc
+++ b/components/media_router/common/media_sink.cc
@@ -29,7 +29,6 @@
 
 bool MediaSink::operator==(const MediaSink& other) const {
   return sink_id_ == other.sink_id_ && name_ == other.name_ &&
-         description_ == other.description_ && domain_ == other.domain_ &&
          icon_type_ == other.icon_type_ && provider_id_ == other.provider_id_;
 }
 
diff --git a/components/media_router/common/media_sink.h b/components/media_router/common/media_sink.h
index dbf0b45..8cfe946 100644
--- a/components/media_router/common/media_sink.h
+++ b/components/media_router/common/media_sink.h
@@ -59,16 +59,6 @@
   void set_name(const std::string& name) { name_ = name; }
   const std::string& name() const { return name_; }
 
-  void set_description(const std::string& description) {
-    description_ = description;
-  }
-  const absl::optional<std::string>& description() const {
-    return description_;
-  }
-
-  void set_domain(const std::string& domain) { domain_ = domain; }
-  const absl::optional<std::string>& domain() const { return domain_; }
-
   void set_icon_type(SinkIconType icon_type) { icon_type_ = icon_type; }
   SinkIconType icon_type() const { return icon_type_; }
 
@@ -92,12 +82,6 @@
   // Descriptive name of the MediaSink.
   std::string name_;
 
-  // Optional description of the MediaSink.
-  absl::optional<std::string> description_;
-
-  // Optional domain of the MediaSink.
-  absl::optional<std::string> domain_;
-
   // The type of icon that corresponds with the MediaSink.
   SinkIconType icon_type_ = SinkIconType::GENERIC;
 
diff --git a/components/media_router/common/mojom/media_router.mojom b/components/media_router/common/mojom/media_router.mojom
index 995ba23..2d43ba01 100644
--- a/components/media_router/common/mojom/media_router.mojom
+++ b/components/media_router/common/mojom/media_router.mojom
@@ -32,15 +32,6 @@
   string sink_id;
   // The human-readable name, e.g. "Janet's Chromecast".
   string name;
-  // Optional description of the sink.
-  //
-  // DEPRECATED.  This is currently used in the Media Router UX to relay a
-  // Hangouts meeting name.  It should not be used for other purposes.
-  // TODO(crbug.com/786208): Remove this at a future date when Hangouts names
-  // are no longer required.
-  string? description;
-  // Optional domain of the sink if this sink is associated with an identity.
-  string? domain;
   // The type of icon to show in the UI for this media sink.
   SinkIconType icon_type;
   // The ID of the MediaRouteProvider that this sink belongs to.
diff --git a/components/media_router/common/mojom/media_router_mojom_traits.cc b/components/media_router/common/mojom/media_router_mojom_traits.cc
index 9e7bbe9..2eaeb35 100644
--- a/components/media_router/common/mojom/media_router_mojom_traits.cc
+++ b/components/media_router/common/mojom/media_router_mojom_traits.cc
@@ -70,20 +70,6 @@
 
   out->sink().set_name(name);
 
-  absl::optional<std::string> description;
-  if (!data.ReadDescription(&description))
-    return false;
-
-  if (description)
-    out->sink().set_description(*description);
-
-  absl::optional<std::string> domain;
-  if (!data.ReadDomain(&domain))
-    return false;
-
-  if (domain)
-    out->sink().set_domain(*domain);
-
   media_router::SinkIconType icon_type;
   if (!data.ReadIconType(&icon_type))
     return false;
diff --git a/components/media_router/common/mojom/media_router_mojom_traits.h b/components/media_router/common/mojom/media_router_mojom_traits.h
index ced5f4d7..22c916e 100644
--- a/components/media_router/common/mojom/media_router_mojom_traits.h
+++ b/components/media_router/common/mojom/media_router_mojom_traits.h
@@ -215,16 +215,6 @@
     return sink_internal.sink().name();
   }
 
-  static const absl::optional<std::string>& description(
-      const media_router::MediaSinkInternal& sink_internal) {
-    return sink_internal.sink().description();
-  }
-
-  static const absl::optional<std::string>& domain(
-      const media_router::MediaSinkInternal& sink_internal) {
-    return sink_internal.sink().domain();
-  }
-
   static media_router::SinkIconType icon_type(
       const media_router::MediaSinkInternal& sink_internal) {
     return sink_internal.sink().icon_type();
diff --git a/components/paint_preview/player/android/BUILD.gn b/components/paint_preview/player/android/BUILD.gn
index fa91e24..11c82864 100644
--- a/components/paint_preview/player/android/BUILD.gn
+++ b/components/paint_preview/player/android/BUILD.gn
@@ -97,6 +97,7 @@
     "//content/public/android:content_java",
     "//third_party/android_swipe_refresh:android_swipe_refresh_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//ui/accessibility:ax_base_java",
     "//ui/android:ui_java",
     "//ui/android:ui_java_resources",
     "//url:gurl_java",
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
index a2b664e..0fa66f29 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
@@ -21,6 +21,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.content_public.browser.WebContentsAccessibility;
+import org.chromium.ui.accessibility.AccessibilityState;
 
 import java.util.List;
 
@@ -134,8 +135,7 @@
 
     @Override
     public boolean onHoverEvent(MotionEvent event) {
-        if (mWebContentsAccessibility != null
-                && mWebContentsAccessibility.isTouchExplorationEnabled()) {
+        if (mWebContentsAccessibility != null && AccessibilityState.hasTouchExplorationEnabled()) {
             return mWebContentsAccessibility.onHoverEventNoRenderer(event);
         }
         return super.onHoverEvent(event);
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index 8555690..07da563 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -810,8 +810,7 @@
   LogMetricsForSuggestions(suggestions);
   autofill::AutofillClient::PopupOpenArgs open_args(
       bounds, text_direction, suggestions,
-      autofill::AutoselectFirstSuggestion(false),
-      autofill::PopupType::kPasswords);
+      autofill::AutoselectFirstSuggestion(false));
   autofill_client_->ShowAutofillPopup(open_args,
                                       weak_ptr_factory_.GetWeakPtr());
   return true;
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index 21199b2..9fbf008 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -286,8 +286,7 @@
   return {gfx::RectF(), base::i18n::LEFT_TO_RIGHT,
           CreateTestSuggestions(has_opt_in_and_fill, has_opt_in_and_generate,
                                 has_re_signin),
-          autofill::AutoselectFirstSuggestion(false),
-          autofill::PopupType::kPasswords};
+          autofill::AutoselectFirstSuggestion(false)};
 }
 
 }  // namespace
@@ -603,7 +602,6 @@
 #endif
                   autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY)));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 // Test that a popup without entries doesn't show "Manage all Passwords".
@@ -625,7 +623,6 @@
               SuggestionVectorIdsAre(ElementsAre(
                   autofill::POPUP_ITEM_ID_PASSWORD_ACCOUNT_STORAGE_OPT_IN)));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 // Test that the popup is updated once account-stored suggestions are unlocked.
@@ -651,7 +648,6 @@
 #endif
                   autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY)));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 // Test that the popup is updated once "opt in and fill" is clicked.
@@ -1132,7 +1128,6 @@
           testing::Contains(std::vector<std::vector<Suggestion::Text>>{
               {Suggestion::Text(u"android://com.example1.android/")}})));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 TEST_F(PasswordAutofillManagerTest, FillSuggestionPasswordField) {
@@ -1166,7 +1161,6 @@
           Suggestion::Text(GetManagePasswordsTitle(),
                            Suggestion::Text::IsPrimary(true)))));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 // Verify that typing "foo" into the username field will match usernames
@@ -1211,7 +1205,6 @@
                   Suggestion::Text(GetManagePasswordsTitle(),
                                    Suggestion::Text::IsPrimary(true)))));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 // Verify that typing "oo" into the username field will not match any usernames
@@ -1288,7 +1281,6 @@
                   Suggestion::Text(GetManagePasswordsTitle(),
                                    Suggestion::Text::IsPrimary(true)))));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 // Verify that typing "example" into the username field will match and order
@@ -1336,7 +1328,6 @@
                                    Suggestion::Text::IsPrimary(true)))));
 
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 TEST_F(PasswordAutofillManagerTest, PreviewAndFillEmptyUsernameSuggestion) {
@@ -1476,7 +1467,6 @@
           Suggestion::Text(GetManagePasswordsTitle(),
                            Suggestion::Text::IsPrimary(true)))));
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 TEST_F(PasswordAutofillManagerTest,
@@ -1663,7 +1653,6 @@
               testing::Ge(1u));  // No footer on Android.
   EXPECT_THAT(open_args.suggestions[0].trailing_icon, "google");
   EXPECT_FALSE(open_args.autoselect_first_suggestion);
-  EXPECT_EQ(open_args.popup_type, PopupType::kPasswords);
 }
 
 TEST_F(PasswordAutofillManagerTest, FillsSuggestionIfAuthNotAvailable) {
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
index 219ccee4..8150e71a 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
@@ -23,6 +23,7 @@
     public static final String ANDROID_APP_PAYMENT_UPDATE_EVENTS = "AndroidAppPaymentUpdateEvents";
     public static final String ENFORCE_FULL_DELEGATION = "EnforceFullDelegation";
     public static final String GPAY_APP_DYNAMIC_UPDATE = "GPayAppDynamicUpdate";
+    public static final String OMIT_PARAMETERS_IN_READY_TO_PAY = "OmitParametersInReadyToPay";
     public static final String SECURE_PAYMENT_CONFIRMATION = "SecurePaymentConfirmationBrowser";
     public static final String SERVICE_WORKER_PAYMENT_APPS = "ServiceWorkerPaymentApps";
     public static final String WEB_PAYMENTS = "WebPayments";
diff --git a/components/payments/content/android/junit/src/org/chromium/components/payments/test_support/ShadowPaymentFeatureList.java b/components/payments/content/android/junit/src/org/chromium/components/payments/test_support/ShadowPaymentFeatureList.java
index 769b1072..09faac4 100644
--- a/components/payments/content/android/junit/src/org/chromium/components/payments/test_support/ShadowPaymentFeatureList.java
+++ b/components/payments/content/android/junit/src/org/chromium/components/payments/test_support/ShadowPaymentFeatureList.java
@@ -37,6 +37,8 @@
                 PaymentFeatureList.SECURE_PAYMENT_CONFIRMATION, true);
         ShadowPaymentFeatureList.setFeatureEnabled(
                 PaymentFeatureList.ADD_IDENTITY_IN_CAN_MAKE_PAYMENT_EVENT, false);
+        ShadowPaymentFeatureList.setFeatureEnabled(
+                PaymentFeatureList.OMIT_PARAMETERS_IN_READY_TO_PAY, false);
     }
 
     @Resetter
diff --git a/components/payments/content/android/payment_feature_list.cc b/components/payments/content/android/payment_feature_list.cc
index 4567856..06d6345 100644
--- a/components/payments/content/android/payment_feature_list.cc
+++ b/components/payments/content/android/payment_feature_list.cc
@@ -37,6 +37,7 @@
     &features::kWebPaymentsRedactShippingAddress,
     &features::kWebPaymentsSingleAppUiSkip,
     &kAndroidAppPaymentUpdateEvents,
+    &kOmitParametersInReadyToPay,
 };
 
 const base::Feature* FindFeatureExposedToJava(const std::string& feature_name) {
@@ -56,6 +57,9 @@
 BASE_FEATURE(kAndroidAppPaymentUpdateEvents,
              "AndroidAppPaymentUpdateEvents",
              base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kOmitParametersInReadyToPay,
+             "OmitParametersInReadyToPay",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 static jboolean JNI_PaymentFeatureList_IsEnabled(
     JNIEnv* env,
diff --git a/components/payments/content/android/payment_feature_list.h b/components/payments/content/android/payment_feature_list.h
index 3948cc7..eb99addf 100644
--- a/components/payments/content/android/payment_feature_list.h
+++ b/components/payments/content/android/payment_feature_list.h
@@ -13,8 +13,13 @@
 namespace android {
 
 // Android only payment features in alphabetical order:
+
 BASE_DECLARE_FEATURE(kAndroidAppPaymentUpdateEvents);
 
+// If enabled, then the web merchant origin and web wallet parameters will be
+// omitted from the isReadyToPayRequest. See: https://crbug.com/1406655.
+BASE_DECLARE_FEATURE(kOmitParametersInReadyToPay);
+
 }  // namespace android
 }  // namespace payments
 
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml
index 4bfbb985..e13f08a 100644
--- a/components/policy/resources/templates/policies.yaml
+++ b/components/policy/resources/templates/policies.yaml
@@ -1054,6 +1054,7 @@
   1053: KerberosUseCustomPrefilledConfig
   1054: DeviceHindiInscriptLayoutEnabled
   1055: DeviceLoginScreenExtensionManifestV2Availability
+  1056: KioskTroubleshootingToolsEnabled
 atomic_groups:
   1: Homepage
   2: RemoteAccess
diff --git a/components/policy/resources/templates/policy_definitions/Kiosk/KioskTroubleshootingToolsEnabled.yaml b/components/policy/resources/templates/policy_definitions/Kiosk/KioskTroubleshootingToolsEnabled.yaml
new file mode 100644
index 0000000..4bec9a0
--- /dev/null
+++ b/components/policy/resources/templates/policy_definitions/Kiosk/KioskTroubleshootingToolsEnabled.yaml
@@ -0,0 +1,31 @@
+owners:
+- pbond@chromium.org
+- file://chrome/browser/ash/app_mode/OWNERS
+caption: Enable Kiosk troubleshooting tools
+desc: |-
+  Setting the policy to Enabled means Kiosk troubleshooting tools are available
+  to be used in a Kiosk session:
+  - Chrome developer tools
+  - Chrome browser window
+  - Task manager
+  Leaving this policy unset or setting to Disabled means Kiosk troubleshooting tools are disabled.
+
+  Please remember not to keep this policy enabled at all times, especially in production deployments.
+future_on:
+- chrome_os
+features:
+  dynamic_refresh: true
+  per_profile: true
+type: main
+schema:
+  type: boolean
+supported_chrome_os_management:
+- google_cloud
+items:
+- caption: Enable Kiosk troubleshooting tools.
+  value: true
+- caption: Disable Kiosk troubleshooting tools.
+  value: false
+default: false
+example_value: false
+tags: []
diff --git a/components/policy/resources/templates/policy_definitions/Kiosk/policy_atomic_groups.yaml b/components/policy/resources/templates/policy_definitions/Kiosk/policy_atomic_groups.yaml
index e4d786e..d722a50 100644
--- a/components/policy/resources/templates/policy_definitions/Kiosk/policy_atomic_groups.yaml
+++ b/components/policy/resources/templates/policy_definitions/Kiosk/policy_atomic_groups.yaml
@@ -6,3 +6,4 @@
   - DeviceLocalAccountAutoLoginDelay
   - DeviceLocalAccountAutoLoginBailoutEnabled
   - DeviceLocalAccountPromptForNetworkWhenOffline
+  - KioskTroubleshootingToolsEnabled
diff --git a/components/services/storage/public/cpp/storage_prefs.cc b/components/services/storage/public/cpp/storage_prefs.cc
index c293a78a..33222b5 100644
--- a/components/services/storage/public/cpp/storage_prefs.cc
+++ b/components/services/storage/public/cpp/storage_prefs.cc
@@ -19,16 +19,10 @@
 const char kPrefixedStorageInfoEnabled[] =
     "policy.prefixed_storage_info_enabled";
 
-// Boolean policy to force async interface in SyncAccessHandle to be enabled.
-const char kFileSystemSyncAccessHandleAsyncInterfaceEnabled[] =
-    "policy.file_system_sync_access_handle_async_interface_enabled";
-
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterBooleanPref(kWebSQLAccess, false);
   registry->RegisterBooleanPref(kWebSQLNonSecureContextEnabled, false);
   registry->RegisterBooleanPref(kPrefixedStorageInfoEnabled, false);
-  registry->RegisterBooleanPref(
-      kFileSystemSyncAccessHandleAsyncInterfaceEnabled, false);
 }
 
 }  // namespace storage
diff --git a/components/services/storage/public/cpp/storage_prefs.h b/components/services/storage/public/cpp/storage_prefs.h
index 998dbfb..2808d2be 100644
--- a/components/services/storage/public/cpp/storage_prefs.h
+++ b/components/services/storage/public/cpp/storage_prefs.h
@@ -15,8 +15,6 @@
 
 extern const char kPrefixedStorageInfoEnabled[];
 
-extern const char kFileSystemSyncAccessHandleAsyncInterfaceEnabled[];
-
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
 }  // namespace storage
diff --git a/components/user_manager/user_manager.cc b/components/user_manager/user_manager.cc
index c73cc86..ba5932ad 100644
--- a/components/user_manager/user_manager.cc
+++ b/components/user_manager/user_manager.cc
@@ -42,9 +42,6 @@
 void UserManager::UserSessionStateObserver::UserAddedToSession(
     const User* active_user) {}
 
-void UserManager::UserSessionStateObserver::ActiveUserHashChanged(
-    const std::string& hash) {}
-
 UserManager::UserSessionStateObserver::~UserSessionStateObserver() {}
 
 UserManager::UserAccountData::UserAccountData(
diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h
index bc7c49c..b3e5080 100644
--- a/components/user_manager/user_manager.h
+++ b/components/user_manager/user_manager.h
@@ -91,10 +91,6 @@
     // Called when another user got added to the existing session.
     virtual void UserAddedToSession(const User* added_user);
 
-    // Called right before notifying on user change so that those who rely
-    // on account_id hash would be accessing up-to-date value.
-    virtual void ActiveUserHashChanged(const std::string& hash);
-
    protected:
     virtual ~UserSessionStateObserver();
   };
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index 367c9c6..1c1ee86 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -321,7 +321,6 @@
   // Move the user to the front.
   SetLRUUser(active_user_);
 
-  NotifyActiveUserHashChanged(active_user_->username_hash());
   NotifyActiveUserChanged(active_user_);
   CallUpdateLoginState();
 }
@@ -1063,7 +1062,6 @@
 void UserManagerBase::NotifyOnLogin() {
   DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
 
-  NotifyActiveUserHashChanged(active_user_->username_hash());
   NotifyActiveUserChanged(active_user_);
   CallUpdateLoginState();
 }
@@ -1149,12 +1147,6 @@
     observer.UserAddedToSession(added_user);
 }
 
-void UserManagerBase::NotifyActiveUserHashChanged(const std::string& hash) {
-  DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
-  for (auto& observer : session_state_observer_list_)
-    observer.ActiveUserHashChanged(hash);
-}
-
 void UserManagerBase::Initialize() {
   UserManager::Initialize();
   if (!HasBrowserRestarted()) {
diff --git a/components/user_manager/user_manager_base.h b/components/user_manager/user_manager_base.h
index 2f57716..5172367 100644
--- a/components/user_manager/user_manager_base.h
+++ b/components/user_manager/user_manager_base.h
@@ -342,9 +342,6 @@
   // Notifies observers that merge session state had changed.
   void NotifyMergeSessionStateChanged();
 
-  // Notifies observers that active account_id hash has changed.
-  void NotifyActiveUserHashChanged(const std::string& hash);
-
   // Call UpdateLoginState.
   void CallUpdateLoginState();
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.h b/content/browser/accessibility/browser_accessibility_manager_win.h
index e65a0cd..59e0e34 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.h
+++ b/content/browser/accessibility/browser_accessibility_manager_win.h
@@ -70,14 +70,6 @@
   // Do event post-processing
   void FinalizeAccessibilityEvents() override;
 
-  // Track this object and post a VISIBLE_DATA_CHANGED notification when
-  // its container scrolls.
-  // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed.
-  void TrackScrollingObject(BrowserAccessibilityWin* node);
-
-  // Called when |accessible_hwnd_| is deleted by its parent.
-  void OnAccessibleHwndDeleted();
-
  protected:
   // AXTreeObserver methods.
   void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index 5fc1dbe..c207d572 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -104,22 +104,6 @@
   "failed_send_attempts=failed_send_attempts+1 "            \
   "WHERE " column "=?"
 
-#define ATTRIBUTION_NEXT_REPORT_TIME_SQL(table) \
-  "SELECT MIN(report_time)FROM " table " WHERE report_time>?"
-
-// Set the report time for all reports that should have been sent before now
-// to now + a random number of microseconds between `min_delay` and
-// `max_delay`, both inclusive. We use RANDOM, instead of a method on the
-// delegate, to avoid having to pull all reports into memory and update them
-// one by one. We use ABS because RANDOM may return a negative integer. We add
-// 1 to the difference between `max_delay` and `min_delay` to ensure that the
-// range of generated values is inclusive. If `max_delay == min_delay`, we
-// take the remainder modulo 1, which is always 0.
-#define ATTRIBUTION_SET_REPORT_TIME_SQL(table) \
-  "UPDATE " table                              \
-  " SET report_time=?+ABS(RANDOM()%?)"         \
-  "WHERE report_time<?"
-
 // clang-format off
 
 #define ATTRIBUTION_SOURCE_COLUMNS_SQL(prefix) \
@@ -160,22 +144,6 @@
   "FROM aggregatable_report_metadata A "                               \
   "JOIN sources I ON A.source_id=I.source_id "
 
-// This query should be reasonably optimized via
-// `kConversionDestinationIndexSql`. The conversion origin is the third
-// column in a multi-column index where the first two columns are just booleans.
-// Therefore the third column in the index should be very well-sorted.
-//
-// Note: to take advantage of this, we need to hint to the query planner that
-// |event_level_active| and |aggregatable_active| are booleans, so include
-// them in the conditional.
-#define ATTRIBUTION_COUNT_REPORTS_SQL(table) \
-  "SELECT COUNT(*)FROM " table " R "         \
-  "JOIN sources I "                          \
-  "ON I.source_id=R.source_id "              \
-  "WHERE I.destination_site=? "              \
-  "AND(event_level_active BETWEEN 0 AND 1)"  \
-  "AND(aggregatable_active BETWEEN 0 AND 1)"
-
 // clang-format on
 
 void RecordInitializationStatus(
@@ -1508,9 +1476,8 @@
 
 absl::optional<base::Time> AttributionStorageSql::GetNextEventLevelReportTime(
     base::Time time) {
-  static constexpr char kNextReportTimeSql[] =
-      ATTRIBUTION_NEXT_REPORT_TIME_SQL(ATTRIBUTION_CONVERSIONS_TABLE);
-  return GetNextReportTime(SQL_FROM_HERE, kNextReportTimeSql, time);
+  return GetNextReportTime(
+      SQL_FROM_HERE, attribution_queries::kNextEventLevelReportTimeSql, time);
 }
 
 std::vector<AttributionReport> AttributionStorageSql::GetReports(
@@ -1704,10 +1671,9 @@
     base::TimeDelta min_delay,
     base::TimeDelta max_delay,
     base::Time now) {
-  static constexpr char kSetReportTimeSql[] =
-      ATTRIBUTION_SET_REPORT_TIME_SQL(ATTRIBUTION_CONVERSIONS_TABLE);
-  if (!AdjustOfflineReportTimes(SQL_FROM_HERE, kSetReportTimeSql, min_delay,
-                                max_delay, now)) {
+  if (!AdjustOfflineReportTimes(
+          SQL_FROM_HERE, attribution_queries::kSetEventLevelReportTimeSql,
+          min_delay, max_delay, now)) {
     return absl::nullopt;
   }
 
@@ -1960,13 +1926,11 @@
   switch (report_type) {
     case AttributionReport::Type::kEventLevel:
       statement.Assign(db_->GetCachedStatement(
-          SQL_FROM_HERE,
-          ATTRIBUTION_COUNT_REPORTS_SQL(ATTRIBUTION_CONVERSIONS_TABLE)));
+          SQL_FROM_HERE, attribution_queries::kCountEventLevelReportsSql));
       break;
     case AttributionReport::Type::kAggregatableAttribution:
       statement.Assign(db_->GetCachedStatement(
-          SQL_FROM_HERE, ATTRIBUTION_COUNT_REPORTS_SQL(
-                             ATTRIBUTION_AGGREGATABLE_REPORT_METADATA_TABLE)));
+          SQL_FROM_HERE, attribution_queries::kCountAggregatableReportsSql));
       break;
   }
 
@@ -2670,12 +2634,8 @@
 std::vector<AggregatableHistogramContribution>
 AttributionStorageSql::GetAggregatableContributions(
     AttributionReport::AggregatableAttributionData::Id aggregation_id) {
-  static constexpr char kGetContributionsSql[] =
-      "SELECT key_high_bits,key_low_bits,value "
-      "FROM aggregatable_contributions "
-      "WHERE aggregation_id=?";
-  sql::Statement statement(
-      db_->GetCachedStatement(SQL_FROM_HERE, kGetContributionsSql));
+  sql::Statement statement(db_->GetCachedStatement(
+      SQL_FROM_HERE, attribution_queries::kGetContributionsSql));
   statement.BindInt64(0, *aggregation_id);
 
   std::vector<AggregatableHistogramContribution> contributions;
@@ -2739,9 +2699,8 @@
 absl::optional<base::Time>
 AttributionStorageSql::GetNextAggregatableAttributionReportTime(
     base::Time time) {
-  static constexpr char kNextReportTimeSql[] = ATTRIBUTION_NEXT_REPORT_TIME_SQL(
-      ATTRIBUTION_AGGREGATABLE_REPORT_METADATA_TABLE);
-  return GetNextReportTime(SQL_FROM_HERE, kNextReportTimeSql, time);
+  return GetNextReportTime(
+      SQL_FROM_HERE, attribution_queries::kNextAggregatableReportTimeSql, time);
 }
 
 absl::optional<base::Time>
@@ -2749,10 +2708,9 @@
     base::TimeDelta min_delay,
     base::TimeDelta max_delay,
     base::Time now) {
-  static constexpr char kSetReportTimeSql[] = ATTRIBUTION_SET_REPORT_TIME_SQL(
-      ATTRIBUTION_AGGREGATABLE_REPORT_METADATA_TABLE);
-  if (!AdjustOfflineReportTimes(SQL_FROM_HERE, kSetReportTimeSql, min_delay,
-                                max_delay, now)) {
+  if (!AdjustOfflineReportTimes(
+          SQL_FROM_HERE, attribution_queries::kSetAggregatableReportTimeSql,
+          min_delay, max_delay, now)) {
     return absl::nullopt;
   }
 
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_query_plans_unittest.cc b/content/browser/attribution_reporting/attribution_storage_sql_query_plans_unittest.cc
index fb07bc5..8f60935 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql_query_plans_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql_query_plans_unittest.cc
@@ -46,7 +46,7 @@
       std::string query,
       absl::optional<SqlFullScanReason> reason = absl::nullopt) {
     auto plan = explainer_->GetPlan(std::move(query), reason);
-    EXPECT_TRUE(plan.has_value());
+    EXPECT_TRUE(plan.has_value()) << plan.error();
     return *plan;
   }
 
@@ -112,5 +112,46 @@
               UsesCoveringIndex("aggregate_source_id_idx"));
 }
 
+TEST_F(AttributionSqlQueryPlanTest, kGetContributionsSql) {
+  EXPECT_THAT(GetPlan(attribution_queries::kGetContributionsSql),
+              UsesPrimaryKey());
+}
+
+TEST_F(AttributionSqlQueryPlanTest, kCountEventLevelReportsSql) {
+  EXPECT_THAT(GetPlan(attribution_queries::kCountEventLevelReportsSql),
+              AllOf(UsesCoveringIndex(
+                        "sources_by_active_destination_site_reporting_origin",
+                        {"event_level_active"}),
+                    UsesCoveringIndex("event_level_reports_by_source_id")));
+}
+
+TEST_F(AttributionSqlQueryPlanTest, kCountAggregatableReportsSql) {
+  EXPECT_THAT(GetPlan(attribution_queries::kCountAggregatableReportsSql),
+              AllOf(UsesCoveringIndex(
+                        "sources_by_active_destination_site_reporting_origin",
+                        {"event_level_active"}),
+                    UsesCoveringIndex("aggregate_source_id_idx")));
+}
+
+TEST_F(AttributionSqlQueryPlanTest, kNextEventLevelReportTimeSql) {
+  EXPECT_THAT(GetPlan(attribution_queries::kNextEventLevelReportTimeSql),
+              UsesCoveringIndex("event_level_reports_by_report_time"));
+}
+
+TEST_F(AttributionSqlQueryPlanTest, kNextAggregatableReportTimeSql) {
+  EXPECT_THAT(GetPlan(attribution_queries::kNextAggregatableReportTimeSql),
+              UsesCoveringIndex("aggregate_report_time_idx"));
+}
+
+TEST_F(AttributionSqlQueryPlanTest, kSetEventLevelReportTimeSql) {
+  EXPECT_THAT(GetPlan(attribution_queries::kSetEventLevelReportTimeSql),
+              UsesIndex("event_level_reports_by_report_time"));
+}
+
+TEST_F(AttributionSqlQueryPlanTest, kSetAggregatableReportTimeSql) {
+  EXPECT_THAT(GetPlan(attribution_queries::kSetAggregatableReportTimeSql),
+              UsesIndex("aggregate_report_time_idx"));
+}
+
 }  // namespace
 }  // namespace content
diff --git a/content/browser/attribution_reporting/sql_queries.h b/content/browser/attribution_reporting/sql_queries.h
index 04099a3..471a197 100644
--- a/content/browser/attribution_reporting/sql_queries.h
+++ b/content/browser/attribution_reporting/sql_queries.h
@@ -74,6 +74,62 @@
     "WHERE source_id=? "
     "RETURNING aggregation_id";
 
+inline constexpr const char kGetContributionsSql[] =
+    "SELECT key_high_bits,key_low_bits,value "
+    "FROM aggregatable_contributions "
+    "WHERE aggregation_id=?";
+
+// We need to hint to the query planner that/ `event_level_active` and
+// `aggregatable_active` are booleans, so include them in the conditional.
+#define ATTRIBUTION_COUNT_REPORTS_SQL(table) \
+  "SELECT COUNT(*)FROM " table               \
+  " R "                                      \
+  "JOIN sources I "                          \
+  "ON I.source_id=R.source_id "              \
+  "WHERE I.destination_site=? "              \
+  "AND(event_level_active BETWEEN 0 AND 1)"  \
+  "AND(aggregatable_active BETWEEN 0 AND 1)"
+
+inline constexpr const char kCountEventLevelReportsSql[] =
+    ATTRIBUTION_COUNT_REPORTS_SQL("event_level_reports");
+
+inline constexpr const char kCountAggregatableReportsSql[] =
+    ATTRIBUTION_COUNT_REPORTS_SQL("aggregatable_report_metadata");
+
+#undef ATTRIBUTION_COUNT_REPORTS_SQL
+
+#define ATTRIBUTION_NEXT_REPORT_TIME_SQL(table) \
+  "SELECT MIN(report_time)FROM " table " WHERE report_time>?"
+
+inline constexpr char kNextEventLevelReportTimeSql[] =
+    ATTRIBUTION_NEXT_REPORT_TIME_SQL("event_level_reports");
+
+inline constexpr char kNextAggregatableReportTimeSql[] =
+    ATTRIBUTION_NEXT_REPORT_TIME_SQL("aggregatable_report_metadata");
+
+#undef ATTRIBUTION_NEXT_REPORT_TIME_SQL
+
+// Set the report time for all reports that should have been sent before now
+// to now + a random number of microseconds between `min_delay` and
+// `max_delay`, both inclusive. We use RANDOM, instead of a method on the
+// delegate, to avoid having to pull all reports into memory and update them
+// one by one. We use ABS because RANDOM may return a negative integer. We add
+// 1 to the difference between `max_delay` and `min_delay` to ensure that the
+// range of generated values is inclusive. If `max_delay == min_delay`, we
+// take the remainder modulo 1, which is always 0.
+#define ATTRIBUTION_SET_REPORT_TIME_SQL(table) \
+  "UPDATE " table                              \
+  " SET report_time=?+ABS(RANDOM()%?)"         \
+  "WHERE report_time<?"
+
+inline constexpr const char kSetEventLevelReportTimeSql[] =
+    ATTRIBUTION_SET_REPORT_TIME_SQL("event_level_reports");
+
+inline constexpr const char kSetAggregatableReportTimeSql[] =
+    ATTRIBUTION_SET_REPORT_TIME_SQL("aggregatable_report_metadata");
+
+#undef ATTRIBUTION_SET_REPORT_TIME_SQL
+
 }  // namespace content::attribution_queries
 
 #endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_SQL_QUERIES_H_
diff --git a/content/browser/attribution_reporting/sql_query_plan_test_util.cc b/content/browser/attribution_reporting/sql_query_plan_test_util.cc
index 36554ee8..a1bd820 100644
--- a/content/browser/attribution_reporting/sql_query_plan_test_util.cc
+++ b/content/browser/attribution_reporting/sql_query_plan_test_util.cc
@@ -222,4 +222,16 @@
   return plan;
 }
 
+std::ostream& operator<<(std::ostream& out,
+                         SqlQueryPlanExplainer::Error error) {
+  switch (error) {
+    case SqlQueryPlanExplainer::Error::kInvalidOutput:
+      return out << "kInvalidOutput";
+    case SqlQueryPlanExplainer::Error::kMissingFullScanAnnotation:
+      return out << "kMissingFullScanAnnotation";
+    case SqlQueryPlanExplainer::Error::kExtraneousFullScanAnnotation:
+      return out << "kExtraneousFullScanAnnotation";
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/attribution_reporting/sql_query_plan_test_util.h b/content/browser/attribution_reporting/sql_query_plan_test_util.h
index cb91783..19f3691 100644
--- a/content/browser/attribution_reporting/sql_query_plan_test_util.h
+++ b/content/browser/attribution_reporting/sql_query_plan_test_util.h
@@ -76,6 +76,8 @@
   const base::FilePath shell_path_;
 };
 
+std::ostream& operator<<(std::ostream&, SqlQueryPlanExplainer::Error);
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_SQL_QUERY_PLAN_TEST_UTIL_H_
diff --git a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
index b788900..2b66e8d 100644
--- a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
@@ -450,8 +450,6 @@
                      const std::string& value) {
     std::vector<std::string> data;
 
-    // TODO(crbug.com/1199077): Update this when BackgroundFetchTestBase
-    // implements StorageKey.
     base::RunLoop run_loop;
     embedded_worker_test_helper()->context_wrapper()->StoreRegistrationUserData(
         service_worker_registration_id, storage_key(), {{key, value}},
diff --git a/content/browser/background_fetch/background_fetch_job_controller.cc b/content/browser/background_fetch/background_fetch_job_controller.cc
index f1985709..c1ae214 100644
--- a/content/browser/background_fetch/background_fetch_job_controller.cc
+++ b/content/browser/background_fetch/background_fetch_job_controller.cc
@@ -113,8 +113,6 @@
   for (const auto& request_info : active_fetch_requests)
     active_guids.push_back(request_info->download_guid());
 
-  // TODO(https://crbug.com/1199077): Should we update
-  // BackgroundFetchDescription to StorageKey?
   auto fetch_description = std::make_unique<BackgroundFetchDescription>(
       registration_id().unique_id(), registration_id().storage_key().origin(),
       options_->title, icon_, completed_downloads_, total_downloads_,
diff --git a/content/browser/background_fetch/background_fetch_test_base.cc b/content/browser/background_fetch/background_fetch_test_base.cc
index 9b0bbfd..19c37d094 100644
--- a/content/browser/background_fetch/background_fetch_test_base.cc
+++ b/content/browser/background_fetch/background_fetch_test_base.cc
@@ -161,8 +161,6 @@
     int64_t service_worker_registration_id) {
   base::RunLoop run_loop;
   const GURL scope = GetScopeForId(kTestOrigin, service_worker_registration_id);
-  // TODO(crbug.com/1199077): Update this when background fetch implements
-  // StorageKey.
   embedded_worker_test_helper_.context()->UnregisterServiceWorker(
       scope, blink::StorageKey(url::Origin::Create(scope)),
       /*is_immediate=*/false,
diff --git a/content/browser/devtools/devtools_background_services_context_impl.cc b/content/browser/devtools/devtools_background_services_context_impl.cc
index 5031a974..57324ee0 100644
--- a/content/browser/devtools/devtools_background_services_context_impl.cc
+++ b/content/browser/devtools/devtools_background_services_context_impl.cc
@@ -218,8 +218,6 @@
   event.mutable_event_metadata()->insert(event_metadata.begin(),
                                          event_metadata.end());
 
-  // TODO(crbug.com/1199077): Update this when
-  // DevToolsBackgroundServicesContextImpl implements StorageKey.
   service_worker_context_->StoreRegistrationUserData(
       service_worker_registration_id, storage_key,
       {{CreateEntryKey(event.background_service()), event.SerializeAsString()}},
diff --git a/content/browser/devtools/devtools_background_services_context_impl_unittest.cc b/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
index aeb8fc6b..75bd586 100644
--- a/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
+++ b/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
@@ -214,8 +214,6 @@
  private:
   int64_t RegisterServiceWorker() {
     GURL script_url(origin_.GetURL().spec() + "sw.js");
-    // TODO(crbug.com/1199077): Update this when
-    // DevToolsBackgroundServicesContextImpl implements StorageKey.
     blink::StorageKey key(origin_);
     int64_t service_worker_registration_id =
         blink::mojom::kInvalidServiceWorkerRegistrationId;
diff --git a/content/browser/media/media_license_database.cc b/content/browser/media/media_license_database.cc
index f4d3ed7..f3abf63d 100644
--- a/content/browser/media/media_license_database.cc
+++ b/content/browser/media/media_license_database.cc
@@ -151,6 +151,14 @@
     return MediaLicenseStorageHostOpenError::kOk;
 
   bool success = false;
+
+  // If this is not the first call to `OpenDatabase()` because we are re-trying
+  // initialization, then the error callback will have previously been set.
+  db_.reset_error_callback();
+
+  // base::Unretained is safe becase |db_| is owned by |this|
+  db_.set_error_callback(base::BindRepeating(
+      &MediaLicenseDatabase::OnDatabaseOpenError, base::Unretained(this)));
   if (path_.empty()) {
     success = db_.OpenInMemory();
   } else {
@@ -212,4 +220,16 @@
   return MediaLicenseStorageHostOpenError::kOk;
 }
 
+void MediaLicenseDatabase::OnDatabaseOpenError(int error,
+                                               sql::Statement* stmt) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // This histogram is only logged when the db is closed because we want to only
+  // log errors which prevent the db from opening.
+  if (!db_.is_open()) {
+    sql::UmaHistogramSqliteResult(
+        "Media.EME.MediaLicenseDatabaseOpenSQLiteError", error);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/media/media_license_database.h b/content/browser/media/media_license_database.h
index e0e36030..51a24a5 100644
--- a/content/browser/media/media_license_database.h
+++ b/content/browser/media/media_license_database.h
@@ -38,6 +38,8 @@
   MediaLicenseStorageHost::MediaLicenseStorageHostOpenError OpenDatabase(
       bool is_retry = false);
 
+  void OnDatabaseOpenError(int error, sql::Statement* stmt);
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // Empty if the database is in-memory.
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 6f1ca82..b33cfec3 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -610,26 +610,11 @@
   host_->SetNeedsRedrawRect(host_->device_viewport_rect());
 }
 
-void CompositorImpl::DidUpdateLayers() {
-  // Dump property trees and layers if run with:
-  //   --vmodule=compositor_impl_android=3
-  VLOG(3) << "After updating layers:\n"
-          << "property trees:\n"
-          << host_->property_trees()->ToString() << "\n"
-          << "cc::Layers:\n"
-          << host_->LayersAsString();
-}
-
 void CompositorImpl::BeginMainFrame(const viz::BeginFrameArgs& args) {
-  latest_frame_time_ = args.frame_time;
-}
-
-void CompositorImpl::UpdateLayerTreeHost() {
-  DCHECK(!latest_frame_time_.is_null());
   client_->UpdateLayerTreeHost();
   if (needs_animate_) {
     needs_animate_ = false;
-    root_window_->Animate(latest_frame_time_);
+    root_window_->Animate(args.frame_time);
   }
 }
 
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index 6f7735d..44a90a5 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -132,7 +132,7 @@
   void WillBeginMainFrame() override {}
   void DidBeginMainFrame() override {}
   void WillUpdateLayers() override {}
-  void DidUpdateLayers() override;
+  void DidUpdateLayers() override {}
   void BeginMainFrame(const viz::BeginFrameArgs& args) override;
   void OnDeferMainFrameUpdatesChanged(bool) override {}
   void OnDeferCommitsChanged(
@@ -143,7 +143,7 @@
   void OnCommitRequested() override {}
   void BeginMainFrameNotExpectedSoon() override {}
   void BeginMainFrameNotExpectedUntil(base::TimeTicks time) override {}
-  void UpdateLayerTreeHost() override;
+  void UpdateLayerTreeHost() override {}
   void ApplyViewportChanges(const cc::ApplyViewportChangesArgs& args) override {
   }
   void UpdateCompositorScrollState(
@@ -301,8 +301,6 @@
 
   size_t num_of_consecutive_surface_failures_ = 0u;
 
-  base::TimeTicks latest_frame_time_;
-
   uint32_t pending_readbacks_ = 0u;
 
   bool enable_swap_completion_callbacks_ = false;
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index ed709b48..5ce8618 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -136,8 +136,9 @@
   if (controls.hotword_enabled) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     // Only enable if a hotword device exists.
-    if (ash::CrasAudioHandler::Get()->HasHotwordDevice())
+    if (ash::CrasAudioHandler::Get()->HasHotwordDevice()) {
       *effects |= media::AudioParameters::HOTWORD;
+    }
 #endif
   }
 }
@@ -152,16 +153,18 @@
     std::string* device_id,
     absl::optional<std::string>* group_id) {
   // The source_id can be empty if the constraint is set but empty.
-  if (hmac_device_id.empty())
+  if (hmac_device_id.empty()) {
     return false;
+  }
 
   for (const auto& device_info : devices) {
     if (!MediaStreamManager::DoesMediaDeviceIDMatchHMAC(
             salt, security_origin, hmac_device_id, device_info.device_id)) {
       continue;
     }
-    if (device_id)
+    if (device_id) {
       *device_id = device_info.device_id;
+    }
     if (group_id) {
       *group_id = device_info.group_id.empty()
                       ? absl::nullopt
@@ -449,8 +452,9 @@
       display_surface, /*logical_surface=*/true,
       media::mojom::CursorCaptureType::NEVER, /*capture_handle=*/nullptr);
   devices.push_back(device);
-  if (!request_audio)
+  if (!request_audio) {
     return devices;
+  }
 
   MediaStreamDevice audio_device(
       MediaStreamType::DISPLAY_AUDIO_CAPTURE,
@@ -841,8 +845,9 @@
 
     MediaObserver* media_observer =
         GetContentClient()->browser()->GetMediaObserver();
-    if (!media_observer)
+    if (!media_observer) {
       return;
+    }
 
     if (stream_type == MediaStreamType::NUM_MEDIA_TYPES) {
       for (int i = static_cast<int>(MediaStreamType::NO_SERVICE) + 1;
@@ -916,8 +921,9 @@
   void SetCapturingLinkSecured(bool is_secure) {
     MediaObserver* media_observer =
         GetContentClient()->browser()->GetMediaObserver();
-    if (!media_observer)
+    if (!media_observer) {
       return;
+    }
 
     media_observer->OnSetCapturingLinkSecured(target_process_id_,
                                               target_frame_id_, page_request_id,
@@ -1059,8 +1065,9 @@
   }
 
   void NotifyMultiCaptureStateChanged(MediaRequestState new_state) {
-    if (!IsGetDisplayMediaSet())
+    if (!IsGetDisplayMediaSet()) {
       return;
+    }
     switch (new_state) {
       case MediaRequestState::MEDIA_REQUEST_STATE_OPENING:
         GetUIThreadTaskRunner({})->PostTask(
@@ -1758,13 +1765,14 @@
                                        int requester_id,
                                        int page_request_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  for (const LabeledDeviceRequest& labeled_request : requests_) {
-    DeviceRequest* const request = labeled_request.second.get();
+  for (auto request_it = requests_.begin(); request_it != requests_.end();
+       ++request_it) {
+    const DeviceRequest* const request = request_it->second.get();
     if (request->requesting_process_id == render_process_id &&
         request->requesting_frame_id == render_frame_id &&
         request->requester_id == requester_id &&
         request->page_request_id == page_request_id) {
-      CancelRequest(labeled_request.first);
+      CancelRequest(request_it);
       return;
     }
   }
@@ -1773,16 +1781,30 @@
 void MediaStreamManager::CancelRequest(const std::string& label) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  SendLogMessage(
-      base::StringPrintf("CancelRequest({label=%s})", label.c_str()));
   const DeviceRequests::const_iterator request_it = FindRequestIterator(label);
   if (request_it == requests_.end()) {
-    // The request does not exist.
+    SendLogMessage(
+        base::StringPrintf("CancelRequest({label=%s})", label.c_str()));
     LOG(ERROR) << "The request with label = " << label << " does not exist.";
     return;
   }
+
+  CancelRequest(request_it);
+}
+
+void MediaStreamManager::CancelRequest(
+    DeviceRequests::const_iterator request_it) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (request_it == requests_.end()) {
+    return;
+  }
+  const std::string& label = request_it->first;
   DeviceRequest* const request = request_it->second.get();
 
+  SendLogMessage(
+      base::StringPrintf("CancelRequest({label=%s})", label.c_str()));
+
   // This is a request for closing one or more devices.
   for (const blink::mojom::StreamDevicesPtr& stream_devices_ptr :
        request->stream_devices_set.stream_devices) {
@@ -1809,14 +1831,15 @@
   // Cancel the request if still pending at UI side.
   request->SetState(MediaStreamType::NUM_MEDIA_TYPES,
                     MEDIA_REQUEST_STATE_CLOSING);
-  DeleteRequest(request_it);
+  DeleteRequest(request_it);  // Invalidates |label| and |request|.
 }
 
 void MediaStreamManager::CancelAllRequests(int render_process_id,
                                            int render_frame_id,
                                            int requester_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  auto request_it = requests_.begin();
+
+  DeviceRequests::const_iterator request_it = requests_.begin();
   while (request_it != requests_.end()) {
     if (request_it->second->requesting_process_id != render_process_id ||
         request_it->second->requesting_frame_id != render_frame_id ||
@@ -1824,9 +1847,10 @@
       ++request_it;
       continue;
     }
-    const std::string label = request_it->first;
-    ++request_it;
-    CancelRequest(label);
+
+    const DeviceRequests::const_iterator next = std::next(request_it);
+    CancelRequest(request_it);
+    request_it = next;
   }
 }
 
@@ -1862,8 +1886,9 @@
              request->stream_devices_set.stream_devices) {
           const blink::MediaStreamDevice* const device =
               GetStreamDevice(*stream_devices_ptr, session_id);
-          if (!device || device->id != device_id)
+          if (!device || device->id != device_id) {
             continue;
+          }
 
           if (request->IsTransferMapEmpty(device->type)) {
             // There are no ongoing transfers for this device.
@@ -1901,8 +1926,9 @@
              request->stream_devices_set.stream_devices) {
           const blink::MediaStreamDevice* const device =
               GetStreamDevice(*stream_devices_ptr, session_id);
-          if (!device)
+          if (!device) {
             continue;
+          }
 
           UpdateDeviceTransferStatus(request, device, transfer_id,
                                      TransferState::KEPT_ALIVE);
@@ -1959,15 +1985,17 @@
       if (devices.audio_device.has_value() &&
           devices.audio_device->type == type &&
           devices.audio_device->session_id() == session_id) {
-        if (request->state(type) == MEDIA_REQUEST_STATE_DONE)
+        if (request->state(type) == MEDIA_REQUEST_STATE_DONE) {
           CloseDevice(type, session_id);
+        }
         devices.audio_device = absl::nullopt;
       }
       if (devices.video_device.has_value() &&
           devices.video_device->type == type &&
           devices.video_device->session_id() == session_id) {
-        if (request->state(type) == MEDIA_REQUEST_STATE_DONE)
+        if (request->state(type) == MEDIA_REQUEST_STATE_DONE) {
           CloseDevice(type, session_id);
+        }
         devices.video_device = absl::nullopt;
       }
 
@@ -2015,8 +2043,9 @@
           continue;
         }
         const blink::MediaStreamDevice& device = device_ptr->value();
-        if (device.session_id() != session_id || device.type != type)
+        if (device.session_id() != session_id || device.type != type) {
           continue;
+        }
 
         MaybeStopTrackingCaptureHandleConfig(labeled_request.first, device);
         // Notify observers that this device is being closed.
@@ -2032,8 +2061,9 @@
         }
         if (request->ui_proxy) {
           const DesktopMediaID media_id = DesktopMediaID::Parse(device.id);
-          if (!media_id.is_null())
+          if (!media_id.is_null()) {
             request->ui_proxy->OnDeviceStopped(labeled_request.first, media_id);
+          }
         }
       }
     }
@@ -2098,8 +2128,9 @@
   DCHECK(stream_type == MediaStreamType::DEVICE_AUDIO_CAPTURE ||
          stream_type == MediaStreamType::DEVICE_VIDEO_CAPTURE);
   // The source_id can be empty if the constraint is set but empty.
-  if (source_id.empty())
+  if (source_id.empty()) {
     return false;
+  }
 
   // TODO(guidou): Change to use MediaDevicesManager::EnumerateDevices.
   // See http://crbug.com/648155.
@@ -2155,8 +2186,9 @@
       }
     }
   }
-  for (const auto& session_id : session_ids)
+  for (const auto& session_id : session_ids) {
     StopDevice(stream_type, session_id);
+  }
 }
 
 bool MediaStreamManager::PickDeviceId(
@@ -2164,8 +2196,9 @@
     const TrackControls& controls,
     const blink::WebMediaDeviceInfoArray& devices,
     std::string* device_id) const {
-  if (controls.device_id.empty())
+  if (controls.device_id.empty()) {
     return true;
+  }
   if (!GetDeviceIDAndGroupIDFromHMAC(
           salt_and_origin.device_id_salt, salt_and_origin.origin,
           controls.device_id, devices, device_id, /*group_id=*/nullptr)) {
@@ -2221,13 +2254,15 @@
   // Start enumeration for devices of all requested device types.
   bool request_audio_input =
       request->audio_type() != MediaStreamType::NO_SERVICE;
-  if (request_audio_input)
+  if (request_audio_input) {
     request->SetState(request->audio_type(), MEDIA_REQUEST_STATE_REQUESTED);
+  }
 
   bool request_video_input =
       request->video_type() != MediaStreamType::NO_SERVICE;
-  if (request_video_input)
+  if (request_video_input) {
     request->SetState(request->video_type(), MEDIA_REQUEST_STATE_REQUESTED);
+  }
 
   // base::Unretained is safe here because MediaStreamManager is deleted on the
   // UI thread, after the IO thread has been stopped.
@@ -2301,8 +2336,9 @@
       const blink::MediaStreamDevice* const existing_device =
           GetStreamDevice(*stream_devices_ptr, existing_device_session_id);
 
-      if (!existing_device)
+      if (!existing_device) {
         continue;
+      }
       if (existing_request->state(existing_device->type) !=
           MEDIA_REQUEST_STATE_DONE) {
         // TODO(https://crbug.com/1288839): Ensure state of MediaStreamDevice
@@ -2413,8 +2449,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!output_parameters || output_parameters->IsValid());
   DeviceRequest* request = FindRequest(label);
-  if (!request)
+  if (!request) {
     return;
+  }
   DCHECK(request->HasUIRequest());
   SendLogMessage(
       base::StringPrintf("PostRequestToUI({label=%s}, ", label.c_str()));
@@ -2423,10 +2460,12 @@
   const MediaStreamType video_type = request->video_type();
 
   // Post the request to UI and set the state.
-  if (blink::IsAudioInputMediaType(audio_type))
+  if (blink::IsAudioInputMediaType(audio_type)) {
     request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
-  if (blink::IsVideoInputMediaType(video_type))
+  }
+  if (blink::IsVideoInputMediaType(video_type)) {
     request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
+  }
 
   if (ShouldUseFakeUIProxy(request->video_type())) {
     request->ui_proxy = MakeFakeUIProxy(label, enumeration, request);
@@ -2722,8 +2761,9 @@
     const std::string& label) const {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DeviceRequest* request = FindRequest(label);
-  if (!request)
+  if (!request) {
     return MediaStreamDevices();
+  }
   return blink::ToMediaStreamDevicesList(request->stream_devices_set);
 }
 
@@ -2891,8 +2931,9 @@
     bool pan_tilt_zoom_allowed) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DeviceRequest* request = FindRequest(label);
-  if (!request)
+  if (!request) {
     return;
+  }
 
   SendLogMessage(base::StringPrintf(
       "PanTiltZoomPermissionChecked({label=%s}, {requester_id="
@@ -2902,8 +2943,9 @@
 
   request->PanTiltZoomPermissionChecked(label, pan_tilt_zoom_allowed);
 
-  if (request->IsGetDisplayMediaSet())
+  if (request->IsGetDisplayMediaSet()) {
     return;
+  }
 
 #if !BUILDFLAG(IS_ANDROID)
   // 1. Only the first call to SetCapturedDisplaySurfaceFocus() has an
@@ -3101,8 +3143,9 @@
     const std::string& label = labeled_request.first;
     DeviceRequest* request = labeled_request.second.get();
 
-    if (request->stream_devices_set.stream_devices.empty())
+    if (request->stream_devices_set.stream_devices.empty()) {
       continue;
+    }
 
     // It can happen that a previous stream already failed and set an error,
     // in which case this streams request does not need to be handled further.
@@ -3157,8 +3200,9 @@
               device.input.set_effects(effects);
             }
           }
-          if (RequestDone(*request))
+          if (RequestDone(*request)) {
             HandleRequestDone(label, request);
+          }
           break;
         }
       }
@@ -3228,8 +3272,9 @@
   MediaStreamType stream_types[] = {MediaStreamType::DEVICE_AUDIO_CAPTURE,
                                     MediaStreamType::DEVICE_VIDEO_CAPTURE};
   for (size_t i = 0; i < std::size(requested); ++i) {
-    if (!requested[i])
+    if (!requested[i]) {
       continue;
+    }
 
     DCHECK(request->audio_type() == stream_types[i] ||
            request->video_type() == stream_types[i]);
@@ -3238,10 +3283,11 @@
     }
   }
 
-  if (!SetUpDeviceCaptureRequest(request, enumeration))
+  if (!SetUpDeviceCaptureRequest(request, enumeration)) {
     FinalizeRequestFailed(request_it, MediaStreamRequestResult::NO_HARDWARE);
-  else
+  } else {
     ReadOutputParamsAndPostRequestToUI(label, request, enumeration);
+  }
 }
 
 void MediaStreamManager::Aborted(
@@ -3291,8 +3337,9 @@
 
 void MediaStreamManager::AddLogMessageOnIOThread(const std::string& message) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  for (const auto& callback : log_callbacks_)
+  for (const auto& callback : log_callbacks_) {
     callback.second.Run(message);
+  }
 }
 
 void MediaStreamManager::HandleAccessRequestResponse(
@@ -3379,8 +3426,9 @@
         int sample_rate = output_parameters.sample_rate();
         // If we weren't able to get the native sampling rate or the sample_rate
         // is outside the valid range for input devices set reasonable defaults.
-        if (sample_rate <= 0 || sample_rate > 96000)
+        if (sample_rate <= 0 || sample_rate > 96000) {
           sample_rate = 44100;
+        }
 
         media::AudioParameters params(
             device.input.format(), media::ChannelLayoutConfig::Stereo(),
@@ -3391,10 +3439,11 @@
         device.input = params;
       }
 
-      if (device.type == request->audio_type())
+      if (device.type == request->audio_type()) {
         found_audio = true;
-      else if (device.type == request->video_type())
+      } else if (device.type == request->video_type()) {
         found_video = true;
+      }
 
       // If this is request for a new MediaStream, a device is only opened once
       // per render frame. This is so that the permission to use a device can be
@@ -3446,11 +3495,13 @@
     DVLOG(1) << "Set no audio found label " << label;
   }
 
-  if (!found_video && blink::IsVideoInputMediaType(request->video_type()))
+  if (!found_video && blink::IsVideoInputMediaType(request->video_type())) {
     request->SetState(request->video_type(), MEDIA_REQUEST_STATE_ERROR);
+  }
 
-  if (RequestDone(*request))
+  if (RequestDone(*request)) {
     HandleRequestDone(label, request);
+  }
 }
 
 void MediaStreamManager::HandleChangeSourceRequestResponse(
@@ -3495,9 +3546,11 @@
 void MediaStreamManager::StopMediaStreamFromBrowser(const std::string& label) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  DeviceRequest* request = FindRequest(label);
-  if (!request)
+  const DeviceRequests::const_iterator request_it = FindRequestIterator(label);
+  if (request_it == requests_.end()) {
     return;
+  }
+  DeviceRequest* const request = request_it->second.get();
 
   SendLogMessage(base::StringPrintf("StopMediaStreamFromBrowser({label=%s})",
                                     label.c_str()));
@@ -3520,7 +3573,7 @@
     }
   }
 
-  CancelRequest(label);
+  CancelRequest(request_it);
   IncrementDesktopCaptureCounter(DESKTOP_CAPTURE_NOTIFICATION_STOP);
 }
 
@@ -3530,8 +3583,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   DeviceRequest* request = FindRequest(label);
-  if (!request)
+  if (!request) {
     return;
+  }
 
   DCHECK_EQ(1u, request->stream_devices_set.stream_devices.size());
   const blink::mojom::StreamDevices& devices =
@@ -3566,8 +3620,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   DeviceRequest* request = FindRequest(label);
-  if (!request)
+  if (!request) {
     return;
+  }
 
   SendLogMessage(base::StringPrintf("RequestStateChangeFromBrowser({label=%s})",
                                     label.c_str()));
@@ -3579,12 +3634,15 @@
   DVLOG(3) << "MediaStreamManager::WillDestroyCurrentMessageLoop()";
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) ||
          !BrowserThread::IsThreadInitialized(BrowserThread::IO));
-  if (media_devices_manager_)
+  if (media_devices_manager_) {
     media_devices_manager_->StopMonitoring();
-  if (video_capture_manager_)
+  }
+  if (video_capture_manager_) {
     video_capture_manager_->UnregisterListener(this);
-  if (audio_input_device_manager_)
+  }
+  if (audio_input_device_manager_) {
     audio_input_device_manager_->UnregisterListener(this);
+  }
 
   audio_input_device_manager_ = nullptr;
   video_capture_manager_ = nullptr;
@@ -3610,13 +3668,15 @@
   if (blink::IsAudioInputMediaType(stream_type)) {
     MediaCaptureDevicesImpl::GetInstance()->OnAudioCaptureDevicesChanged(
         new_devices);
-    if (media_observer)
+    if (media_observer) {
       media_observer->OnAudioCaptureDevicesChanged();
+    }
   } else if (blink::IsVideoInputMediaType(stream_type)) {
     MediaCaptureDevicesImpl::GetInstance()->OnVideoCaptureDevicesChanged(
         new_devices);
-    if (media_observer)
+    if (media_observer) {
       media_observer->OnVideoCaptureDevicesChanged();
+    }
   } else {
     NOTREACHED();
   }
@@ -3637,25 +3697,28 @@
       !requested_audio ||
       request.state(request.audio_type()) == MEDIA_REQUEST_STATE_DONE ||
       request.state(request.audio_type()) == MEDIA_REQUEST_STATE_ERROR;
-  if (!audio_done)
+  if (!audio_done) {
     return false;
+  }
 
   const bool video_done =
       !requested_video ||
       request.state(request.video_type()) == MEDIA_REQUEST_STATE_DONE ||
       request.state(request.video_type()) == MEDIA_REQUEST_STATE_ERROR;
-  if (!video_done)
+  if (!video_done) {
     return false;
+  }
 
   return true;
 }
 
 MediaStreamProvider* MediaStreamManager::GetDeviceManager(
     MediaStreamType stream_type) const {
-  if (blink::IsVideoInputMediaType(stream_type))
+  if (blink::IsVideoInputMediaType(stream_type)) {
     return video_capture_manager();
-  else if (blink::IsAudioInputMediaType(stream_type))
+  } else if (blink::IsAudioInputMediaType(stream_type)) {
     return audio_input_device_manager();
+  }
   NOTREACHED();
   return nullptr;
 }
@@ -3665,11 +3728,13 @@
     blink::mojom::StreamDevicesSetPtr stream_devices_set,
     gfx::NativeViewId window_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (!window_id)
+  if (!window_id) {
     return;
+  }
 
-  if (!blink::IsVideoDesktopCaptureMediaType(video_type))
+  if (!blink::IsVideoDesktopCaptureMediaType(video_type)) {
     return;
+  }
 
   // Pass along for desktop screen and window capturing when
   // DesktopCaptureDevice is used.
@@ -3684,17 +3749,20 @@
         continue;
       }
       const blink::MediaStreamDevice& device = device_ptr->value();
-      if (!blink::IsVideoDesktopCaptureMediaType(device.type))
+      if (!blink::IsVideoDesktopCaptureMediaType(device.type)) {
         continue;
+      }
 
       DesktopMediaID media_id = DesktopMediaID::Parse(device.id);
       // WebContentsVideoCaptureDevice is used for tab/webcontents.
-      if (media_id.type == DesktopMediaID::TYPE_WEB_CONTENTS)
+      if (media_id.type == DesktopMediaID::TYPE_WEB_CONTENTS) {
         continue;
+      }
 #if defined(USE_AURA)
       // DesktopCaptureDeviceAura is used when aura_id is valid.
-      if (media_id.window_id > DesktopMediaID::kNullId)
+      if (media_id.window_id > DesktopMediaID::kNullId) {
         continue;
+      }
 #endif
       video_capture_manager_->SetDesktopCaptureWindowId(device.session_id(),
                                                         window_id);
@@ -3812,8 +3880,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   for (LabeledDeviceRequest& labeled_request : requests_) {
     DeviceRequest* request = labeled_request.second.get();
-    if (request->requesting_process_id != render_process_id)
+    if (request->requesting_process_id != render_process_id) {
       continue;
+    }
 
     for (const blink::mojom::StreamDevicesPtr& stream_devices_ptr :
          request->stream_devices_set.stream_devices) {
@@ -3868,15 +3937,17 @@
   RenderFrameHostImpl* rfh =
       RenderFrameHostImpl::FromID(source.web_contents_id.render_process_id,
                                   source.web_contents_id.main_render_frame_id);
-  if (rfh)
+  if (rfh) {
     rfh->render_view_host()->GetDelegate()->Activate();
+  }
 }
 
 void MediaStreamManager::OnStreamStarted(const std::string& label) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DeviceRequest* const request = FindRequest(label);
-  if (!request)
+  if (!request) {
     return;
+  }
   SendLogMessage(base::StringPrintf(
       "OnStreamStarted({label=%s}, {requester_id=%d}, {request_type=%s})",
       label.c_str(), request->requester_id,
@@ -4018,8 +4089,9 @@
 
   RenderFrameHost* rfh =
       RenderFrameHost::FromID(requesting_process_id, requesting_frame_id);
-  if (!rfh)
+  if (!rfh) {
     return nullptr;
+  }
 
   return PermissionControllerImpl::FromBrowserContext(rfh->GetBrowserContext());
 }
@@ -4057,8 +4129,9 @@
 
   PermissionControllerImpl* controller =
       GetPermissionController(requesting_process_id, requesting_frame_id);
-  if (!controller)
+  if (!controller) {
     return;
+  }
 
   PermissionController::SubscriptionId audio_subscription_id;
   PermissionController::SubscriptionId video_subscription_id;
@@ -4139,8 +4212,9 @@
 
   PermissionControllerImpl* controller =
       GetPermissionController(requesting_process_id, requesting_frame_id);
-  if (!controller)
+  if (!controller) {
     return;
+  }
 
   controller->UnsubscribePermissionStatusChange(audio_subscription_id);
   controller->UnsubscribePermissionStatusChange(video_subscription_id);
@@ -4152,8 +4226,9 @@
     int requester_id,
     int page_request_id,
     blink::mojom::PermissionStatus status) {
-  if (status == blink::mojom::PermissionStatus::GRANTED)
+  if (status == blink::mojom::PermissionStatus::GRANTED) {
     return;
+  }
 
   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
     // It is safe to bind base::Unretained(this) because MediaStreamManager is
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index bfa8dd94..e7f2076 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -542,6 +542,7 @@
                                   const blink::MediaStreamDevice* const device,
                                   const base::UnguessableToken& transfer_id,
                                   TransferState transfer_state);
+  void CancelRequest(DeviceRequests::const_iterator request_it);
   void DeleteRequest(DeviceRequests::const_iterator request_it);
 
   // Prepare the request with label |label| by starting device enumeration if
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index e277498e..33c6c9b 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -2952,6 +2952,8 @@
     return;
   }
 
+  // During initialization, a RenderFrameHost always has a non-null `owner_`.
+  // This is called from the constructor.
   CHECK(owner_);
 
   // The initial empty document inherits its policy container from its creator.
@@ -3178,7 +3180,7 @@
   // If this was the current pending or speculative RFH dying, cancel and
   // destroy it.
   if (lifecycle_state_ == LifecycleStateImpl::kSpeculative) {
-    CHECK(owner_);
+    CHECK(owner_);  // See `owner_` invariants about `lifecycle_state_`.
     owner_->GetRenderFrameHostManager()
         .CleanupSpeculativeRfhForRenderProcessGone();
   }
@@ -3524,11 +3526,13 @@
 }
 
 RenderFrameProxyHost* RenderFrameHostImpl::GetProxyToOuterDelegate() {
-  // Only the main frame should be able to reach the outer WebContents.
+  // Precondition:RFH in subframe, in pending deletion, speculative, or in the
+  // BFCache are not expected to access the outer WebContents.
+  CHECK(lifecycle_state_ == LifecycleStateImpl::kActive ||
+        lifecycle_state_ == LifecycleStateImpl::kPrerendering);
   DCHECK(is_main_frame());
-  // `owner_` should not be null since we don't allow to update the parent on
-  // behalf of the inactive document.
-  CHECK(owner_);
+
+  CHECK(owner_);  // See `owner_` invariants about `lifecycle_state_`.
   return owner_->GetRenderFrameHostManager().GetProxyToOuterDelegate();
 }
 
@@ -3536,8 +3540,7 @@
     network::mojom::ReferrerPolicy referrer_policy) {
   if (!IsActive())
     return;
-  // `owner_` will never be null if `IsActive()` returns true.
-  DCHECK(owner_);
+  DCHECK(owner_);  // See `owner_` invariants about `IsActive()`.
   owner_->DidChangeReferrerPolicy(referrer_policy);
 }
 
@@ -4155,7 +4158,7 @@
   // about the new frame.  Create a proxy for the child frame in all
   // SiteInstances that have a proxy for the frame's parent, since all frames
   // in a frame tree should have the same set of proxies.
-  CHECK(owner_);
+  CHECK(owner_);  // See `owner_` invariants about `lifecycle_state_`.
   owner_->GetRenderFrameHostManager().CreateProxiesForChildFrame(child.get());
 
   // When the child is added, it hasn't committed any navigation yet - its
@@ -4362,7 +4365,7 @@
   if (!IsActive())
     return;
 
-  DCHECK(owner_);
+  DCHECK(owner_);  // See `owner_` invariants about `IsActive()`.
   owner_->SetFocusedFrame(GetSiteInstance()->group());
 }
 
@@ -5876,6 +5879,7 @@
 
 void RenderFrameHostImpl::PrepareForInnerWebContentsAttach(
     PrepareForInnerWebContentsAttachCallback callback) {
+  // TODO(https://crbug.com/1405759) Explain why `owner_` exists.
   CHECK(owner_);
   owner_->GetRenderFrameHostManager().PrepareForInnerDelegateAttach(
       std::move(callback));
@@ -6682,7 +6686,7 @@
     // Reject requests made without transient user activation or a token.
     // TODO(lanwei): Investigate whether we can terminate the renderer when
     // transient user activation and the delegated token are both inactive.
-    CHECK(owner_);
+    CHECK(owner_);  // See `owner_` invariants about `IsActive()`.
     const bool consumed_activation = owner_->UpdateUserActivationState(
         blink::mojom::UserActivationUpdateType::kConsumeTransientActivation,
         blink::mojom::UserActivationNotificationType::kNone);
@@ -6914,7 +6918,7 @@
   if (lifecycle_state() != LifecycleStateImpl::kActive)
     return;
 
-  CHECK(owner_);
+  CHECK(owner_);  // See `owner_` invariants about `lifecycle_state_`.
   owner_->UpdateUserActivationState(update_type, notification_type);
 }
 
@@ -7404,7 +7408,8 @@
   // This async Mojo method can be called from the renderer before entering
   // BFCache but the message can arrive here after it.
   if (!IsPendingDeletion() && !IsInBackForwardCache()) {
-    DCHECK(owner_);
+    DCHECK(owner_);  // See `owner_` invariants about IsPendingDeletion() and
+                     // IsInBackForwardCache().
     owner_->DidStopLoading();
   }
 }
@@ -7511,6 +7516,9 @@
   // NB: This call will consume activations in the browser and the remote frame
   // proxies for this frame. The initiating renderer will consume its view of
   // the activations after we return.
+
+  // See `owner_` invariants about `IsActive()`, which is implied by
+  // `can_create_window`.
   CHECK(owner_);
   bool was_consumed = owner_->UpdateUserActivationState(
       blink::mojom::UserActivationUpdateType::kConsumeTransientActivation,
@@ -8096,11 +8104,14 @@
     // If this is reached in case the RenderFrameHost is in BackForwardCache
     // evict the document from BackForwardCache.
     if (IsInactiveAndDisallowActivation(
-            DisallowActivationReasonId::kBeginNavigation))
+            DisallowActivationReasonId::kBeginNavigation)) {
       return;
+    }
   }
 
-  // `owner_` can not be null since the lifecycle state is checked above.
+  // See `owner_` invariants about `lifecycle_state_`.
+  // `IsInactiveAndDisallowActivation()` check cause both pending deletion and
+  // bfcached states to return early.
   DCHECK(owner_);
   if (owner_->GetRenderFrameHostManager().is_attaching_inner_delegate()) {
     // Avoid starting any new navigations since this frame is in the process of
@@ -8633,6 +8644,8 @@
   if (!for_navigation) {
     // Cancel any pending navigations, to avoid their navigation commit/fail
     // event from wiping out the is_waiting_for_beforeunload_completion_ state.
+    // TODO(https://crbug.com/1405759) Explain why `owner_` is non-null here. It
+    // isn't clear why.
     CHECK(owner_);
     owner_->CancelNavigation();
   }
@@ -11195,6 +11208,12 @@
              url, frame_tree_node_->is_on_initial_empty_document()) ||
          is_same_document);
   DCHECK(!is_same_document_history_api_navigation || is_same_document);
+  DCHECK(!IsPendingDeletion());     // IPC is filtered out by the caller.
+  DCHECK(!IsInBackForwardCache());  // A page in the BackForwardCache is fully
+                                    // loaded and has no pending navigations.
+  // See `owner_` invariants about IsPendingDeletion() and
+  // IsInBackForwardCache().
+  CHECK(owner_);
 
   net::IsolationInfo isolation_info = ComputeIsolationInfoInternal(
       origin, net::IsolationInfo::RequestType::kOther, IsCredentialless(),
@@ -11251,7 +11270,6 @@
   // last committed navigation.
   bool is_overriding_user_agent = is_same_document && is_overriding_user_agent_;
 
-  CHECK(owner_);
   return owner_->CreateNavigationRequestForSynchronousRendererCommit(
       this, is_same_document, url, origin, initiator_base_url, isolation_info,
       std::move(referrer), transition, should_replace_current_entry, method,
@@ -11558,8 +11576,9 @@
   // renderer before getting to NavigationController::RendererDidNavigate if
   // that happens.
   if (is_same_document_navigation) {
-    // `owner_` cannot be null when this is from a same-document navigation.
-    DCHECK(owner_);
+    // `owner_` must exist, because `DidCommitSameDocumentNavigation()` returns
+    // early for RenderFrameHost pending deletion or in the BackForwardCache.
+    CHECK(owner_);
     if (owner_->GetCurrentNavigator()
             .controller()
             .has_post_commit_error_entry()) {
@@ -11890,7 +11909,8 @@
     bool was_loading =
         frame_tree()->LoadingTree()->IsLoadingIncludingInnerFrameTrees();
     is_loading_ = true;
-    DCHECK(owner_);
+    // TODO(https://crbug.com/1405759): Explain why this is true.
+    CHECK(owner_);
     owner_->DidStartLoading(should_show_loading_ui, was_loading);
   }
 
@@ -12358,6 +12378,7 @@
   if (result == blink::mojom::CommitResult::RestartCrossDocument) {
     // The navigation could not be committed as a same-document navigation.
     // Restart the navigation cross-document.
+    // TODO(https://crbug.com/1405759): Explain why `owner_` exists.
     CHECK(owner_);
     owner_->RestartNavigationAsCrossDocument(std::move(request->second));
     return;
diff --git a/content/browser/service_worker/service_worker_context_watcher.cc b/content/browser/service_worker/service_worker_context_watcher.cc
index 050b3d1d..47f9340 100644
--- a/content/browser/service_worker/service_worker_context_watcher.cc
+++ b/content/browser/service_worker/service_worker_context_watcher.cc
@@ -365,8 +365,6 @@
     const GURL& scope,
     const blink::StorageKey& key) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  // TODO(crbug.com/1199077): Pipe in StorageKey when it's available in this
-  // function.
   SendRegistrationInfo(registration_id, scope, key,
                        ServiceWorkerRegistrationInfo::IS_NOT_DELETED);
 }
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index a06519a..88a5ecb 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -422,8 +422,6 @@
   if (is_deleting_and_starting_over_)
     return;
 
-  // TODO(crbug.com/1199077): Update this when ServiceWorkerContextCoreObserver
-  // implements StorageKey.
   auto insertion_result = running_service_workers_.insert(std::make_pair(
       version_id,
       ServiceWorkerRunningInfo(script_url, scope, key, process_id, token)));
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc
index b00282c7..32fa3911 100644
--- a/content/browser/service_worker/service_worker_internals_ui.cc
+++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -726,8 +726,6 @@
 
   // ServiceWorkerContextWrapper::UnregisterServiceWorker doesn't work here
   // because that reduces a status code to boolean.
-  // TODO(crbug.com/1199077): Update this when ServiceWorkerInternalsHandler
-  // implements StorageKey.
   context->context()->UnregisterServiceWorker(scope, storage_key,
                                               /*is_immediate=*/false,
                                               std::move(callback));
diff --git a/content/browser/theme_helper_mac.mm b/content/browser/theme_helper_mac.mm
index 34f8f293..3ebc4c8 100644
--- a/content/browser/theme_helper_mac.mm
+++ b/content/browser/theme_helper_mac.mm
@@ -305,29 +305,34 @@
         if (color) {
           values[i] = NSColorToSkColor(color);
         } else {
-          // If the controlAccentBlueColor isn't available just set a dummy
-          // black value.
+          // If the controlAccentBlueColor isn't available just set a black
+          // value.
           values[i] = SK_ColorBLACK;
         }
         break;
       }
       case blink::MacSystemColorID::kControlAccentColor:
         if (@available(macOS 10.14, *)) {
-          values[i] = NSColorToSkColor([NSColor controlAccentColor]);
+          values[i] = NSColorToSkColor(NSColor.controlAccentColor);
         } else {
           // controlAccentColor property is not available before macOS 10.14,
           // so keyboardFocusIndicatorColor is used instead.
-          values[i] = NSColorToSkColor([NSColor keyboardFocusIndicatorColor]);
+          values[i] = NSColorToSkColor(NSColor.keyboardFocusIndicatorColor);
         }
         break;
       case blink::MacSystemColorID::kKeyboardFocusIndicator:
-        values[i] = NSColorToSkColor([NSColor keyboardFocusIndicatorColor]);
+        values[i] = NSColorToSkColor(NSColor.keyboardFocusIndicatorColor);
         break;
       case blink::MacSystemColorID::kSecondarySelectedControl:
-        values[i] = NSColorToSkColor([NSColor secondarySelectedControlColor]);
+        if (@available(macOS 10.14, *)) {
+          values[i] = NSColorToSkColor(
+              NSColor.unemphasizedSelectedContentBackgroundColor);
+        } else {
+          values[i] = NSColorToSkColor(NSColor.secondarySelectedControlColor);
+        }
         break;
       case blink::MacSystemColorID::kSelectedTextBackground:
-        values[i] = NSColorToSkColor([NSColor selectedTextBackgroundColor]);
+        values[i] = NSColorToSkColor(NSColor.selectedTextBackgroundColor);
         break;
       case blink::MacSystemColorID::kCount:
         NOTREACHED();
@@ -342,31 +347,39 @@
   base::span<SkColor> values = writable_color_map_.GetMemoryAsSpan<SkColor>(
       blink::kMacSystemColorIDCount * blink::kMacSystemColorSchemeCount);
 
-  NSAppearance* savedAppearance;
-  if (@available(macOS 10.14, *)) {
-    savedAppearance = [NSAppearance currentAppearance];
-    // Ensure light mode appearance in web content even if the topchrome is in
-    // dark mode.
+  if (@available(macOS 11, *)) {
+    [[NSAppearance appearanceNamed:NSAppearanceNameAqua]
+        performAsCurrentDrawingAppearance:^{
+          LoadSystemColorsForCurrentAppearance(values.subspan(
+              0, static_cast<size_t>(blink::MacSystemColorID::kCount)));
+        }];
+    [[NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]
+        performAsCurrentDrawingAppearance:^{
+          LoadSystemColorsForCurrentAppearance(values.subspan(
+              static_cast<size_t>(blink::MacSystemColorID::kCount),
+              static_cast<size_t>(blink::MacSystemColorID::kCount)));
+        }];
+  } else if (@available(macOS 10.14, *)) {
+    NSAppearance* savedAppearance = [NSAppearance currentAppearance];
     [NSAppearance
         setCurrentAppearance:[NSAppearance
                                  appearanceNamed:NSAppearanceNameAqua]];
-  }
-
-  LoadSystemColorsForCurrentAppearance(
-      values.subspan(0, static_cast<size_t>(blink::MacSystemColorID::kCount)));
-
-  if (@available(macOS 10.14, *)) {
+    LoadSystemColorsForCurrentAppearance(values.subspan(
+        0, static_cast<size_t>(blink::MacSystemColorID::kCount)));
     [NSAppearance
         setCurrentAppearance:[NSAppearance
                                  appearanceNamed:NSAppearanceNameDarkAqua]];
-  }
-
-  LoadSystemColorsForCurrentAppearance(
-      values.subspan(static_cast<size_t>(blink::MacSystemColorID::kCount),
-                     static_cast<size_t>(blink::MacSystemColorID::kCount)));
-
-  if (@available(macOS 10.14, *))
+    LoadSystemColorsForCurrentAppearance(
+        values.subspan(static_cast<size_t>(blink::MacSystemColorID::kCount),
+                       static_cast<size_t>(blink::MacSystemColorID::kCount)));
     [NSAppearance setCurrentAppearance:savedAppearance];
+  } else {
+    LoadSystemColorsForCurrentAppearance(values.subspan(
+        0, static_cast<size_t>(blink::MacSystemColorID::kCount)));
+    LoadSystemColorsForCurrentAppearance(
+        values.subspan(static_cast<size_t>(blink::MacSystemColorID::kCount),
+                       static_cast<size_t>(blink::MacSystemColorID::kCount)));
+  }
 }
 
 void ThemeHelperMac::Observe(int type,
diff --git a/content/browser/webauth/authenticator_common_impl.cc b/content/browser/webauth/authenticator_common_impl.cc
index 1dc8def..2162d21 100644
--- a/content/browser/webauth/authenticator_common_impl.cc
+++ b/content/browser/webauth/authenticator_common_impl.cc
@@ -1819,7 +1819,7 @@
       case RequestExtension::kLargeBlobEnable:
         response->echo_large_blob = true;
         response->supports_large_blob =
-            response_data.large_blob_key.has_value();
+            response_data.has_associated_large_blob_key;
         break;
       case RequestExtension::kCredBlob:
         response->echo_cred_blob = true;
diff --git a/content/browser/webauth/webauth_browsertest.cc b/content/browser/webauth/webauth_browsertest.cc
index 593b847..b8d4644 100644
--- a/content/browser/webauth/webauth_browsertest.cc
+++ b/content/browser/webauth/webauth_browsertest.cc
@@ -1650,7 +1650,6 @@
 
   device::FakeWinWebAuthnApi fake_api;
   fake_api.set_is_uvpaa(true);
-  fake_api.set_hresult(S_OK);
   auto* virtual_device_factory = InjectVirtualFidoDeviceFactory();
   virtual_device_factory->set_win_webauthn_api(&fake_api);
 
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 45e356a..f05d416 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -795,11 +795,11 @@
     const IdpNetworkRequestManager::ClientMetadata& client_metadata) {
   const GURL& idp_config_url = idp_info->provider->config_url;
   const std::string idp_for_display = FormatUrlForDisplay(idp_config_url);
-  idp_info->data = IdentityProviderData(
-      idp_for_display, accounts, idp_info->metadata,
-      ClientMetadata{GURL(client_metadata.terms_of_service_url),
-                     GURL(client_metadata.privacy_policy_url)},
-      idp_info->rp_context);
+  idp_info->data =
+      IdentityProviderData(idp_for_display, accounts, idp_info->metadata,
+                           ClientMetadata{client_metadata.terms_of_service_url,
+                                          client_metadata.privacy_policy_url},
+                           idp_info->rp_context);
   idp_infos_[idp_config_url] = std::move(idp_info);
 
   pending_idps_.erase(idp_config_url);
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index bdb420c..85331278 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -373,8 +373,8 @@
         FROM_HERE,
         base::BindOnce(std::move(callback), info.client_metadata.fetch_status,
                        IdpNetworkRequestManager::ClientMetadata{
-                           info.client_metadata.privacy_policy_url,
-                           info.client_metadata.terms_of_service_url}));
+                           GURL(info.client_metadata.privacy_policy_url),
+                           GURL(info.client_metadata.terms_of_service_url)}));
   }
 
   void SendAccountsRequest(const GURL& accounts_url,
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
index 0688b12..b92f390 100644
--- a/content/browser/webid/idp_network_request_manager.cc
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -427,9 +427,13 @@
   auto ExtractUrl = [&](const char* key) {
     const std::string* endpoint = response.FindString(key);
     if (!endpoint) {
-      return std::string();
+      return GURL();
     }
-    return *endpoint;
+    GURL url = GURL(*endpoint);
+    if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS()) {
+      return GURL();
+    }
+    return url;
   };
 
   IdpNetworkRequestManager::ClientMetadata data;
diff --git a/content/browser/webid/idp_network_request_manager.h b/content/browser/webid/idp_network_request_manager.h
index b6904d3..3050a06 100644
--- a/content/browser/webid/idp_network_request_manager.h
+++ b/content/browser/webid/idp_network_request_manager.h
@@ -98,8 +98,8 @@
   };
 
   struct ClientMetadata {
-    std::string privacy_policy_url;
-    std::string terms_of_service_url;
+    GURL privacy_policy_url;
+    GURL terms_of_service_url;
   };
 
   // Error codes sent to the metrics endpoint.
diff --git a/content/browser/webid/idp_network_request_manager_unittest.cc b/content/browser/webid/idp_network_request_manager_unittest.cc
index 2bc8a32..610408b7 100644
--- a/content/browser/webid/idp_network_request_manager_unittest.cc
+++ b/content/browser/webid/idp_network_request_manager_unittest.cc
@@ -212,12 +212,11 @@
 
   IdpClientMetadata SendClientMetadataRequestAndWaitForResponse(
       const char* client_id,
-      net::HttpStatusCode http_status = net::HTTP_OK) {
-    const char response[] = R"({})";
+      const std::string& response = R"({})") {
     GURL client_id_endpoint(kTestClientMetadataEndpoint);
     test_url_loader_factory().AddResponse(
         client_id_endpoint.spec() + "?client_id=" + client_id, response,
-        http_status);
+        net::HTTP_OK);
 
     IdpClientMetadata data;
     base::RunLoop run_loop;
@@ -913,8 +912,8 @@
   test_url_loader_factory().SetInterceptor(interceptor);
   IdpClientMetadata data = SendClientMetadataRequestAndWaitForResponse("xxx");
   ASSERT_TRUE(called);
-  ASSERT_EQ("", data.privacy_policy_url);
-  ASSERT_EQ("", data.terms_of_service_url);
+  ASSERT_EQ(GURL(), data.privacy_policy_url);
+  ASSERT_EQ(GURL(), data.terms_of_service_url);
 }
 
 // Tests that we correctly records metrics regarding approved_clients.
@@ -1066,6 +1065,32 @@
   EXPECT_EQ(net::HTTP_INTERNAL_SERVER_ERROR, fetch_status.response_code);
 }
 
+TEST_F(IdpNetworkRequestManagerTest, FetchClientMetadataValidUrls) {
+  // Both HTTPS and HTTP URLs are allowed.
+  const std::string privacy_policy_url = "https://privacy.policy";
+  const std::string terms_of_service_url = "http://terms.of.service";
+
+  IdpClientMetadata data = SendClientMetadataRequestAndWaitForResponse(
+      /*client_id=*/"123", R"({"privacy_policy_url": ")" + privacy_policy_url +
+                               R"(", "terms_of_service_url": ")" +
+                               terms_of_service_url + R"("})");
+  ASSERT_EQ(GURL(privacy_policy_url), data.privacy_policy_url);
+  ASSERT_EQ(GURL(terms_of_service_url), data.terms_of_service_url);
+}
+
+TEST_F(IdpNetworkRequestManagerTest, FetchClientMetadataInvalidUrls) {
+  // Non-HTTP(S) URLs should not be allowed.
+  const std::string privacy_policy_url = "chrome://settings";
+  const std::string terms_of_service_url = "file:///Users/you/file.html";
+
+  IdpClientMetadata data = SendClientMetadataRequestAndWaitForResponse(
+      /*client_id=*/"123", R"({"privacy_policy_url": ")" + privacy_policy_url +
+                               R"(", "terms_of_service_url": ")" +
+                               terms_of_service_url + R"("})");
+  ASSERT_EQ(GURL(), data.privacy_policy_url);
+  ASSERT_EQ(GURL(), data.terms_of_service_url);
+}
+
 }  // namespace
 
 }  // namespace content
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc
index 92f5394..0b8a9f69 100644
--- a/content/browser/webui/shared_resources_data_source.cc
+++ b/content/browser/webui/shared_resources_data_source.cc
@@ -74,6 +74,8 @@
       IDR_MULTIDEVICE_MULTIDEVICE_SETUP_MOJOM_WEBUI_JS,
       IDR_MULTIDEVICE_MULTIDEVICE_TYPES_MOJOM_LITE_JS,
       IDR_MULTIDEVICE_MULTIDEVICE_TYPES_MOJOM_WEBUI_JS,
+      IDR_NEARBY_NEARBY_SHARE_SETTINGS_MOJOM_WEBUI_JS,
+      IDR_NEARBY_NEARBY_SHARE_TARGET_TYPES_MOJOM_WEBUI_JS,
   };
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 76903db..362038a 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -378,8 +378,6 @@
       {wrf::EnableAutomationControlled, switches::kRemoteDebuggingPipe, true},
       {wrf::EnableDatabase, switches::kDisableDatabases, false},
       {wrf::EnableFileSystem, switches::kDisableFileSystem, false},
-      {wrf::EnableFileSystemSyncAccessHandleAsyncInterfaceOverride,
-       switches::kFileSystemSyncAccessHandleAsyncInterfaceEnabled, true},
       {wrf::EnableNetInfoDownlinkMax,
        switches::kEnableNetworkInformationDownlinkMax, true},
       {wrf::EnableNotifications, switches::kDisableNotifications, false},
diff --git a/content/common/private_aggregation_features.cc b/content/common/private_aggregation_features.cc
index ea64098..9561800 100644
--- a/content/common/private_aggregation_features.cc
+++ b/content/common/private_aggregation_features.cc
@@ -19,4 +19,9 @@
     &kPrivateAggregationApi, "enabled_in_fledge",
     /*default_value=*/true};
 
+constexpr base::FeatureParam<bool>
+    kPrivateAggregationApiFledgeExtensionsEnabled{&kPrivateAggregationApi,
+                                                  "fledge_extensions_enabled",
+                                                  /*default_value=*/false};
+
 }  // namespace content
diff --git a/content/common/private_aggregation_features.h b/content/common/private_aggregation_features.h
index 56695e5..bedf8f7b 100644
--- a/content/common/private_aggregation_features.h
+++ b/content/common/private_aggregation_features.h
@@ -22,6 +22,12 @@
     kPrivateAggregationApiEnabledInSharedStorage;
 extern const base::FeatureParam<bool> kPrivateAggregationApiEnabledInFledge;
 
+// Allows the FLEDGE-specific extensions, e.g. `reportContributionForEvent()` to
+// be enabled. Also requires `kPrivateAggregationApiEnabledInFledge.Get()` to be
+// true for this param to have any effect.
+extern const base::FeatureParam<bool>
+    kPrivateAggregationApiFledgeExtensionsEnabled;
+
 }  // namespace content
 
 #endif  // CONTENT_COMMON_PRIVATE_AGGREGATION_FEATURES_H_
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index d21db85..961e24b 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -432,6 +432,8 @@
         angle::IsAMD(gpu_info->active_gpu().vendor_id);
     sandbox_options.use_intel_specific_policies =
         angle::IsIntel(gpu_info->active_gpu().vendor_id);
+    sandbox_options.use_virtio_specific_policies =
+        angle::IsVirtIO(gpu_info->active_gpu().vendor_id);
     sandbox_options.use_nvidia_specific_policies =
         angle::IsNVIDIA(gpu_info->active_gpu().vendor_id);
     sandbox_options.use_asahi_specific_policies =
diff --git a/content/gpu/gpu_sandbox_hook_linux.cc b/content/gpu/gpu_sandbox_hook_linux.cc
index 2d975f07..da71601 100644
--- a/content/gpu/gpu_sandbox_hook_linux.cc
+++ b/content/gpu/gpu_sandbox_hook_linux.cc
@@ -297,6 +297,39 @@
   AddDrmGpuPermissions(permissions);
 }
 
+void AddVirtIOGpuPermissions(std::vector<BrokerFilePermission>* permissions) {
+  static const char* const kReadOnlyList[] = {
+      "/etc/ld.so.cache",
+      // To support threads in mesa we use --gpu-sandbox-start-early and
+      // that requires the following libs and files to be accessible.
+      // "/sys", "/sys/dev", "/sys/dev/char", "/sys/devices" are probed in order
+      // to use kms_swrast.
+      "/sys",
+      "/sys/dev",
+      "/usr/lib64/libEGL.so.1",
+      "/usr/lib64/libGLESv2.so.2",
+      "/usr/lib64/libglapi.so.0",
+      "/usr/lib64/libc++.so.1",
+      // If kms_swrast_dri is not usable, swrast_dri is used instead.
+      "/usr/lib64/dri/swrast_dri.so",
+      "/usr/lib64/dri/kms_swrast_dri.so",
+      "/usr/lib64/dri/virtio_gpu_dri.so",
+  };
+  for (const char* item : kReadOnlyList) {
+    permissions->push_back(BrokerFilePermission::ReadOnly(item));
+  }
+  static const char* kDevices[] = {"/sys/dev/char", "/sys/devices"};
+  for (const char* item : kDevices) {
+    std::string path(item);
+    permissions->push_back(
+        BrokerFilePermission::StatOnlyWithIntermediateDirs(path));
+    permissions->push_back(BrokerFilePermission::ReadOnly(path));
+    permissions->push_back(BrokerFilePermission::ReadOnlyRecursive(path + "/"));
+  }
+
+  AddDrmGpuPermissions(permissions);
+}
+
 void AddArmGpuPermissions(std::vector<BrokerFilePermission>* permissions) {
   static const char kLdSoCache[] = "/etc/ld.so.cache";
 
@@ -443,6 +476,9 @@
     if (options.use_nvidia_specific_policies) {
       AddStandardGpuPermissions(&permissions, options);
     }
+    if (options.use_virtio_specific_policies) {
+      AddVirtIOGpuPermissions(&permissions);
+    }
     return permissions;
   }
 
@@ -610,7 +646,8 @@
   command_set.set(sandbox::syscall_broker::COMMAND_STAT);
   if (IsChromeOS() &&
       (options.use_amd_specific_policies ||
-       options.use_intel_specific_policies || IsArchitectureArm())) {
+       options.use_intel_specific_policies ||
+       options.use_virtio_specific_policies || IsArchitectureArm())) {
     command_set.set(sandbox::syscall_broker::COMMAND_READLINK);
   }
   return command_set;
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index 6fe1c662..dd9d9f8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -237,9 +237,6 @@
     // return an AccessibilityNodeProvider or process touch exploration events.
     private boolean mIsObscuredByAnotherView;
 
-    // Accessibility touch exploration state.
-    private boolean mTouchExplorationEnabled;
-
     // This array maps a given virtualViewId to an |AccessibilityNodeInfoCompat| for that view. We
     // use this to update a node quickly rather than building from one scratch each time.
     private SparseArray<AccessibilityNodeInfoCompat> mNodeInfoCache = new SparseArray<>();
@@ -861,18 +858,11 @@
     }
 
     @Override
-    public boolean isTouchExplorationEnabled() {
-        return mTouchExplorationEnabled;
-    }
-
-    @Override
     public void setState(boolean state) {
         if (!state) {
             mNativeAccessibilityAllowed = false;
-            mTouchExplorationEnabled = false;
         } else {
             mNativeAccessibilityAllowed = true;
-            mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
         }
     }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/SelectPopup.java b/content/public/android/java/src/org/chromium/content/browser/input/SelectPopup.java
index ee77f67..3dd03b96 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/SelectPopup.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/SelectPopup.java
@@ -18,10 +18,10 @@
 import org.chromium.content.browser.PopupController.HideablePopup;
 import org.chromium.content.browser.WindowEventObserver;
 import org.chromium.content.browser.WindowEventObserverManager;
-import org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl;
 import org.chromium.content.browser.webcontents.WebContentsImpl;
 import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.accessibility.AccessibilityState;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.ViewAndroidDelegate;
 import org.chromium.ui.base.WindowAndroid;
@@ -150,9 +150,8 @@
         for (int i = 0; i < items.length; i++) {
             popupItems.add(new SelectPopupItem(items[i], enabled[i]));
         }
-        WebContentsAccessibilityImpl wcax =
-                WebContentsAccessibilityImpl.fromWebContents(mWebContents);
-        if (DeviceFormFactor.isTablet() && !multiple && !wcax.isTouchExplorationEnabled()) {
+        if (DeviceFormFactor.isTablet() && !multiple
+                && !AccessibilityState.hasTouchExplorationEnabled()) {
             mPopupView = new SelectPopupDropdown(context, this::selectMenuItems, anchorView,
                     popupItems, selectedIndices, rightAligned, mWebContents);
         } else {
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsAccessibility.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsAccessibility.java
index 3c0306a..c62125f 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsAccessibility.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsAccessibility.java
@@ -93,11 +93,6 @@
     void setObscuredByAnotherView(boolean isObscured);
 
     /**
-     * Returns true if accessibility is on and touch exploration is enabled.
-     */
-    boolean isTouchExplorationEnabled();
-
-    /**
      * Turns browser accessibility on or off.
      * If |state| is |false|, this turns off both native and injected accessibility.
      * Otherwise, if accessibility script injection is enabled, this will enable the injected
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index ba25e63e..20c23cf 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -456,11 +456,6 @@
 // Enables WebGL rendering into a scanout buffer for overlay support.
 const char kEnableWebGLImageChromium[] = "enable-webgl-image-chromium";
 
-// Controls whether async interface for FileSystemSyncAccessHandle is
-// force-enabled.
-const char kFileSystemSyncAccessHandleAsyncInterfaceEnabled[] =
-    "file-system-sync-access-handle-async-interface-enabled";
-
 // Define an alias root directory which is replaced with the replacement string
 // in file URLs. The format is "/alias=/replacement", which would turn
 // file:///alias/some/path.html into file:///replacement/some/path.html.
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 7d7590d..2878568 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -141,8 +141,6 @@
 CONTENT_EXPORT extern const char kEnableWebRtcSrtpEncryptedHeaders[];
 CONTENT_EXPORT extern const char kEnforceWebRtcIPPermissionCheck[];
 CONTENT_EXPORT extern const char kEnableWebVR[];
-CONTENT_EXPORT extern const char
-    kFileSystemSyncAccessHandleAsyncInterfaceEnabled[];
 CONTENT_EXPORT extern const char kFileUrlPathAlias[];
 CONTENT_EXPORT extern const char kForceDisplayList2dCanvas[];
 CONTENT_EXPORT extern const char kForceEnablePepperVideoDecoderDevAPI[];
diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc
index 3099755..6208ed11 100644
--- a/content/services/auction_worklet/bidder_worklet.cc
+++ b/content/services/auction_worklet/bidder_worklet.cc
@@ -732,9 +732,12 @@
     const scoped_refptr<TrustedSignals::Result>& trusted_bidding_signals_result,
     uint64_t trace_id,
     bool restrict_to_kanon_ads) {
-  // Can't make a bid without any ads.
-  if (!bidder_worklet_non_shared_params->ads)
+  // Can't make a bid without any ads, or if we aren't permitted to spend any
+  // time on it.
+  if (!bidder_worklet_non_shared_params->ads ||
+      (per_buyer_timeout.has_value() && per_buyer_timeout.value().is_zero())) {
     return absl::nullopt;
+  }
 
   base::TimeTicks start = base::TimeTicks::Now();
 
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index 7a68561..d18f366 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -3614,6 +3614,14 @@
       {"https://url.test/ execution of `generateBid` timed out."});
 }
 
+// Test that per-buyer timeout of zero results in no bid produced.
+TEST_F(BidderWorkletTest, TimeoutZero) {
+  per_buyer_timeout_ = base::Seconds(0);
+  RunGenerateBidWithReturnValueExpectingResult(
+      R"({ad: "ad", bid:1, render:"https://response.test/"})",
+      mojom::BidderWorkletBidPtr());
+}
+
 // Test that in the case of multiple setBid() calls, the most recent call takes
 // precedence. Note that this test sets `bid_duration` to
 // `AuctionV8Helper::kScriptTimeout`, to make sure the full timeout time is
diff --git a/content/services/auction_worklet/context_recycler_unittest.cc b/content/services/auction_worklet/context_recycler_unittest.cc
index f180303..07002ba 100644
--- a/content/services/auction_worklet/context_recycler_unittest.cc
+++ b/content/services/auction_worklet/context_recycler_unittest.cc
@@ -825,22 +825,6 @@
     EXPECT_EQ(pa_requests[0], expected_request.Clone());
   }
 
-  // Checks given `pa_requests` has one item, which equals to the request
-  // created from given `expected_contribution`.
-  void ExpectPrivateAggregationForEventRequestsEqual(
-      std::vector<auction_worklet::mojom::PrivateAggregationForEventRequestPtr>
-          pa_requests,
-      auction_worklet::mojom::AggregatableReportForEventContributionPtr
-          expected_contribution) {
-    auction_worklet::mojom::PrivateAggregationForEventRequest expected_request(
-        expected_contribution.Clone(),
-        content::mojom::AggregationServiceMode::kDefault,
-        content::mojom::DebugModeDetails::New());
-
-    ASSERT_EQ(pa_requests.size(), 1u);
-    EXPECT_EQ(pa_requests[0], expected_request.Clone());
-  }
-
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -1462,9 +1446,38 @@
   }
 }
 
+class ContextRecyclerPrivateAggregationExtensionsEnabledTest
+    : public ContextRecyclerTest {
+ public:
+  ContextRecyclerPrivateAggregationExtensionsEnabledTest() {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        content::kPrivateAggregationApi,
+        {{"fledge_extensions_enabled", "true"}});
+  }
+
+  // Checks given `pa_requests` has one item, which equals to the request
+  // created from given `expected_contribution`.
+  void ExpectPrivateAggregationForEventRequestsEqual(
+      std::vector<auction_worklet::mojom::PrivateAggregationForEventRequestPtr>
+          pa_requests,
+      auction_worklet::mojom::AggregatableReportForEventContributionPtr
+          expected_contribution) {
+    auction_worklet::mojom::PrivateAggregationForEventRequest expected_request(
+        expected_contribution.Clone(),
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New());
+
+    ASSERT_EQ(pa_requests.size(), 1u);
+    EXPECT_EQ(pa_requests[0], expected_request.Clone());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
 // Exercise `reportContributionsForEvent()` of PrivateAggregationBindings, and
 // make sure they reset properly.
-TEST_F(ContextRecyclerPrivateAggregationEnabledTest,
+TEST_F(ContextRecyclerPrivateAggregationExtensionsEnabledTest,
        PrivateAggregationForEventBindings) {
   using PrivateAggregationForEventRequests =
       std::vector<auction_worklet::mojom::PrivateAggregationForEventRequestPtr>;
@@ -2323,4 +2336,66 @@
   }
 }
 
+class ContextRecyclerPrivateAggregationOnlyFledgeExtensionsDisabledTest
+    : public ContextRecyclerTest {
+ public:
+  ContextRecyclerPrivateAggregationOnlyFledgeExtensionsDisabledTest() {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        content::kPrivateAggregationApi,
+        {{"fledge_extensions_enabled", "false"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Make sure that `reportContributionForEvent()` isn't available, but the other
+// `privateAggregation` functions are.
+TEST_F(ContextRecyclerPrivateAggregationOnlyFledgeExtensionsDisabledTest,
+       PrivateAggregationForEventBindings) {
+  using PrivateAggregationRequests =
+      std::vector<auction_worklet::mojom::PrivateAggregationRequestPtr>;
+
+  const char kScript[] = R"(
+    function test(args) {
+      // Passing BigInts in directly is complicated so we construct them from
+      // strings.
+      if (typeof args.bucket === "string") {
+        args.bucket = BigInt(args.bucket);
+      }
+      privateAggregation.sendHistogramReport(args);
+      privateAggregation.reportContributionForEvent("example", args);
+    }
+  )";
+
+  v8::Local<v8::UnboundScript> script = Compile(kScript);
+  ASSERT_FALSE(script.IsEmpty());
+
+  ContextRecycler context_recycler(helper_.get());
+  context_recycler.AddPrivateAggregationBindings(
+      /*private_aggregation_permissions_policy_allowed=*/true);
+
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    gin::Dictionary dict = gin::Dictionary::CreateEmpty(helper_->isolate());
+    dict.Set("bucket", std::string("123"));
+    dict.Set("value", 45);
+
+    Run(scope, script, "test", error_msgs,
+        gin::ConvertToV8(helper_->isolate(), dict));
+    EXPECT_THAT(
+        error_msgs,
+        ElementsAre("https://example.org/script.js:9 Uncaught TypeError: "
+                    "privateAggregation.reportContributionForEvent is not a "
+                    "function."));
+
+    PrivateAggregationRequests pa_requests =
+        context_recycler.private_aggregation_bindings()
+            ->TakePrivateAggregationRequests();
+    ASSERT_EQ(pa_requests.size(), 1u);
+  }
+}
+
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/private_aggregation_bindings.cc b/content/services/auction_worklet/private_aggregation_bindings.cc
index 5f45714..82f7eb5 100644
--- a/content/services/auction_worklet/private_aggregation_bindings.cc
+++ b/content/services/auction_worklet/private_aggregation_bindings.cc
@@ -258,14 +258,16 @@
       v8_helper_->CreateStringFromLiteral("sendHistogramReport"),
       send_histogram_report_template);
 
-  v8::Local<v8::FunctionTemplate> report_contribution_for_event_template =
-      v8::FunctionTemplate::New(
-          v8_helper_->isolate(),
-          &PrivateAggregationBindings::ReportContributionForEvent, v8_this);
-  report_contribution_for_event_template->RemovePrototype();
-  private_aggregation_template->Set(
-      v8_helper_->CreateStringFromLiteral("reportContributionForEvent"),
-      report_contribution_for_event_template);
+  if (content::kPrivateAggregationApiFledgeExtensionsEnabled.Get()) {
+    v8::Local<v8::FunctionTemplate> report_contribution_for_event_template =
+        v8::FunctionTemplate::New(
+            v8_helper_->isolate(),
+            &PrivateAggregationBindings::ReportContributionForEvent, v8_this);
+    report_contribution_for_event_template->RemovePrototype();
+    private_aggregation_template->Set(
+        v8_helper_->CreateStringFromLiteral("reportContributionForEvent"),
+        report_contribution_for_event_template);
+  }
 
   v8::Local<v8::FunctionTemplate> enable_debug_mode_template =
       v8::FunctionTemplate::New(v8_helper_->isolate(),
diff --git a/content/test/attribution_simulator_impl.cc b/content/test/attribution_simulator_impl.cc
index 92cb5ebc4..44152c9 100644
--- a/content/test/attribution_simulator_impl.cc
+++ b/content/test/attribution_simulator_impl.cc
@@ -137,17 +137,9 @@
               FormatTime(is_debug_report ? report.attribution_info().time
                                          : report.report_time()));
 
-    base::Value::Dict test_info;
-    if (absl::holds_alternative<AttributionReport::EventLevelData>(
-            report.data())) {
-      test_info.Set("randomized_trigger",
-                    report.attribution_info().source.attribution_logic() ==
-                        StoredSource::AttributionLogic::kFalsely);
-    } else {
-      auto* aggregatable_data =
-          absl::get_if<AttributionReport::AggregatableAttributionData>(
-              &report.data());
-      DCHECK(aggregatable_data);
+    if (const auto* aggregatable_data =
+            absl::get_if<AttributionReport::AggregatableAttributionData>(
+                &report.data())) {
       base::Value::List list;
       for (const auto& contribution : aggregatable_data->contributions) {
         base::Value::Dict dict;
@@ -157,9 +149,10 @@
 
         list.Append(std::move(dict));
       }
+      base::Value::Dict test_info;
       test_info.Set("histograms", std::move(list));
+      value.Set("test_info", std::move(test_info));
     }
-    value.Set("test_info", std::move(test_info));
 
     return value;
   }
diff --git a/content/test/data/attribution_reporting/simulator/README.md b/content/test/data/attribution_reporting/simulator/README.md
index f1a8857..bde54fb 100644
--- a/content/test/data/attribution_reporting/simulator/README.md
+++ b/content/test/data/attribution_reporting/simulator/README.md
@@ -217,11 +217,6 @@
       // URL to which the report would have been sent.
       "report_url": "https://reporting.example/.well-known/attribution-reporting/report-event-attribution",
 
-      "test_info": {
-        // Whether fake reports were generated.
-        "randomized_trigger": false
-      },
-
       // The report itself. See
       // https://github.com/WICG/attribution-reporting-api/blob/main/EVENT.md#attribution-reports
       // for details about its fields.
diff --git a/content/test/data/attribution_reporting/simulator/basic.output.json b/content/test/data/attribution_reporting/simulator/basic.output.json
index 4865e32..9dc5fe1a 100644
--- a/content/test/data/attribution_reporting/simulator/basic.output.json
+++ b/content/test/data/attribution_reporting/simulator/basic.output.json
@@ -10,10 +10,7 @@
          "trigger_data": "7"
       },
       "intended_report_time": "1643411973123",
-      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution"
     }
   ]
 }
diff --git a/content/test/data/attribution_reporting/simulator/basic_aggregatable.output.json b/content/test/data/attribution_reporting/simulator/basic_aggregatable.output.json
index cc974c4..6ff8846 100644
--- a/content/test/data/attribution_reporting/simulator/basic_aggregatable.output.json
+++ b/content/test/data/attribution_reporting/simulator/basic_aggregatable.output.json
@@ -27,10 +27,7 @@
         "trigger_data": "7"
       },
       "intended_report_time": "1643411973123",
-      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution"
     }
   ]
 }
diff --git a/content/test/data/attribution_reporting/simulator/clear_data.output.json b/content/test/data/attribution_reporting/simulator/clear_data.output.json
index d5ed87bec..ec5e92d 100644
--- a/content/test/data/attribution_reporting/simulator/clear_data.output.json
+++ b/content/test/data/attribution_reporting/simulator/clear_data.output.json
@@ -10,10 +10,7 @@
          "trigger_data": "7"
       },
       "intended_report_time": "1643411973000",
-      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution"
     }
   ]
 }
diff --git a/content/test/data/attribution_reporting/simulator/debug.output.json b/content/test/data/attribution_reporting/simulator/debug.output.json
index 847e20d..86162ee 100644
--- a/content/test/data/attribution_reporting/simulator/debug.output.json
+++ b/content/test/data/attribution_reporting/simulator/debug.output.json
@@ -12,10 +12,7 @@
          "trigger_debug_key": "333"
       },
       "intended_report_time": "1643235574000",
-      "report_url": "https://r.test/.well-known/attribution-reporting/debug/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/debug/report-event-attribution"
     }
   ],
   "event_level_reports": [
@@ -31,10 +28,7 @@
          "trigger_debug_key": "333"
       },
       "intended_report_time": "1643411973000",
-      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution"
     },
     {
      "report": {
@@ -47,10 +41,7 @@
          "trigger_data": "6"
       },
       "intended_report_time": "1643411973000",
-      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution"
     }
   ]
 }
diff --git a/content/test/data/attribution_reporting/simulator/no_delay.output.json b/content/test/data/attribution_reporting/simulator/no_delay.output.json
index cb40bad..10620f8 100644
--- a/content/test/data/attribution_reporting/simulator/no_delay.output.json
+++ b/content/test/data/attribution_reporting/simulator/no_delay.output.json
@@ -10,10 +10,7 @@
          "trigger_data": "7"
       },
       "intended_report_time": "1643235574000",
-      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution"
     }
   ]
 }
diff --git a/content/test/data/attribution_reporting/simulator/verbose_debug.output.json b/content/test/data/attribution_reporting/simulator/verbose_debug.output.json
index 2b745bc..989bb9d 100644
--- a/content/test/data/attribution_reporting/simulator/verbose_debug.output.json
+++ b/content/test/data/attribution_reporting/simulator/verbose_debug.output.json
@@ -29,10 +29,7 @@
          "trigger_data": "1"
       },
       "intended_report_time": "1643411972000",
-      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution"
     },
     {
      "report": {
@@ -44,10 +41,7 @@
          "trigger_data": "2"
       },
       "intended_report_time": "1643411972000",
-      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution"
     },
     {
      "report": {
@@ -59,10 +53,7 @@
          "trigger_data": "3"
       },
       "intended_report_time": "1643411972000",
-      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution",
-      "test_info": {
-         "randomized_trigger": false
-      }
+      "report_url": "https://r.test/.well-known/attribution-reporting/report-event-attribution"
     }
   ]
 }
diff --git a/device/fido/authenticator_make_credential_response.cc b/device/fido/authenticator_make_credential_response.cc
index 0559262..cb29ad4 100644
--- a/device/fido/authenticator_make_credential_response.cc
+++ b/device/fido/authenticator_make_credential_response.cc
@@ -12,7 +12,9 @@
 #include "device/fido/attested_credential_data.h"
 #include "device/fido/authenticator_data.h"
 #include "device/fido/device_public_key_extension.h"
+#include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
+#include "device/fido/large_blob.h"
 #include "device/fido/p256_public_key.h"
 #include "device/fido/public_key.h"
 
@@ -119,8 +121,10 @@
   if (response.enterprise_attestation_returned) {
     map.emplace(4, true);
   }
-  if (response.large_blob_key) {
-    map.emplace(5, cbor::Value(*response.large_blob_key));
+  if (response.has_associated_large_blob_key) {
+    // Chrome ignores the value of the large blob key on make credential
+    // requests.
+    map.emplace(5, cbor::Value(std::array<uint8_t, kLargeBlobKeyLength>()));
   }
   if (response.device_public_key_signature.has_value()) {
     cbor::Value::MapValue unsigned_extension_outputs;
diff --git a/device/fido/authenticator_make_credential_response.h b/device/fido/authenticator_make_credential_response.h
index 9761bfd..31ed4d5 100644
--- a/device/fido/authenticator_make_credential_response.h
+++ b/device/fido/authenticator_make_credential_response.h
@@ -88,10 +88,10 @@
   // nullopt for cases where we cannot determine the transport (Windows).
   absl::optional<FidoTransportProtocol> transport_used;
 
-  // The large blob key associated to the credential. This value is only
-  // returned if the credential is created with the largeBlobKey extension on a
-  // capable authenticator.
-  absl::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key;
+  // Whether the credential that was created has an associated large blob key or
+  // not. This can only be true if the credential is created with the
+  // largeBlobKey extension on a capable authenticator.
+  bool has_associated_large_blob_key = false;
 };
 
 // Through cbor::Writer, produces a CTAP style CBOR-encoded byte array
diff --git a/device/fido/cable/fido_cable_discovery.cc b/device/fido/cable/fido_cable_discovery.cc
index 4ff7859..31cc3f8b 100644
--- a/device/fido/cable/fido_cable_discovery.cc
+++ b/device/fido/cable/fido_cable_discovery.cc
@@ -766,12 +766,9 @@
   return ret;
 }
 
-bool FidoCableDiscovery::MaybeStop() {
-  if (!FidoDeviceDiscovery::MaybeStop()) {
-    NOTREACHED();
-  }
+void FidoCableDiscovery::Stop() {
+  FidoDeviceDiscovery::Stop();
   StopAdvertisements(base::DoNothing());
-  return true;
 }
 
 }  // namespace device
diff --git a/device/fido/cable/fido_cable_discovery.h b/device/fido/cable/fido_cable_discovery.h
index 9dd844f..1461ee8f 100644
--- a/device/fido/cable/fido_cable_discovery.h
+++ b/device/fido/cable/fido_cable_discovery.h
@@ -42,7 +42,7 @@
   ~FidoCableDiscovery() override;
 
   // FidoDeviceDiscovery:
-  bool MaybeStop() override;
+  void Stop() override;
 
   // GetV2AdvertStream returns a stream of caBLEv2 BLE adverts. Only a single
   // stream is supported.
diff --git a/device/fido/cable/fido_cable_discovery_unittest.cc b/device/fido/cable/fido_cable_discovery_unittest.cc
index 0f35b30..b798575f 100644
--- a/device/fido/cable/fido_cable_discovery_unittest.cc
+++ b/device/fido/cable/fido_cable_discovery_unittest.cc
@@ -589,7 +589,7 @@
   task_environment_.FastForwardUntilNoTasksRemain();
   EXPECT_EQ(1u, cable_discovery->AdvertisementsForTesting().size());
 
-  EXPECT_TRUE(cable_discovery->MaybeStop());
+  cable_discovery->Stop();
   task_environment_.FastForwardUntilNoTasksRemain();
   EXPECT_EQ(0u, cable_discovery->AdvertisementsForTesting().size());
 }
@@ -608,7 +608,7 @@
   task_environment_.FastForwardUntilNoTasksRemain();
 
   EXPECT_EQ(0u, cable_discovery->AdvertisementsForTesting().size());
-  EXPECT_TRUE(cable_discovery->MaybeStop());
+  cable_discovery->Stop();
 }
 
 // Tests that cable discovery resumes after Bluetooth adapter is powered on.
diff --git a/device/fido/device_response_converter.cc b/device/fido/device_response_converter.cc
index c9a5b83..cf5ca92 100644
--- a/device/fido/device_response_converter.cc
+++ b/device/fido/device_response_converter.cc
@@ -112,8 +112,7 @@
         it->second.GetBytestring().size() != kLargeBlobKeyLength) {
       return absl::nullopt;
     }
-    response.large_blob_key = fido_parsing_utils::Materialize(
-        base::make_span<kLargeBlobKeyLength>(it->second.GetBytestring()));
+    response.has_associated_large_blob_key = true;
   }
 
   it = decoded_map.find(CBOR(0x06));
diff --git a/device/fido/fido_authenticator.cc b/device/fido/fido_authenticator.cc
index 1a06b8d..8037313 100644
--- a/device/fido/fido_authenticator.cc
+++ b/device/fido/fido_authenticator.cc
@@ -237,4 +237,8 @@
   return false;
 }
 
+bool FidoAuthenticator::SupportsLargeBlobs() const {
+  return false;
+}
+
 }  // namespace device
diff --git a/device/fido/fido_authenticator.h b/device/fido/fido_authenticator.h
index 1281b016..6dbab6c6 100644
--- a/device/fido/fido_authenticator.h
+++ b/device/fido/fido_authenticator.h
@@ -307,6 +307,7 @@
   virtual bool SupportsEnterpriseAttestation() const;
   virtual bool SupportsCredBlobOfSize(size_t num_bytes) const;
   virtual bool SupportsDevicePublicKey() const;
+  virtual bool SupportsLargeBlobs() const;
   virtual const absl::optional<AuthenticatorSupportedOptions>& Options()
       const = 0;
   virtual absl::optional<FidoTransportProtocol> AuthenticatorTransport()
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc
index 5884ad3d..3d826fb 100644
--- a/device/fido/fido_device_authenticator.cc
+++ b/device/fido/fido_device_authenticator.cc
@@ -1190,6 +1190,10 @@
                         kExtensionDevicePublicKey);
 }
 
+bool FidoDeviceAuthenticator::SupportsLargeBlobs() const {
+  return options_ && options_->supports_large_blobs;
+}
+
 const absl::optional<AuthenticatorSupportedOptions>&
 FidoDeviceAuthenticator::Options() const {
   return options_;
diff --git a/device/fido/fido_device_authenticator.h b/device/fido/fido_device_authenticator.h
index f4dda2ea..5f99c8c 100644
--- a/device/fido/fido_device_authenticator.h
+++ b/device/fido/fido_device_authenticator.h
@@ -137,6 +137,7 @@
   bool SupportsEnterpriseAttestation() const override;
   bool SupportsCredBlobOfSize(size_t num_bytes) const override;
   bool SupportsDevicePublicKey() const override;
+  bool SupportsLargeBlobs() const override;
   const absl::optional<AuthenticatorSupportedOptions>& Options() const override;
   absl::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
   base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
diff --git a/device/fido/fido_device_discovery.cc b/device/fido/fido_device_discovery.cc
index 47fc74e7..d3d30dc 100644
--- a/device/fido/fido_device_discovery.cc
+++ b/device/fido/fido_device_discovery.cc
@@ -115,9 +115,8 @@
   return true;
 }
 
-bool FidoDeviceDiscovery::MaybeStop() {
+void FidoDeviceDiscovery::Stop() {
   state_ = State::kStopped;
-  return true;
 }
 
 }  // namespace device
diff --git a/device/fido/fido_device_discovery.h b/device/fido/fido_device_discovery.h
index c00a196..750e27c 100644
--- a/device/fido/fido_device_discovery.h
+++ b/device/fido/fido_device_discovery.h
@@ -81,7 +81,7 @@
 
   // FidoDiscoveryBase:
   void Start() override;
-  bool MaybeStop() override;
+  void Stop() override;
 
  protected:
   explicit FidoDeviceDiscovery(FidoTransportProtocol transport);
diff --git a/device/fido/fido_discovery_base.cc b/device/fido/fido_discovery_base.cc
index 8f34857..f65c189 100644
--- a/device/fido/fido_discovery_base.cc
+++ b/device/fido/fido_discovery_base.cc
@@ -10,8 +10,6 @@
     : transport_(transport) {}
 FidoDiscoveryBase::~FidoDiscoveryBase() = default;
 
-bool FidoDiscoveryBase::MaybeStop() {
-  return false;
-}
+void FidoDiscoveryBase::Stop() {}
 
 }  // namespace device
diff --git a/device/fido/fido_discovery_base.h b/device/fido/fido_discovery_base.h
index d91d73b..66544cf8 100644
--- a/device/fido/fido_discovery_base.h
+++ b/device/fido/fido_discovery_base.h
@@ -55,8 +55,8 @@
   // this method.
   virtual void Start() = 0;
 
-  // Attempts to stop discovery and returns whether stopping was successful.
-  virtual bool MaybeStop();
+  // Stop discovering new devices.
+  virtual void Stop();
 
   Observer* observer() const { return observer_; }
   void set_observer(Observer* observer) {
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc
index a1083b5..ca0e88ea 100644
--- a/device/fido/fido_request_handler_base.cc
+++ b/device/fido/fido_request_handler_base.cc
@@ -455,7 +455,7 @@
 
 void FidoRequestHandlerBase::StopDiscoveries() {
   for (const auto& discovery : discoveries_) {
-    discovery->MaybeStop();
+    discovery->Stop();
   }
 }
 
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index bdd0be3..ac713c86 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -127,6 +127,14 @@
     return MakeCredentialStatus::kAuthenticatorMissingResidentKeys;
   }
 
+  // The largeBlobs extension only works for resident credentials on CTAP 2.1
+  // authenticators or on some Windows versions.
+  if (options.large_blob_support == LargeBlobSupport::kRequired &&
+      (!authenticator->SupportsLargeBlobs() ||
+       !request.resident_key_required)) {
+    return MakeCredentialStatus::kAuthenticatorMissingLargeBlob;
+  }
+
   const absl::optional<AuthenticatorSupportedOptions>& auth_options =
       authenticator->Options();
   if (!auth_options) {
@@ -163,13 +171,6 @@
     return MakeCredentialStatus::kAuthenticatorMissingUserVerification;
   }
 
-  // The largeBlobs extension only works for resident credentials on CTAP 2.1
-  // authenticators.
-  if (options.large_blob_support == LargeBlobSupport::kRequired &&
-      (!auth_options->supports_large_blobs || !request.resident_key_required)) {
-    return MakeCredentialStatus::kAuthenticatorMissingLargeBlob;
-  }
-
   absl::optional<base::span<const int32_t>> supported_algorithms(
       authenticator->GetAlgorithms());
   if (supported_algorithms) {
@@ -375,7 +376,7 @@
     return false;
   }
 
-  if (request.large_blob_key && !response.large_blob_key) {
+  if (request.large_blob_key && !response.has_associated_large_blob_key) {
     FIDO_LOG(ERROR) << "Large blob key requested but not returned";
     return false;
   }
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index d9aa768..c7a1602 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -185,9 +185,8 @@
                         std::move(attestation_statement)));
   make_credential_response.enterprise_attestation_returned =
       enterprise_attestation_requested;
-  if (large_blob_key) {
-    make_credential_response.large_blob_key = *large_blob_key;
-  }
+  make_credential_response.has_associated_large_blob_key =
+      large_blob_key.has_value();
   make_credential_response.device_public_key_signature =
       std::move(dpk_signature);
   return AsCTAPStyleCBORBytes(make_credential_response);
diff --git a/device/fido/virtual_fido_device_discovery.cc b/device/fido/virtual_fido_device_discovery.cc
index 77819eaa..3c5ef4b 100644
--- a/device/fido/virtual_fido_device_discovery.cc
+++ b/device/fido/virtual_fido_device_discovery.cc
@@ -68,9 +68,9 @@
   RemoveDevice(id_);
 }
 
-bool VirtualFidoDeviceDiscovery::MaybeStop() {
+void VirtualFidoDeviceDiscovery::Stop() {
   trace_->discoveries[trace_index_].is_stopped = true;
-  return FidoDeviceDiscovery::MaybeStop();
+  FidoDeviceDiscovery::Stop();
 }
 
 }  // namespace device::test
diff --git a/device/fido/virtual_fido_device_discovery.h b/device/fido/virtual_fido_device_discovery.h
index d69c3937..8765c6d 100644
--- a/device/fido/virtual_fido_device_discovery.h
+++ b/device/fido/virtual_fido_device_discovery.h
@@ -51,7 +51,7 @@
 
  protected:
   void StartInternal() override;
-  bool MaybeStop() override;
+  void Stop() override;
 
  private:
   void Disconnect(bool _);
diff --git a/device/fido/win/authenticator.cc b/device/fido/win/authenticator.cc
index 27354cbc..77bcd0df 100644
--- a/device/fido/win/authenticator.cc
+++ b/device/fido/win/authenticator.cc
@@ -36,7 +36,8 @@
 namespace {
 
 struct PlatformCredentialListDeleter {
-  PlatformCredentialListDeleter(WinWebAuthnApi* win_api) : win_api_(win_api) {}
+  explicit PlatformCredentialListDeleter(WinWebAuthnApi* win_api)
+      : win_api_(win_api) {}
   inline void operator()(PWEBAUTHN_CREDENTIAL_DETAILS_LIST ptr) const {
     win_api_->FreePlatformCredentialList(ptr);
   }
@@ -342,6 +343,10 @@
   return win_api_->Version() >= WEBAUTHN_API_VERSION_3;
 }
 
+bool WinWebAuthnApiAuthenticator::SupportsLargeBlobs() const {
+  return win_api_->SupportsLargeBlobs();
+}
+
 const absl::optional<AuthenticatorSupportedOptions>&
 WinWebAuthnApiAuthenticator::Options() const {
   // The request can potentially be fulfilled by any device that Windows
diff --git a/device/fido/win/authenticator.h b/device/fido/win/authenticator.h
index 067ab9c..eabf049 100644
--- a/device/fido/win/authenticator.h
+++ b/device/fido/win/authenticator.h
@@ -97,6 +97,7 @@
   bool SupportsHMACSecretExtension() const override;
   bool SupportsEnterpriseAttestation() const override;
   bool SupportsCredBlobOfSize(size_t num_bytes) const override;
+  bool SupportsLargeBlobs() const override;
   const absl::optional<AuthenticatorSupportedOptions>& Options() const override;
   absl::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
   base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;
diff --git a/device/fido/win/authenticator_unittest.cc b/device/fido/win/authenticator_unittest.cc
index 51518ffa..8a87a7aa 100644
--- a/device/fido/win/authenticator_unittest.cc
+++ b/device/fido/win/authenticator_unittest.cc
@@ -10,7 +10,13 @@
 
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
+#include "device/fido/authenticator_make_credential_response.h"
+#include "device/fido/authenticator_selection_criteria.h"
 #include "device/fido/ctap_get_assertion_request.h"
+#include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_test_data.h"
+#include "device/fido/fido_types.h"
 #include "device/fido/public_key_credential_rp_entity.h"
 #include "device/fido/public_key_credential_user_entity.h"
 #include "device/fido/test_callback_receiver.h"
@@ -20,6 +26,10 @@
 namespace device {
 namespace {
 
+using MakeCredentialCallbackReceiver = test::StatusAndValueCallbackReceiver<
+    CtapDeviceResponseCode,
+    absl::optional<AuthenticatorMakeCredentialResponse>>;
+
 using GetCredentialCallbackReceiver =
     test::TestCallbackReceiver<std::vector<DiscoverableCredentialMetadata>,
                                bool>;
@@ -200,5 +210,58 @@
   }
 }
 
+TEST_F(WinAuthenticatorTest, MakeCredentialLargeBlob) {
+  enum Availability : bool {
+    kNotAvailable = false,
+    kAvailable = true,
+  };
+
+  enum Result : bool {
+    kDoesNotHaveLargeBlob = false,
+    kHasLargeBlob = true,
+  };
+
+  struct LargeBlobTestCase {
+    LargeBlobSupport requirement;
+    Availability availability;
+    Result result;
+  };
+
+  std::array<LargeBlobTestCase, 5> test_cases = {{
+      {LargeBlobSupport::kNotRequested, kAvailable, kDoesNotHaveLargeBlob},
+      {LargeBlobSupport::kNotRequested, kNotAvailable, kDoesNotHaveLargeBlob},
+      {LargeBlobSupport::kPreferred, kAvailable, kHasLargeBlob},
+      {LargeBlobSupport::kPreferred, kNotAvailable, kDoesNotHaveLargeBlob},
+      {LargeBlobSupport::kRequired, kAvailable, kHasLargeBlob},
+      // Calling the Windows API with large blob = required is not allowed if
+      // it's not supported by the API version.
+  }};
+
+  for (const LargeBlobTestCase& test_case : test_cases) {
+    SCOPED_TRACE(testing::Message()
+                 << "requirement=" << static_cast<bool>(test_case.requirement)
+                 << ", availability="
+                 << static_cast<bool>(test_case.availability));
+
+    fake_webauthn_api_->set_supports_large_blobs(test_case.availability);
+    EXPECT_EQ(authenticator_->SupportsLargeBlobs(), test_case.availability);
+    PublicKeyCredentialRpEntity rp("adrestian-empire.com");
+    PublicKeyCredentialUserEntity user(std::vector<uint8_t>{1, 2, 3, 4},
+                                       "el@adrestian-empire.com", "Edelgard");
+    CtapMakeCredentialRequest request(
+        test_data::kClientDataJson, rp, user,
+        PublicKeyCredentialParams({{CredentialType::kPublicKey, -257}}));
+    MakeCredentialOptions options;
+    options.large_blob_support = test_case.requirement;
+    MakeCredentialCallbackReceiver callback;
+    authenticator_->MakeCredential(std::move(request), options,
+                                   callback.callback());
+    callback.WaitForCallback();
+    ASSERT_EQ(callback.status(), CtapDeviceResponseCode::kSuccess);
+    EXPECT_EQ(callback.value()->has_associated_large_blob_key,
+              test_case.result);
+  }
+}
+
 }  // namespace
 }  // namespace device
diff --git a/device/fido/win/fake_webauthn_api.cc b/device/fido/win/fake_webauthn_api.cc
index d4087ccb..efaad51a 100644
--- a/device/fido/win/fake_webauthn_api.cc
+++ b/device/fido/win/fake_webauthn_api.cc
@@ -4,6 +4,8 @@
 
 #include "device/fido/win/fake_webauthn_api.h"
 
+#include <stdint.h>
+#include <winerror.h>
 #include <memory>
 #include <string>
 
@@ -14,15 +16,59 @@
 #include "base/strings/string_util_win.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/cbor/values.h"
+#include "components/cbor/writer.h"
 #include "crypto/sha2.h"
+#include "device/fido/attestation_statement.h"
 #include "device/fido/attested_credential_data.h"
 #include "device/fido/authenticator_data.h"
+#include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_test_data.h"
+#include "device/fido/public_key.h"
+#include "device/fido/public_key_credential_rp_entity.h"
+#include "device/fido/public_key_credential_user_entity.h"
+#include "device/fido/virtual_fido_device.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/microsoft_webauthn/webauthn.h"
 
 namespace device {
 
+namespace {
+
+constexpr std::array<uint8_t, kAaguidLength> kTestWindowsAaguid = {
+    {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+     0x0d, 0x0e, 0x0f, 0x10}};
+
+std::unique_ptr<VirtualFidoDevice::PrivateKey> MakePrivateKey(
+    PCWEBAUTHN_COSE_CREDENTIAL_PARAMETERS cose_credential_parameters,
+    bool is_platform_credential) {
+  for (size_t i = 0; i < cose_credential_parameters->cCredentialParameters;
+       ++i) {
+    WEBAUTHN_COSE_CREDENTIAL_PARAMETER parameter =
+        cose_credential_parameters->pCredentialParameters[i];
+    if (is_platform_credential) {
+      // Windows only supports RS256 for platform credentials.
+      if (parameter.lAlg ==
+          static_cast<LONG>(CoseAlgorithmIdentifier::kRs256)) {
+        return VirtualFidoDevice::PrivateKey::FreshP256Key();
+      }
+      continue;
+    }
+
+    switch (parameter.lAlg) {
+      case static_cast<LONG>(CoseAlgorithmIdentifier::kEs256):
+        return VirtualFidoDevice::PrivateKey::FreshP256Key();
+      case static_cast<LONG>(CoseAlgorithmIdentifier::kRs256):
+        return VirtualFidoDevice::PrivateKey::FreshRSAKey();
+      case static_cast<LONG>(CoseAlgorithmIdentifier::kEdDSA):
+        return VirtualFidoDevice::PrivateKey::FreshEd25519Key();
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace
+
 struct FakeWinWebAuthnApi::CredentialInfoList {
   WEBAUTHN_CREDENTIAL_DETAILS_LIST credential_details_list;
   std::vector<WEBAUTHN_CREDENTIAL_DETAILS*> win_credentials;
@@ -33,6 +79,7 @@
   // This structure contains pointers to itself and thus
   // must not be moved in memory.
   CredentialInfo() = default;
+  CredentialInfo(const CredentialInfo&) = delete;
   CredentialInfo(CredentialInfo&&) = delete;
   CredentialInfo& operator=(CredentialInfo&&) = delete;
   CredentialInfo& operator=(const CredentialInfo&) = delete;
@@ -50,10 +97,28 @@
   std::u16string user_display_name;
 };
 
+struct FakeWinWebAuthnApi::WebAuthnAttestation {
+  WebAuthnAttestation() = default;
+  // This structure contains pointers to itself and thus
+  // must not be moved in memory.
+  WebAuthnAttestation(const WebAuthnAttestation&) = delete;
+  WebAuthnAttestation(WebAuthnAttestation&&) = delete;
+  WebAuthnAttestation& operator=(WebAuthnAttestation&&) = delete;
+  WebAuthnAttestation& operator=(const WebAuthnAttestation&&) = delete;
+
+  std::vector<uint8_t> authenticator_data;
+  std::vector<uint8_t> attestation;
+  std::vector<uint8_t> attestation_object;
+  std::vector<uint8_t> credential_id;
+
+  WEBAUTHN_CREDENTIAL_ATTESTATION win_attestation;
+};
+
 struct FakeWinWebAuthnApi::WebAuthnAssertionEx {
   // This structure contains pointers to itself and thus
   // must not be moved in memory.
   WebAuthnAssertionEx() = default;
+  WebAuthnAssertionEx(const WebAuthnAssertionEx&) = delete;
   WebAuthnAssertionEx(WebAuthnAssertionEx&&) = delete;
   WebAuthnAssertionEx& operator=(WebAuthnAssertionEx&&) = delete;
   WebAuthnAssertionEx& operator=(const WebAuthnAssertionEx&) = delete;
@@ -110,6 +175,10 @@
   return supports_silent_discovery_;
 }
 
+bool FakeWinWebAuthnApi::SupportsLargeBlobs() const {
+  return supports_large_blobs_;
+}
+
 HRESULT FakeWinWebAuthnApi::IsUserVerifyingPlatformAuthenticatorAvailable(
     BOOL* result) {
   DCHECK(is_available_);
@@ -125,14 +194,106 @@
     PCWEBAUTHN_CLIENT_DATA client_data,
     PCWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS options,
     PWEBAUTHN_CREDENTIAL_ATTESTATION* credential_attestation_ptr) {
-  // TODO(martinkr): Implement to create a credential in |registrations_|.
   DCHECK(is_available_);
   if (result_override_ != S_OK) {
     return result_override_;
   }
 
-  returned_attestations_.push_back(FakeAttestation());
-  *credential_attestation_ptr = &returned_attestations_.back();
+  // Validate the input parameters.
+  DCHECK_GT(client_data->cbClientDataJSON, 0u);
+  DCHECK(client_data->pbClientDataJSON);
+  DCHECK(rp->pwszId);
+  DCHECK_GT(wcslen(rp->pwszId), 0u);
+  DCHECK(rp->pwszName);
+  DCHECK_GT(user->cbId, 0u);
+  DCHECK(user->pbId);
+  DCHECK(user->pwszName);
+  DCHECK_GT(wcslen(user->pwszName), 0u);
+  DCHECK(user->pwszDisplayName);
+  DCHECK(options->pExcludeCredentialList);
+
+  std::unique_ptr<VirtualFidoDevice::PrivateKey> private_key =
+      MakePrivateKey(cose_credential_parameters,
+                     options->dwAuthenticatorAttachment ==
+                         WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM);
+  if (!private_key) {
+    return NTE_NOT_SUPPORTED;
+  }
+
+  for (size_t i = 0; i < options->pExcludeCredentialList->cCredentials; ++i) {
+    PWEBAUTHN_CREDENTIAL_EX exclude_credential =
+        options->pExcludeCredentialList->ppCredentials[i];
+    std::vector<uint8_t> credential_id = fido_parsing_utils::Materialize(
+        base::make_span(exclude_credential->pbId, exclude_credential->cbId));
+    if (registrations_.contains(credential_id)) {
+      return NTE_EXISTS;
+    }
+  }
+
+  std::unique_ptr<PublicKey> public_key = private_key->GetPublicKey();
+  std::vector<uint8_t> credential_id = fido_parsing_utils::Materialize(
+      crypto::SHA256Hash(public_key->cose_key_bytes));
+  std::string rp_id = base::WideToUTF8(rp->pwszId);
+  std::array<uint8_t, crypto::kSHA256Length> rp_id_hash =
+      fido_parsing_utils::CreateSHA256Hash(rp_id);
+  std::vector<uint8_t> user_id =
+      fido_parsing_utils::Materialize(base::make_span(user->pbId, user->cbId));
+
+  RegistrationData registration(std::move(private_key), std::move(rp_id_hash),
+                                /*counter=*/1);
+  bool resident_key =
+      options->bRequireResidentKey || options->bPreferResidentKey;
+  if (resident_key) {
+    registration.rp =
+        PublicKeyCredentialRpEntity(rp_id, base::WideToUTF8(rp->pwszName));
+    registration.user =
+        PublicKeyCredentialUserEntity(user_id, base::WideToUTF8(user->pwszName),
+                                      base::WideToUTF8(user->pwszDisplayName));
+  }
+
+  std::array<uint8_t, 2> credential_id_length = {0, crypto::kSHA256Length};
+  AttestedCredentialData credential_data(
+      kTestWindowsAaguid, credential_id_length, credential_id,
+      registration.private_key->GetPublicKey());
+  auto attestation = std::make_unique<WebAuthnAttestation>();
+  attestation->authenticator_data =
+      AuthenticatorData(registration.application_parameter,
+                        /*user_present=*/true,
+                        options->dwUserVerificationRequirement !=
+                            WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED,
+                        /*backup_eligible=*/false, registration.counter,
+                        std::move(credential_data),
+                        /*extensions=*/absl::nullopt)
+          .SerializeToByteArray();
+  attestation->credential_id = credential_id;
+  // For now, only support none attestation.
+  attestation->attestation =
+      *cbor::Writer::Write(NoneAttestationStatement().AsCBOR());
+
+  attestation->win_attestation.dwVersion =
+      WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4;
+  attestation->win_attestation.pwszFormatType = WEBAUTHN_ATTESTATION_TYPE_NONE;
+  attestation->win_attestation.cbAuthenticatorData =
+      attestation->authenticator_data.size();
+  attestation->win_attestation.pbAuthenticatorData =
+      attestation->authenticator_data.data();
+  attestation->win_attestation.cbAttestation = attestation->attestation.size();
+  attestation->win_attestation.pbAttestation = attestation->attestation.data();
+  attestation->win_attestation.bResidentKey = resident_key;
+  attestation->win_attestation.bLargeBlobSupported =
+      options->dwLargeBlobSupport != WEBAUTHN_LARGE_BLOB_SUPPORT_NONE &&
+      SupportsLargeBlobs();
+  attestation->win_attestation.dwUsedTransport =
+      options->dwAuthenticatorAttachment ==
+              WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM
+          ? WEBAUTHN_CTAP_TRANSPORT_INTERNAL
+          : WEBAUTHN_CTAP_TRANSPORT_USB;
+
+  *credential_attestation_ptr = &attestation->win_attestation;
+  returned_attestations_.push_back(std::move(attestation));
+  DCHECK(
+      registrations_.insert({std::move(credential_id), std::move(registration)})
+          .second);
   return S_OK;
 }
 
@@ -352,7 +513,7 @@
     PWEBAUTHN_CREDENTIAL_ATTESTATION credential_attestation) {
   for (auto it = returned_attestations_.begin();
        it != returned_attestations_.end(); ++it) {
-    if (credential_attestation != &*it) {
+    if (credential_attestation != &(*it)->win_attestation) {
       continue;
     }
     returned_attestations_.erase(it);
diff --git a/device/fido/win/fake_webauthn_api.h b/device/fido/win/fake_webauthn_api.h
index 7fd3bb31..aeaee7b 100644
--- a/device/fido/win/fake_webauthn_api.h
+++ b/device/fido/win/fake_webauthn_api.h
@@ -64,11 +64,16 @@
     supports_silent_discovery_ = supports_silent_discovery;
   }
 
+  void set_supports_large_blobs(bool supports_large_blobs) {
+    supports_large_blobs_ = supports_large_blobs;
+  }
+
   void set_version(int version) { version_ = version; }
 
   // WinWebAuthnApi:
   bool IsAvailable() const override;
   bool SupportsSilentDiscovery() const override;
+  bool SupportsLargeBlobs() const override;
   HRESULT IsUserVerifyingPlatformAuthenticatorAvailable(
       BOOL* available) override;
   HRESULT AuthenticatorMakeCredential(
@@ -101,6 +106,7 @@
  private:
   struct CredentialInfo;
   struct CredentialInfoList;
+  struct WebAuthnAttestation;
   struct WebAuthnAssertionEx;
 
   static WEBAUTHN_CREDENTIAL_ATTESTATION FakeAttestation();
@@ -108,11 +114,12 @@
   bool is_available_ = true;
   bool is_uvpaa_ = false;
   bool supports_silent_discovery_ = false;
+  bool supports_large_blobs_ = false;
   int version_ = WEBAUTHN_API_VERSION_2;
   HRESULT result_override_ = S_OK;
 
   // Owns the attestations returned by AuthenticatorMakeCredential().
-  std::vector<WEBAUTHN_CREDENTIAL_ATTESTATION> returned_attestations_;
+  std::vector<std::unique_ptr<WebAuthnAttestation>> returned_attestations_;
 
   // Owns assertions returned by AuthenticatorGetAssertion().
   std::vector<std::unique_ptr<WebAuthnAssertionEx>> returned_assertions_;
diff --git a/device/fido/win/logging.cc b/device/fido/win/logging.cc
index 55bd9e5..8a112a0d 100644
--- a/device/fido/win/logging.cc
+++ b/device/fido/win/logging.cc
@@ -159,6 +159,10 @@
   } else {
     out << ", (null)";
   }
+  if (in.dwVersion < WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_4) {
+    return out << "}";
+  }
+  out << kSep << in.dwLargeBlobSupport;
   return out << "}";
 }
 
@@ -178,6 +182,10 @@
     return out << "}";
   }
   out << kSep << in.dwUsedTransport;
+  if (in.dwVersion < WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4) {
+    return out << "}";
+  }
+  out << kSep << in.bLargeBlobSupported;
   return out << "}";
 }
 
diff --git a/device/fido/win/type_conversions.cc b/device/fido/win/type_conversions.cc
index 6563b67a..021ca4d 100644
--- a/device/fido/win/type_conversions.cc
+++ b/device/fido/win/type_conversions.cc
@@ -20,10 +20,12 @@
 #include "device/fido/authenticator_make_credential_response.h"
 #include "device/fido/discoverable_credential_metadata.h"
 #include "device/fido/fido_transport_protocol.h"
+#include "device/fido/fido_types.h"
 #include "device/fido/get_assertion_request_handler.h"
 #include "device/fido/make_credential_request_handler.h"
 #include "device/fido/opaque_attestation_statement.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/microsoft_webauthn/webauthn.h"
 
 namespace device {
 
@@ -85,6 +87,8 @@
       WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4) {
     ret.enterprise_attestation_returned = credential_attestation.bEpAtt;
     ret.is_resident_key = credential_attestation.bResidentKey;
+    ret.has_associated_large_blob_key =
+        credential_attestation.bLargeBlobSupported;
   }
 
   return ret;
@@ -207,6 +211,17 @@
   return result;
 }
 
+uint32_t ToWinLargeBlobSupport(LargeBlobSupport large_blob_support) {
+  switch (large_blob_support) {
+    case LargeBlobSupport::kNotRequested:
+      return WEBAUTHN_LARGE_BLOB_SUPPORT_NONE;
+    case LargeBlobSupport::kPreferred:
+      return WEBAUTHN_LARGE_BLOB_SUPPORT_PREFERRED;
+    case LargeBlobSupport::kRequired:
+      return WEBAUTHN_LARGE_BLOB_SUPPORT_REQUIRED;
+  }
+}
+
 CtapDeviceResponseCode WinErrorNameToCtapDeviceResponseCode(
     const std::u16string& error_name) {
   // See WebAuthNGetErrorName in <webauthn.h> for these string literals.
diff --git a/device/fido/win/type_conversions.h b/device/fido/win/type_conversions.h
index dd9cd79..e0f497c 100644
--- a/device/fido/win/type_conversions.h
+++ b/device/fido/win/type_conversions.h
@@ -13,6 +13,7 @@
 #include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/authenticator_make_credential_response.h"
 #include "device/fido/fido_constants.h"
+#include "device/fido/fido_types.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/microsoft_webauthn/webauthn.h"
 
@@ -49,6 +50,9 @@
 std::vector<WEBAUTHN_CREDENTIAL_EX> ToWinCredentialExVector(
     const std::vector<PublicKeyCredentialDescriptor>* credentials);
 
+COMPONENT_EXPORT(DEVICE_FIDO)
+uint32_t ToWinLargeBlobSupport(LargeBlobSupport large_blob_support);
+
 // WinErrorNameToCtapDeviceResponseCode maps a string returned by
 // WebAuthNGetErrorName() to a CtapDeviceResponseCode.
 //
diff --git a/device/fido/win/webauthn_api.cc b/device/fido/win/webauthn_api.cc
index 72ebeb2b..53a6f35 100644
--- a/device/fido/win/webauthn_api.cc
+++ b/device/fido/win/webauthn_api.cc
@@ -20,9 +20,11 @@
 #include "base/threading/scoped_thread_priority.h"
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/features.h"
+#include "device/fido/fido_types.h"
 #include "device/fido/win/logging.h"
 #include "device/fido/win/type_conversions.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/microsoft_webauthn/webauthn.h"
 
 namespace device {
 
@@ -41,7 +43,7 @@
 
 class WinWebAuthnApiImpl : public WinWebAuthnApi {
  public:
-  WinWebAuthnApiImpl() : WinWebAuthnApi(), is_bound_(false) {
+  WinWebAuthnApiImpl() {
     if (!base::FeatureList::IsEnabled(device::kWebAuthUseNativeWinApi)) {
       FIDO_LOG(DEBUG) << "Windows WebAuthn API deactivated via feature flag";
       return;
@@ -51,7 +53,7 @@
       // (http://crbug/973868).
       SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
       webauthn_dll_ =
-          LoadLibraryExA("webauthn.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+          LoadLibraryExA("webauthn.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
     }
     if (!webauthn_dll_) {
       FIDO_LOG(ERROR) << "Windows WebAuthn API failed to load";
@@ -107,7 +109,7 @@
     FIDO_LOG(DEBUG) << "webauthn.dll version " << api_version_;
   }
 
-  ~WinWebAuthnApiImpl() override {}
+  ~WinWebAuthnApiImpl() override = default;
 
   // WinWebAuthnApi:
   bool IsAvailable() const override {
@@ -118,6 +120,10 @@
     return get_platform_credential_list_;
   }
 
+  bool SupportsLargeBlobs() const override {
+    return is_bound_ && (api_version_ >= WEBAUTHN_API_VERSION_3);
+  }
+
   HRESULT IsUserVerifyingPlatformAuthenticatorAvailable(
       BOOL* available) override {
     DCHECK(is_bound_);
@@ -245,6 +251,8 @@
                                     CtapMakeCredentialRequest request,
                                     MakeCredentialOptions request_options) {
   DCHECK(webauthn_api->IsAvailable());
+  DCHECK(request_options.large_blob_support != LargeBlobSupport::kRequired ||
+         webauthn_api->SupportsLargeBlobs());
   const int api_version = webauthn_api->Version();
 
   std::u16string rp_id = base::UTF8ToUTF16(request.rp.id);
@@ -402,7 +410,7 @@
       &cancellation_id,
       &exclude_credential_list,
       enterprise_attestation,
-      WEBAUTHN_LARGE_BLOB_SUPPORT_NONE,
+      ToWinLargeBlobSupport(request_options.large_blob_support),
       /*bPreferResidentKey=*/request_options.resident_key ==
           ResidentKeyRequirement::kPreferred,
       request_options.is_off_the_record_context,
diff --git a/device/fido/win/webauthn_api.h b/device/fido/win/webauthn_api.h
index 7a401cc..34265ca 100644
--- a/device/fido/win/webauthn_api.h
+++ b/device/fido/win/webauthn_api.h
@@ -48,6 +48,11 @@
   // This should be preferred to checking the API version.
   virtual bool SupportsSilentDiscovery() const = 0;
 
+  // Returns whether the API is available and supports large blobs.
+  // Attempting to request large blob support is not allowed if this returns
+  // false.
+  virtual bool SupportsLargeBlobs() const = 0;
+
   virtual HRESULT IsUserVerifyingPlatformAuthenticatorAvailable(
       BOOL* available) = 0;
 
diff --git a/docs/updater/functional_spec.md b/docs/updater/functional_spec.md
index 0b223e9c..26b6fb29a 100644
--- a/docs/updater/functional_spec.md
+++ b/docs/updater/functional_spec.md
@@ -937,6 +937,14 @@
 becomes the command line
   `echo.exe AA %2 %BB`
 
+#### Signature verification
+Signature verification is not done on the app command executables, since the
+AppCommands will always be running from a secure location for system, and the
+key that defines the application command path is in HKLM, both of which mitigate
+the threat of a non-admin attacker. An Admin attacker would already be able to
+bypass any signature checking by binplanting a DLL, or just by performing
+whatever changes they like on the system, so is outside the threat model.
+
 ### Policy Status API
 The feature allows Chrome and other applications to query the policies that are
 currently in effect.
diff --git a/extensions/browser/api/networking_private/networking_private_lacros.cc b/extensions/browser/api/networking_private/networking_private_lacros.cc
index 630df9c..c4a8547 100644
--- a/extensions/browser/api/networking_private/networking_private_lacros.cc
+++ b/extensions/browser/api/networking_private/networking_private_lacros.cc
@@ -12,9 +12,6 @@
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
 #include "base/values.h"
-#include "chrome/browser/ash/crosapi/crosapi_ash.h"
-#include "chrome/browser/ash/crosapi/crosapi_manager.h"
-#include "chrome/browser/ash/crosapi/extension_info_private_ash.h"
 #include "chromeos/crosapi/mojom/networking_private.mojom.h"
 #include "chromeos/lacros/lacros_service.h"
 #include "extensions/browser/extensions_browser_client.h"
diff --git a/fuchsia_web/webengine/browser/input_browsertest.cc b/fuchsia_web/webengine/browser/input_browsertest.cc
index 3d830cc..2cd4db9 100644
--- a/fuchsia_web/webengine/browser/input_browsertest.cc
+++ b/fuchsia_web/webengine/browser/input_browsertest.cc
@@ -49,42 +49,52 @@
   return key_event;
 }
 
-// Returns a KeyEvent with only the |key| field set, and |key_meaning| not set.
-KeyEvent CreateKeyEventNoMeaning(Key key, KeyEventType event_type) {
-  KeyEvent key_event;
-  key_event.set_timestamp(base::TimeTicks::Now().ToZxTime());
-  key_event.set_type(event_type);
-  key_event.set_key(key);
-  return key_event;
-}
+struct KeyEventOptions {
+  bool repeat;
+};
 
 // Returns a KeyEvent with both |key| and |key_meaning| set.
 KeyEvent CreateKeyEvent(Key key,
                         KeyMeaning key_meaning,
-                        KeyEventType event_type) {
-  KeyEvent key_event = CreateKeyEventNoMeaning(key, event_type);
+                        KeyEventType event_type,
+                        KeyEventOptions options = {}) {
+  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.set_repeat_sequence(1);
+  }
   return key_event;
 }
-KeyEvent CreateKeyEvent(Key key, uint32_t codepoint, KeyEventType event_type) {
+KeyEvent CreateKeyEvent(Key key,
+                        uint32_t codepoint,
+                        KeyEventType event_type,
+                        KeyEventOptions options = {}) {
   return CreateKeyEvent(key, KeyMeaning::WithCodepoint(std::move(codepoint)),
-                        event_type);
+                        event_type, options);
 }
 KeyEvent CreateKeyEvent(Key key,
                         NonPrintableKey non_printable_key,
-                        KeyEventType event_type) {
+                        KeyEventType event_type,
+                        KeyEventOptions options = {}) {
   return CreateKeyEvent(
       key, KeyMeaning::WithNonPrintableKey(std::move(non_printable_key)),
-      event_type);
+      event_type, options);
 }
 
 base::Value ExpectedKeyValue(base::StringPiece code,
                              base::StringPiece key,
-                             base::StringPiece type) {
+                             base::StringPiece type,
+                             KeyEventOptions options = {}) {
   base::Value::Dict expected;
   expected.Set("code", code);
   expected.Set("key", key);
   expected.Set("type", type);
+  expected.Set("repeat", options.repeat);
   return base::Value(std::move(expected));
 }
 
@@ -365,6 +375,21 @@
                        ExpectedKeyValue("ShiftRight", "Shift", kKeyUp));
 }
 
+IN_PROC_BROWSER_TEST_F(KeyboardInputTest, RepeatedKeys) {
+  keyboard_service_->SendKeyEvent(
+      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.
+  ExpectKeyEventsEqual(
+      ExpectedKeyValue("KeyA", "a", kKeyDown, {.repeat = true}),
+      ExpectedKeyValue("KeyA", "a", kKeyPress, {.repeat = true}),
+      ExpectedKeyValue("Digit8", "8", kKeyDown, {.repeat = true}),
+      ExpectedKeyValue("Digit8", "8", kKeyPress, {.repeat = true}));
+}
+
 IN_PROC_BROWSER_TEST_F(KeyboardInputTest, Disconnect) {
   // Disconnect the keyboard service.
   keyboard_service_.reset();
diff --git a/fuchsia_web/webengine/test/data/keyevents.html b/fuchsia_web/webengine/test/data/keyevents.html
index 21e9d761..89714d8c 100644
--- a/fuchsia_web/webengine/test/data/keyevents.html
+++ b/fuchsia_web/webengine/test/data/keyevents.html
@@ -4,17 +4,17 @@
   var keyDicts = [];
 
   document.addEventListener("keypress", (e) => {
-    keyDicts.push( { "key" : e.key, "code" : e.code, "type" : e.type});
+    keyDicts.push( { "key" : e.key, "code" : e.code, "type" : e.type, "repeat": e.repeat});
     document.title = keyDicts.length;
   });
 
   document.addEventListener("keyup", (e) => {
-    keyDicts.push({ "key" : e.key, "code" : e.code, "type" : e.type});
+    keyDicts.push({ "key" : e.key, "code" : e.code, "type" : e.type, "repeat": e.repeat});
     document.title = keyDicts.length;
   });
 
   document.addEventListener("keydown", (e) => {
-    keyDicts.push({ "key" : e.key, "code" : e.code, "type" : e.type});
+    keyDicts.push({ "key" : e.key, "code" : e.code, "type" : e.type, "repeat": e.repeat});
     document.title = keyDicts.length;
   });
 
diff --git a/gpu/command_buffer/service/dxgi_shared_handle_manager_unittest.cc b/gpu/command_buffer/service/dxgi_shared_handle_manager_unittest.cc
index c2507b7..7f33a273 100644
--- a/gpu/command_buffer/service/dxgi_shared_handle_manager_unittest.cc
+++ b/gpu/command_buffer/service/dxgi_shared_handle_manager_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/synchronization/lock.h"
 #include "base/task/thread_pool.h"
 #include "base/test/bind.h"
-#include "base/win/windows_version.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_angle_util_win.h"
 
@@ -21,12 +20,9 @@
 class DXGISharedHandleManagerTest : public testing::Test {
  protected:
   void SetUp() override {
-    // Using DXGI NT handles is universally supported only on Win8 and above.
     // TODO(sunnyps): Unify this with the check in D3DImageBackingFactory.
-    const bool shared_handles_supported =
-        base::win::GetVersion() >= base::win::Version::WIN8;
     d3d11_device_ = gl::QueryD3D11DeviceObjectFromANGLE();
-    if (shared_handles_supported && d3d11_device_) {
+    if (d3d11_device_) {
       dxgi_shared_handle_manager_ =
           base::MakeRefCounted<DXGISharedHandleManager>(d3d11_device_);
     }
diff --git a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc
index 428662c9..4cbecffd 100644
--- a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc
@@ -12,7 +12,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/trace_event/trace_event.h"
-#include "base/win/windows_version.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
@@ -41,9 +40,7 @@
 const char* kDXGISwapChainImageBackingLabel = "DXGISwapChainImageBacking";
 
 bool IsWaitableSwapChainEnabled() {
-  // Waitable swap chains were first enabled in Win 8.1/DXGI 1.3
-  return (base::win::GetVersion() >= base::win::Version::WIN8_1) &&
-         base::FeatureList::IsEnabled(features::kDXGIWaitableSwapChain);
+  return base::FeatureList::IsEnabled(features::kDXGIWaitableSwapChain);
 }
 
 UINT GetMaxWaitableQueuedFrames() {
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 2348e26..562ed37 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -385,6 +385,7 @@
   int32_t GetPreferredAdapterIndex(WGPUPowerPreference power_preference,
                                    bool force_fallback) const;
 
+  // Decide if a device feature is exposed to render process.
   bool IsFeatureExposed(WGPUFeatureName feature) const;
 
   // Dawn wire uses procs which forward their calls to these methods.
@@ -1199,10 +1200,14 @@
     case WGPUFeatureName_TimestampQueryInsidePasses:
     case WGPUFeatureName_PipelineStatisticsQuery:
     case WGPUFeatureName_ChromiumExperimentalDp4a:
+    // TODO(crbug.com/1258986): DawnMultiPlanarFormats is a stable feature in
+    // Dawn, but currently we hide it from Render process as unsafe apis, so
+    // that 0-copy code path, which explicitly checks this feature, is protected
+    // under unsafe apis as well.
     case WGPUFeatureName_DawnMultiPlanarFormats:
-    case WGPUFeatureName_DepthClipControl:
       return allow_unsafe_apis_;
     case WGPUFeatureName_Depth32FloatStencil8:
+    case WGPUFeatureName_DepthClipControl:
     case WGPUFeatureName_TextureCompressionBC:
     case WGPUFeatureName_TextureCompressionETC2:
     case WGPUFeatureName_TextureCompressionASTC:
@@ -1344,7 +1349,9 @@
   // SharedImage / interop methods that would need specific usages.
   required_features.push_back(WGPUFeatureName_DawnInternalUsages);
 
-  // Always enable "multi-planar-formats" as long as available.
+  // Always require "multi-planar-formats" as long as supported, although
+  // currently this feature is not exposed to render process if unsafe apis
+  // disallowed.
   if (dawn::native::GetProcs().adapterHasFeature(
           adapter, WGPUFeatureName_DawnMultiPlanarFormats)) {
     required_features.push_back(WGPUFeatureName_DawnMultiPlanarFormats);
diff --git a/gpu/config/gpu_info_collector_win.cc b/gpu/config/gpu_info_collector_win.cc
index cb02307..fd4c295 100644
--- a/gpu/config/gpu_info_collector_win.cc
+++ b/gpu/config/gpu_info_collector_win.cc
@@ -8,11 +8,6 @@
 #include <stddef.h>
 #include <stdint.h>
 
-// This has to be included before windows.h.
-#include "third_party/re2/src/re2/re2.h"
-
-#include <windows.h>
-
 #include <d3d11.h>
 #include <d3d11_3.h>
 #include <d3d12.h>
@@ -32,9 +27,9 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/scoped_com_initializer.h"
-#include "base/win/windows_version.h"
 #include "build/branding_buildflags.h"
 #include "gpu/config/gpu_util.h"
+#include "third_party/re2/src/re2/re2.h"
 #include "ui/gl/direct_composition_support.h"
 #include "ui/gl/gl_angle_util_win.h"
 #include "ui/gl/gl_display.h"
@@ -163,20 +158,6 @@
 
 }  // namespace
 
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && defined(OFFICIAL_BUILD)
-// This function has a real implementation for official builds that can
-// be found in src/third_party/amd.
-bool GetAMDSwitchableInfo(bool* is_switchable,
-                          uint32_t* active_vendor_id,
-                          uint32_t* active_device_id);
-#else
-bool GetAMDSwitchableInfo(bool* is_switchable,
-                          uint32_t* active_vendor_id,
-                          uint32_t* active_device_id) {
-  return false;
-}
-#endif
-
 // This has to be called after a context is created, active GPU is identified,
 // and GPU driver bug workarounds are computed again. Otherwise the workaround
 // |disable_direct_composition| may not be correctly applied.
@@ -211,10 +192,6 @@
   if (FAILED(hr))
     return false;
 
-  bool found_amd = false;
-  bool found_intel = false;
-  bool found_nvidia = false;
-
   UINT i;
   Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
   for (i = 0; SUCCEEDED(dxgi_factory->EnumAdapters(i, &dxgi_adapter)); i++) {
@@ -241,20 +218,6 @@
       DLOG(ERROR) << "Unable to retrieve the umd version of adapter: "
                   << desc.Description << " HR: " << std::hex << hr;
     }
-    switch (device.vendor_id) {
-      case 0x8086:
-        found_intel = true;
-        break;
-      case 0x1002:
-        found_amd = true;
-        break;
-      case 0x10de:
-        found_nvidia = true;
-        break;
-      default:
-        break;
-    }
-
     if (i == 0) {
       gpu_info->gpu = device;
     } else {
@@ -262,23 +225,6 @@
     }
   }
 
-  if (found_intel && base::win::GetVersion() < base::win::Version::WIN10) {
-    // Since Windows 10 (and Windows 8.1 on some systems), switchable graphics
-    // platforms are managed by Windows and each adapter is accessible as
-    // separate devices.
-    // See https://msdn.microsoft.com/en-us/windows/dn265501(v=vs.80)
-    if (found_amd) {
-      bool is_amd_switchable = false;
-      uint32_t active_vendor = 0, active_device = 0;
-      GetAMDSwitchableInfo(&is_amd_switchable, &active_vendor, &active_device);
-      gpu_info->amd_switchable = is_amd_switchable;
-    } else if (found_nvidia) {
-      // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
-      HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
-      gpu_info->optimus = nvd3d9wrap != nullptr;
-    }
-  }
-
   Microsoft::WRL::ComPtr<IDXGIFactory6> dxgi_factory6;
   if (gpu_info->GpuCount() > 1 && SUCCEEDED(dxgi_factory.As(&dxgi_factory6))) {
     if (SUCCEEDED(dxgi_factory6->EnumAdapterByGpuPreference(
@@ -541,7 +487,6 @@
     PFN_vkEnumeratePhysicalDevices* vkEnumeratePhysicalDevices,
     PFN_vkEnumerateDeviceExtensionProperties*
         vkEnumerateDeviceExtensionProperties) {
-
   *vkEnumeratePhysicalDevices =
       reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(
           vkGetInstanceProcAddr(vk_instance, "vkEnumeratePhysicalDevices"));
diff --git a/gpu/ipc/service/gpu_watchdog_thread.cc b/gpu/ipc/service/gpu_watchdog_thread.cc
index 38d1d94..7dfca751 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread.cc
@@ -4,6 +4,10 @@
 
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
 
+#include <memory>
+#include <string>
+#include <utility>
+
 #include "base/atomicops.h"
 #include "base/bit_cast.h"
 #include "base/command_line.h"
@@ -30,10 +34,6 @@
 #include "gpu/config/gpu_switches.h"
 #include "gpu/ipc/common/result_codes.h"
 
-#if BUILDFLAG(IS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
 namespace gpu {
 
 base::TimeDelta GetGpuWatchdogTimeout() {
@@ -50,12 +50,11 @@
   }
 
 #if BUILDFLAG(IS_WIN)
-  if (base::win::GetVersion() >= base::win::Version::WIN10) {
-    int num_of_processors = base::SysInfo::NumberOfProcessors();
-    if (num_of_processors > 8)
-      return (kGpuWatchdogTimeout - base::Seconds(10));
-    else if (num_of_processors <= 4)
-      return kGpuWatchdogTimeout + base::Seconds(5);
+  int num_of_processors = base::SysInfo::NumberOfProcessors();
+  if (num_of_processors > 8) {
+    return (kGpuWatchdogTimeout - base::Seconds(10));
+  } else if (num_of_processors <= 4) {
+    return kGpuWatchdogTimeout + base::Seconds(5);
   }
 #endif
 
diff --git a/gpu/ipc/service/gpu_watchdog_thread_unittest.cc b/gpu/ipc/service/gpu_watchdog_thread_unittest.cc
index 23d68d8..13bea03 100644
--- a/gpu/ipc/service/gpu_watchdog_thread_unittest.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread_unittest.cc
@@ -3,6 +3,10 @@
 // found in the LICENSE file.
 
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
+
+#include <memory>
+#include <string>
+
 #include "base/test/task_environment.h"
 
 #include "base/power_monitor/power_monitor.h"
@@ -15,10 +19,6 @@
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(IS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
 #if BUILDFLAG(IS_MAC)
 #include "base/mac/mac_util.h"
 #endif
@@ -121,13 +121,7 @@
 
   TimeOutType timeout_type = kNormal;
 
-#if BUILDFLAG(IS_WIN)
-  // Win7
-  if (base::win::GetVersion() < base::win::Version::WIN10) {
-    timeout_type = kSlow;
-  }
-
-#elif BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_MAC)
   // Use slow timeout for Mac versions < 11.00 and for MacBookPro model <
   // MacBookPro14,1
   int os_version = base::mac::internal::MacOSVersion();
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 2cfeea1c..2018a88 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -352,7 +352,6 @@
     "lib/switches.cc",
     "lib/utility/headless_content_utility_client.cc",
     "lib/utility/headless_content_utility_client.h",
-    "public/headless_browser.cc",
     "public/headless_browser.h",
     "public/headless_browser_context.h",
     "public/headless_devtools_channel.h",
diff --git a/headless/lib/browser/headless_browser_impl.cc b/headless/lib/browser/headless_browser_impl.cc
index 6d7716c..6b809b912 100644
--- a/headless/lib/browser/headless_browser_impl.cc
+++ b/headless/lib/browser/headless_browser_impl.cc
@@ -14,13 +14,122 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/devtools_agent_host.h"
+#include "content/public/common/user_agent.h"
 #include "headless/lib/browser/headless_browser_context_impl.h"
 #include "headless/lib/browser/headless_browser_main_parts.h"
 #include "headless/lib/browser/headless_devtools_agent_host_client.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
+#include "headless/public/version.h"
 
 namespace headless {
 
+namespace {
+// Product name for building the default user agent string.
+const char kHeadlessProductName[] = "HeadlessChrome";
+constexpr gfx::Size kDefaultWindowSize(800, 600);
+
+constexpr gfx::FontRenderParams::Hinting kDefaultFontRenderHinting =
+    gfx::FontRenderParams::Hinting::HINTING_FULL;
+
+}  // namespace
+
+using Options = HeadlessBrowser::Options;
+using Builder = HeadlessBrowser::Options::Builder;
+
+Options::Options()
+    : user_agent(content::BuildUserAgentFromProduct(
+          HeadlessBrowser::GetProductNameAndVersion())),
+      window_size(kDefaultWindowSize),
+      font_render_hinting(kDefaultFontRenderHinting) {}
+
+Options::Options(Options&& options) = default;
+
+Options::~Options() = default;
+
+Options& Options::operator=(Options&& options) = default;
+
+bool Options::DevtoolsServerEnabled() {
+  return (devtools_pipe_enabled || !devtools_endpoint.IsEmpty());
+}
+
+Builder::Builder() = default;
+
+Builder::~Builder() = default;
+
+Builder& Builder::SetUserAgent(const std::string& agent) {
+  options_.user_agent = agent;
+  return *this;
+}
+
+Builder& Builder::SetAcceptLanguage(const std::string& language) {
+  options_.accept_language = language;
+  return *this;
+}
+
+Builder& Builder::SetEnableBeginFrameControl(bool enable) {
+  options_.enable_begin_frame_control = enable;
+  return *this;
+}
+
+Builder& Builder::EnableDevToolsServer(const net::HostPortPair& endpoint) {
+  options_.devtools_endpoint = endpoint;
+  return *this;
+}
+
+Builder& Builder::EnableDevToolsPipe() {
+  options_.devtools_pipe_enabled = true;
+  return *this;
+}
+
+Builder& Builder::SetProxyConfig(std::unique_ptr<net::ProxyConfig> config) {
+  options_.proxy_config = std::move(config);
+  return *this;
+}
+
+Builder& Builder::SetUserDataDir(const base::FilePath& dir) {
+  options_.user_data_dir = dir;
+  return *this;
+}
+
+Builder& Builder::SetWindowSize(const gfx::Size& size) {
+  options_.window_size = size;
+  return *this;
+}
+
+Builder& Builder::SetIncognitoMode(bool incognito) {
+  options_.incognito_mode = incognito;
+  return *this;
+}
+
+Builder& Builder::SetBlockNewWebContents(bool block) {
+  options_.block_new_web_contents = block;
+  return *this;
+}
+
+Builder& Builder::SetCrashReporterEnabled(bool enabled) {
+  options_.enable_crash_reporter = enabled;
+  return *this;
+}
+
+Builder& Builder::SetCrashDumpsDir(const base::FilePath& dir) {
+  options_.crash_dumps_dir = dir;
+  return *this;
+}
+
+Builder& Builder::SetFontRenderHinting(gfx::FontRenderParams::Hinting hinting) {
+  options_.font_render_hinting = hinting;
+  return *this;
+}
+
+Options Builder::Build() {
+  return std::move(options_);
+}
+
+/// static
+std::string HeadlessBrowser::GetProductNameAndVersion() {
+  return std::string(kHeadlessProductName) + "/" + PRODUCT_VERSION;
+}
+
 HeadlessBrowserImpl::HeadlessBrowserImpl(
     base::OnceCallback<void(HeadlessBrowser*)> on_start_callback,
     HeadlessBrowser::Options options)
diff --git a/headless/public/headless_browser.cc b/headless/public/headless_browser.cc
deleted file mode 100644
index 17f83fe3..0000000
--- a/headless/public/headless_browser.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// 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.
-
-#include "headless/public/headless_browser.h"
-
-#include <utility>
-
-#include "build/build_config.h"
-#include "content/public/common/user_agent.h"
-#include "headless/public/version.h"
-#include "ui/gl/gl_switches.h"
-
-#if BUILDFLAG(IS_WIN)
-#include "sandbox/win/src/sandbox_types.h"
-#endif
-
-using Options = headless::HeadlessBrowser::Options;
-using Builder = headless::HeadlessBrowser::Options::Builder;
-
-namespace headless {
-
-namespace {
-// Product name for building the default user agent string.
-const char kHeadlessProductName[] = "HeadlessChrome";
-constexpr gfx::Size kDefaultWindowSize(800, 600);
-
-constexpr gfx::FontRenderParams::Hinting kDefaultFontRenderHinting =
-    gfx::FontRenderParams::Hinting::HINTING_FULL;
-
-}  // namespace
-
-Options::Options()
-    : user_agent(content::BuildUserAgentFromProduct(
-          HeadlessBrowser::GetProductNameAndVersion())),
-      window_size(kDefaultWindowSize),
-      font_render_hinting(kDefaultFontRenderHinting) {}
-
-Options::Options(Options&& options) = default;
-
-Options::~Options() = default;
-
-Options& Options::operator=(Options&& options) = default;
-
-bool Options::DevtoolsServerEnabled() {
-  return (devtools_pipe_enabled || !devtools_endpoint.IsEmpty());
-}
-
-Builder::Builder() = default;
-
-Builder::~Builder() = default;
-
-Builder& Builder::SetUserAgent(const std::string& agent) {
-  options_.user_agent = agent;
-  return *this;
-}
-
-Builder& Builder::SetAcceptLanguage(const std::string& language) {
-  options_.accept_language = language;
-  return *this;
-}
-
-Builder& Builder::SetEnableBeginFrameControl(bool enable) {
-  options_.enable_begin_frame_control = enable;
-  return *this;
-}
-
-Builder& Builder::EnableDevToolsServer(const net::HostPortPair& endpoint) {
-  options_.devtools_endpoint = endpoint;
-  return *this;
-}
-
-Builder& Builder::EnableDevToolsPipe() {
-  options_.devtools_pipe_enabled = true;
-  return *this;
-}
-
-Builder& Builder::SetProxyConfig(std::unique_ptr<net::ProxyConfig> config) {
-  options_.proxy_config = std::move(config);
-  return *this;
-}
-
-Builder& Builder::SetUserDataDir(const base::FilePath& dir) {
-  options_.user_data_dir = dir;
-  return *this;
-}
-
-Builder& Builder::SetWindowSize(const gfx::Size& size) {
-  options_.window_size = size;
-  return *this;
-}
-
-Builder& Builder::SetIncognitoMode(bool incognito) {
-  options_.incognito_mode = incognito;
-  return *this;
-}
-
-Builder& Builder::SetBlockNewWebContents(bool block) {
-  options_.block_new_web_contents = block;
-  return *this;
-}
-
-Builder& Builder::SetCrashReporterEnabled(bool enabled) {
-  options_.enable_crash_reporter = enabled;
-  return *this;
-}
-
-Builder& Builder::SetCrashDumpsDir(const base::FilePath& dir) {
-  options_.crash_dumps_dir = dir;
-  return *this;
-}
-
-Builder& Builder::SetFontRenderHinting(gfx::FontRenderParams::Hinting hinting) {
-  options_.font_render_hinting = hinting;
-  return *this;
-}
-
-Options Builder::Build() {
-  return std::move(options_);
-}
-
-/// static
-std::string HeadlessBrowser::GetProductNameAndVersion() {
-  return std::string(kHeadlessProductName) + "/" + PRODUCT_VERSION;
-}
-
-}  // namespace headless
diff --git a/headless/public/headless_browser.h b/headless/public/headless_browser.h
index 222f81c0..ec5398a 100644
--- a/headless/public/headless_browser.h
+++ b/headless/public/headless_browser.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 #include <string>
-#include <unordered_set>
 #include <vector>
 
 #include "base/files/file_path.h"
@@ -19,10 +18,6 @@
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/geometry/size.h"
 
-#if BUILDFLAG(IS_WIN)
-#include "sandbox/win/src/sandbox_types.h"
-#endif
-
 namespace base {
 class SingleThreadTaskRunner;
 }
@@ -174,8 +169,6 @@
 
   Builder& EnableDevToolsServer(const net::HostPortPair& endpoint);
   Builder& EnableDevToolsPipe();
-  Builder& SetGLImplementation(const std::string& implementation);
-  Builder& SetANGLEImplementation(const std::string& implementation);
 
   // Per-context settings.
 
diff --git a/infra/config/generated/builders/ci/linux-wpt-content-shell-leak-detection/properties.json b/infra/config/generated/builders/ci/linux-wpt-content-shell-leak-detection/properties.json
new file mode 100644
index 0000000..fbf39b5
--- /dev/null
+++ b/infra/config/generated/builders/ci/linux-wpt-content-shell-leak-detection/properties.json
@@ -0,0 +1,53 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "linux-wpt-content-shell-leak-detection",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "linux-wpt-content-shell-leak-detection",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-trusted",
+    "jobs": 250,
+    "metrics_project": "chromium-reclient-metrics"
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.fyi",
+  "recipe": "chromium"
+}
\ No newline at end of file
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index edf423d..70ffdd8 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -41295,6 +41295,96 @@
       }
     }
     builders {
+      name: "linux-wpt-content-shell-leak-detection"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/ci/linux-wpt-content-shell-leak-detection/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
+        '}'
+      priority: 35
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experimental: YES
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.buildbucket.omit_python2"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "linux-wpt-fyi-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -49727,10 +49817,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -49738,14 +49824,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -49823,10 +49901,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -49834,14 +49908,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -49919,10 +49985,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -49930,14 +49992,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -50015,10 +50069,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -50026,14 +50076,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -50111,10 +50153,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -50122,14 +50160,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -50205,10 +50235,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -50216,14 +50242,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -50301,10 +50319,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -50312,14 +50326,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -50400,10 +50406,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -50411,14 +50413,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -56248,10 +56242,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -56259,14 +56249,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -56348,10 +56330,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -56359,14 +56337,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -56458,10 +56428,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -56469,14 +56435,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -56568,10 +56526,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -56579,14 +56533,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -56678,10 +56624,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -56689,14 +56631,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -56791,10 +56725,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -56802,14 +56732,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -56902,10 +56824,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -56913,14 +56831,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -57013,10 +56923,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -57024,14 +56930,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -57197,10 +57095,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -57208,14 +57102,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -57307,10 +57193,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -57318,14 +57200,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -57420,10 +57294,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -57431,14 +57301,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -57531,10 +57393,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -57542,14 +57400,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -57652,10 +57502,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -57663,14 +57509,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -57763,10 +57601,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -57774,14 +57608,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -57873,10 +57699,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -57884,14 +57706,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -57989,10 +57803,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -58000,14 +57810,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -58099,10 +57901,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -58110,14 +57908,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -58201,10 +57991,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -58212,14 +57998,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -58311,10 +58089,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -58322,14 +58096,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -58421,10 +58187,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -58432,14 +58194,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -58531,10 +58285,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -58542,14 +58292,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -58641,10 +58383,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -58652,14 +58390,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -58751,10 +58481,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -58762,14 +58488,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -58861,10 +58579,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -58872,14 +58586,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -58971,10 +58677,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -58982,14 +58684,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -59081,10 +58775,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -59092,14 +58782,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -59191,10 +58873,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -59202,14 +58880,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -59301,10 +58971,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -59312,14 +58978,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -59411,10 +59069,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -59422,14 +59076,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -59521,10 +59167,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -59532,14 +59174,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -59631,10 +59265,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -59642,14 +59272,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -59739,10 +59361,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -59750,14 +59368,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -59846,10 +59456,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -59857,14 +59463,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -59953,10 +59551,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -59964,14 +59558,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -60063,10 +59649,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -60074,14 +59656,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -60173,10 +59747,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -60184,14 +59754,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -60286,10 +59848,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -60297,14 +59855,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -60397,10 +59947,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -60408,14 +59954,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -60518,10 +60056,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -60529,14 +60063,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -60629,10 +60155,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -60640,14 +60162,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -60739,10 +60253,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -60750,14 +60260,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -60849,10 +60351,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -60860,14 +60358,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -60958,10 +60448,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -60969,14 +60455,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -61068,10 +60546,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -61079,14 +60553,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -61178,10 +60644,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -61189,14 +60651,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -61287,10 +60741,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -61298,14 +60748,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -61396,10 +60838,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -61407,14 +60845,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -61506,10 +60936,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -61517,14 +60943,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -61616,10 +61034,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -61627,14 +61041,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -61726,10 +61132,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -61737,14 +61139,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -61836,10 +61230,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -61847,14 +61237,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -61946,10 +61328,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -61957,14 +61335,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -62056,10 +61426,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -62067,14 +61433,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -62165,10 +61523,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -62176,14 +61530,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -62275,10 +61621,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -62286,14 +61628,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -62385,10 +61719,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -62396,14 +61726,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -62495,10 +61817,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -62506,14 +61824,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -62604,10 +61914,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -62615,14 +61921,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -62714,10 +62012,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -62725,14 +62019,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -62824,10 +62110,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -62835,14 +62117,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -62933,10 +62207,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -62944,14 +62214,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -63042,10 +62304,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -63053,14 +62311,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -63152,10 +62402,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -63163,14 +62409,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -63309,10 +62547,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -63320,14 +62554,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -63407,10 +62633,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -63418,14 +62640,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -63518,10 +62732,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -63529,14 +62739,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -63628,10 +62830,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -63639,14 +62837,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -63738,10 +62928,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -63749,14 +62935,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -63847,10 +63025,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -63858,14 +63032,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -63958,10 +63124,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -63969,14 +63131,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -64069,10 +63223,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -64080,14 +63230,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -64178,10 +63320,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -64189,14 +63327,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -64288,10 +63418,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -64299,14 +63425,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -64398,10 +63516,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -64409,14 +63523,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -64508,10 +63614,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -64519,14 +63621,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -64610,10 +63704,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -64621,14 +63711,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -64718,10 +63800,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -64729,14 +63807,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -64826,10 +63896,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -64837,14 +63903,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -64934,10 +63992,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -64945,14 +63999,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -65042,10 +64088,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -65053,14 +64095,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -65150,10 +64184,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -65161,14 +64191,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -65259,10 +64281,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -65270,14 +64288,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -65368,10 +64378,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -65379,14 +64385,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -65476,10 +64474,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -65487,14 +64481,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -65584,10 +64570,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -65595,14 +64577,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -65692,10 +64666,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -65703,14 +64673,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -65800,10 +64762,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -65811,14 +64769,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -65945,10 +64895,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -65956,14 +64902,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -66055,10 +64993,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -66066,14 +65000,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -66165,10 +65091,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -66176,14 +65098,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -66384,10 +65298,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -66395,14 +65305,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -66494,10 +65396,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -66505,14 +65403,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -66596,10 +65486,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -66607,14 +65493,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -66706,10 +65584,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -66717,14 +65591,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -66816,10 +65682,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -66827,14 +65689,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -66918,10 +65772,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -66929,14 +65779,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -67028,10 +65870,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -67039,14 +65877,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -67138,10 +65968,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -67149,14 +65975,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -67248,10 +66066,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -67259,14 +66073,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -67468,10 +66274,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -67479,14 +66281,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -67579,10 +66373,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -67590,14 +66380,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -67799,10 +66581,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -67810,14 +66588,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -67905,10 +66675,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -67916,14 +66682,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68011,10 +66769,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68022,14 +66776,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68117,10 +66863,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68128,14 +66870,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68223,10 +66957,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68234,14 +66964,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68329,10 +67051,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68340,14 +67058,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68435,10 +67145,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68446,14 +67152,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68541,10 +67239,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68552,14 +67246,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68647,10 +67333,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68658,14 +67340,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68753,10 +67427,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68764,14 +67434,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68859,10 +67521,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68870,14 +67528,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -68965,10 +67615,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -68976,14 +67622,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -69071,10 +67709,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -69082,14 +67716,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -69177,10 +67803,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -69188,14 +67810,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -69283,10 +67897,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -69294,14 +67904,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -69389,10 +67991,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -69400,14 +67998,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -69495,10 +68085,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -69506,14 +68092,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -69601,10 +68179,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -69612,14 +68186,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -69707,10 +68273,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -69718,14 +68280,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -69813,10 +68367,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -69824,14 +68374,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -69918,10 +68460,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -69929,14 +68467,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70023,10 +68553,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70034,14 +68560,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70128,10 +68646,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70139,14 +68653,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70233,10 +68739,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70244,14 +68746,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70338,10 +68832,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70349,14 +68839,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70443,10 +68925,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70454,14 +68932,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70548,10 +69018,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70559,14 +69025,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70653,10 +69111,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70664,14 +69118,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70758,10 +69204,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70769,14 +69211,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70863,10 +69297,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70874,14 +69304,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -70968,10 +69390,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -70979,14 +69397,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -71073,10 +69483,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -71084,14 +69490,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -71178,10 +69576,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -71189,14 +69583,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -71284,10 +69670,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -71295,14 +69677,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -71390,10 +69764,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -71401,14 +69771,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -71496,10 +69858,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -71507,14 +69865,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -71602,10 +69952,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -71613,14 +69959,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -71708,10 +70046,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -71719,14 +70053,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -71814,10 +70140,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -71825,14 +70147,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -71920,10 +70234,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -71931,14 +70241,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -72026,10 +70328,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -72037,14 +70335,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -72132,10 +70422,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -72143,14 +70429,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -72238,10 +70516,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -72249,14 +70523,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -72344,10 +70610,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -72355,14 +70617,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -72450,10 +70704,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -72461,14 +70711,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -72555,10 +70797,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -72566,14 +70804,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -72660,10 +70890,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -72671,14 +70897,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -72848,10 +71066,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -72859,14 +71073,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -72960,10 +71166,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -72971,14 +71173,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -73072,10 +71266,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -73083,14 +71273,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -73182,10 +71364,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -73193,14 +71371,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -73294,10 +71464,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -73305,14 +71471,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -73406,10 +71564,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -73417,14 +71571,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -73518,10 +71664,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -73529,14 +71671,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -73630,10 +71764,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -73641,14 +71771,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -73739,10 +71861,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -73750,14 +71868,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -73852,10 +71962,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -73863,14 +71969,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -73964,10 +72062,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -73975,14 +72069,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -74077,10 +72163,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -74088,14 +72170,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -74189,10 +72263,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -74200,14 +72270,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -74301,10 +72363,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -74312,14 +72370,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -74413,10 +72463,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -74424,14 +72470,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -74525,10 +72563,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -74536,14 +72570,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -74637,10 +72663,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -74648,14 +72670,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -74749,10 +72763,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -74760,14 +72770,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -74861,10 +72863,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -74872,14 +72870,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -74973,10 +72963,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -74984,14 +72970,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -75085,10 +73063,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -75096,14 +73070,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -75194,10 +73160,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -75205,14 +73167,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -75302,10 +73256,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -75313,14 +73263,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -75415,10 +73357,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -75426,14 +73364,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -75526,10 +73456,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -75537,14 +73463,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -75635,10 +73553,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -75646,14 +73560,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -75744,10 +73650,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -75755,14 +73657,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -75854,10 +73748,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -75865,14 +73755,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -75964,10 +73846,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -75975,14 +73853,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -76074,10 +73944,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -76085,14 +73951,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -76183,10 +74041,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -76194,14 +74048,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -76411,10 +74257,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -76422,14 +74264,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -76521,10 +74355,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -76532,14 +74362,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -76631,10 +74453,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -76642,14 +74460,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -76741,10 +74551,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -76752,14 +74558,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -76851,10 +74649,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -76862,14 +74656,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -76961,10 +74747,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -76972,14 +74754,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -77071,10 +74845,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -77082,14 +74852,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -77181,10 +74943,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -77192,14 +74950,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -77291,10 +75041,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -77302,14 +75048,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -77393,10 +75131,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -77404,14 +75138,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -77503,10 +75229,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -77514,14 +75236,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -77612,10 +75326,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -77623,14 +75333,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -77722,10 +75424,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -77733,14 +75431,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -77832,10 +75522,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -77843,14 +75529,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -77941,10 +75619,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -77952,14 +75626,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -78052,10 +75718,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -78063,14 +75725,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -78162,10 +75816,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -78173,14 +75823,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -78264,10 +75906,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -78275,14 +75913,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -78374,10 +76004,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -78385,14 +76011,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -78482,10 +76100,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -78493,14 +76107,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -78592,10 +76198,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -78603,14 +76205,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -78702,10 +76296,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -78713,14 +76303,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -78812,10 +76394,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -78823,14 +76401,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -78922,10 +76492,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -78933,14 +76499,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -79032,10 +76590,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -79043,14 +76597,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -79142,10 +76688,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -79153,14 +76695,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -79252,10 +76786,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -79263,14 +76793,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -79362,10 +76884,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -79373,14 +76891,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -79464,10 +76974,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -79475,14 +76981,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -79574,10 +77072,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -79585,14 +77079,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -79684,10 +77170,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -79695,14 +77177,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -79794,10 +77268,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -79805,14 +77275,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -79903,10 +77365,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -79914,14 +77372,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -80014,10 +77464,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -80025,14 +77471,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -80125,10 +77563,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -80136,14 +77570,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -80235,10 +77661,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -80246,14 +77668,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -80340,10 +77754,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -80351,14 +77761,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -80450,10 +77852,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -80461,14 +77859,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -80560,10 +77950,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -80571,14 +77957,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -80670,10 +78048,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -80681,14 +78055,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -80780,10 +78146,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -80791,14 +78153,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -80893,10 +78247,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -80904,14 +78254,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -81004,10 +78346,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -81015,14 +78353,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -81114,10 +78444,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -81125,14 +78451,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -81235,10 +78553,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -81246,14 +78560,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -81349,10 +78655,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -81360,14 +78662,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -81458,10 +78752,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -81469,14 +78759,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -81567,10 +78849,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -81578,14 +78856,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -81676,10 +78946,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -81687,14 +78953,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -81783,10 +79041,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -81794,14 +79048,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -81925,10 +79171,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -81936,14 +79178,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -82032,10 +79266,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -82043,14 +79273,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -82142,10 +79364,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -82153,14 +79371,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -82252,10 +79462,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -82263,14 +79469,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -82365,10 +79563,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -82376,14 +79570,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -82476,10 +79662,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -82487,14 +79669,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -82597,10 +79771,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -82608,14 +79778,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -82708,10 +79870,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -82719,14 +79877,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -82818,10 +79968,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -82829,14 +79975,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -82928,10 +80066,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -82939,14 +80073,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -83038,10 +80164,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -83049,14 +80171,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -83148,10 +80262,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -83159,14 +80269,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -83257,10 +80359,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -83268,14 +80366,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -83367,10 +80457,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -83378,14 +80464,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -83477,10 +80555,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -83488,14 +80562,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -83587,10 +80653,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -83598,14 +80660,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -83700,10 +80754,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -83711,14 +80761,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -83811,10 +80853,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -83822,14 +80860,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -83932,10 +80962,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -83943,14 +80969,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -84043,10 +81061,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -84054,14 +81068,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -84153,10 +81159,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -84164,14 +81166,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -84263,10 +81257,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -84274,14 +81264,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -84369,10 +81351,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -84380,14 +81358,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -84479,10 +81449,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -84490,14 +81456,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -84592,10 +81550,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -84603,14 +81557,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -84702,10 +81648,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -84713,14 +81655,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -84816,10 +81750,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -84827,14 +81757,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -84926,10 +81848,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -84937,14 +81855,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -85039,10 +81949,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -85050,14 +81956,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -85150,10 +82048,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -85161,14 +82055,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -85271,10 +82157,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -85282,14 +82164,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -85382,10 +82256,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -85393,14 +82263,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -85491,10 +82353,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -85502,14 +82360,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -85592,10 +82442,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -85603,14 +82449,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -85702,10 +82540,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -85713,14 +82547,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -85890,10 +82716,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -85901,14 +82723,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -85999,10 +82813,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86010,14 +82820,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -86108,10 +82910,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86119,14 +82917,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -86219,10 +83009,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86230,14 +83016,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -86328,10 +83106,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86339,14 +83113,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -86436,10 +83202,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86447,14 +83209,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -86545,10 +83299,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86556,14 +83306,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -86654,10 +83396,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86665,14 +83403,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -86762,10 +83492,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86773,14 +83499,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -86871,10 +83589,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86882,14 +83596,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -86980,10 +83686,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -86991,14 +83693,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -87093,10 +83787,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -87104,14 +83794,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -87202,10 +83884,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -87213,14 +83891,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -87311,10 +83981,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -87322,14 +83988,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -87432,10 +84090,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -87443,14 +84097,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -87538,10 +84184,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -87549,14 +84191,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -87646,10 +84280,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -87657,14 +84287,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -87754,10 +84376,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -87765,14 +84383,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -87863,10 +84473,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -87874,14 +84480,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -87972,10 +84570,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -87983,14 +84577,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -88081,10 +84667,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -88092,14 +84674,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -88190,10 +84764,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -88201,14 +84771,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -88299,10 +84861,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -88310,14 +84868,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -88408,10 +84958,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -88419,14 +84965,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -88517,10 +85055,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -88528,14 +85062,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -88627,10 +85153,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -88638,14 +85160,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -88737,10 +85251,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -88748,14 +85258,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -88846,10 +85348,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -88857,14 +85355,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -88955,10 +85445,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -88966,14 +85452,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -89064,10 +85542,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -89075,14 +85549,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -89173,10 +85639,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -89184,14 +85646,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -89282,10 +85736,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -89293,14 +85743,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -89391,10 +85833,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -89402,14 +85840,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -89500,10 +85930,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -89511,14 +85937,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -89609,10 +86027,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -89620,14 +86034,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -89718,10 +86124,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -89729,14 +86131,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -89827,10 +86221,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -89838,14 +86228,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -89936,10 +86318,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -89947,14 +86325,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90044,10 +86414,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -90055,14 +86421,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90144,10 +86502,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -90155,14 +86509,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90244,10 +86590,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -90255,14 +86597,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90354,10 +86688,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -90365,14 +86695,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90463,10 +86785,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -90474,14 +86792,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90560,10 +86870,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 0
       }
@@ -90571,14 +86877,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90666,10 +86964,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -90677,14 +86971,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90767,10 +87053,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -90778,14 +87060,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90868,10 +87142,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -90879,14 +87149,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -90969,10 +87231,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -90980,14 +87238,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -91079,10 +87329,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -91090,14 +87336,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -91301,10 +87539,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -91312,14 +87546,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -91446,10 +87672,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -91457,14 +87679,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -91556,10 +87770,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -91567,14 +87777,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -91666,10 +87868,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -91677,14 +87875,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -91772,10 +87962,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -91783,14 +87969,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -91880,10 +88058,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -91891,14 +88065,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -91990,10 +88156,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -92001,14 +88163,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -92091,10 +88245,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -92102,14 +88252,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -92201,10 +88343,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -92212,14 +88350,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -92311,10 +88441,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -92322,14 +88448,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -92424,10 +88542,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -92435,14 +88549,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -92535,10 +88641,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -92546,14 +88648,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -92645,10 +88739,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -92656,14 +88746,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -92766,10 +88848,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -92777,14 +88855,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -92874,10 +88944,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -92885,14 +88951,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -93016,10 +89074,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -93027,14 +89081,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -93158,10 +89204,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -93169,14 +89211,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -93265,10 +89299,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -93276,14 +89306,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -93372,10 +89394,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -93383,14 +89401,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -93482,10 +89492,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -93493,14 +89499,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -93592,10 +89590,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -93603,14 +89597,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -93694,10 +89680,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -93705,14 +89687,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -93804,10 +89778,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -93815,14 +89785,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -93914,10 +89876,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -93925,14 +89883,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -94024,10 +89974,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -94035,14 +89981,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -94134,10 +90072,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -94145,14 +90079,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -94244,10 +90170,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -94255,14 +90177,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -94354,10 +90268,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -94365,14 +90275,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -94464,10 +90366,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -94475,14 +90373,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -94574,10 +90464,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -94585,14 +90471,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -94684,10 +90562,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -94695,14 +90569,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -94794,10 +90660,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -94805,14 +90667,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -94904,10 +90758,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -94915,14 +90765,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -95014,10 +90856,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -95025,14 +90863,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -95124,10 +90954,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -95135,14 +90961,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -95233,10 +91051,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -95244,14 +91058,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -95329,10 +91135,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -95340,14 +91142,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
@@ -95439,10 +91233,6 @@
         value: 100
       }
       experiments {
-        key: "enable_weetbix_queries"
-        value: 100
-      }
-      experiments {
         key: "luci.buildbucket.omit_python2"
         value: 100
       }
@@ -95450,14 +91240,6 @@
         key: "luci.recipes.use_python3"
         value: 100
       }
-      experiments {
-        key: "weetbix.enable_weetbix_exonerations"
-        value: 100
-      }
-      experiments {
-        key: "weetbix.retry_weak_exonerations"
-        value: 100
-      }
       resultdb {
         enable: true
         bq_exports {
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 679ebb39..4eff6bf 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -8730,6 +8730,10 @@
     category: "linux"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/linux-wpt-content-shell-leak-detection"
+    category: "linux"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/linux-wpt-fyi-rel"
     category: "linux"
   }
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index c035b22..0533ec7 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -5090,6 +5090,15 @@
   }
 }
 job {
+  id: "linux-wpt-content-shell-leak-detection"
+  realm: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "linux-wpt-content-shell-leak-detection"
+  }
+}
+job {
   id: "linux-wpt-fyi-rel"
   realm: "ci"
   buildbucket {
@@ -6286,6 +6295,7 @@
   triggers: "linux-win_cross-rel"
   triggers: "linux-wpt-content-shell-asan-fyi-rel"
   triggers: "linux-wpt-content-shell-fyi-rel"
+  triggers: "linux-wpt-content-shell-leak-detection"
   triggers: "linux-wpt-fyi-rel"
   triggers: "linux-wpt-identity-fyi-rel"
   triggers: "linux-wpt-input-fyi-rel"
diff --git a/infra/config/generated/luci/project.cfg b/infra/config/generated/luci/project.cfg
index 8a047f1..1abf2a4d 100644
--- a/infra/config/generated/luci/project.cfg
+++ b/infra/config/generated/luci/project.cfg
@@ -7,7 +7,7 @@
 name: "chromium"
 access: "group:all"
 lucicfg {
-  version: "1.37.0"
+  version: "1.35.3"
   package_dir: "../.."
   config_dir: "generated/luci"
   entry_point: "main.star"
diff --git a/infra/config/lib/try.star b/infra/config/lib/try.star
index 5520280..a9b3068 100644
--- a/infra/config/lib/try.star
+++ b/infra/config/lib/try.star
@@ -207,12 +207,6 @@
     # TODO(crbug.com/1346781): Remove when the experiment is the default.
     experiments.setdefault("chromium_swarming.expose_merge_script_failures", 100)
 
-    # TODO(crbug.com/1314194): Enable weetbix everywhere. Remove once chromium
-    # recipe is updated to use this by default.
-    experiments.setdefault("weetbix.enable_weetbix_exonerations", 100)
-    experiments.setdefault("weetbix.retry_weak_exonerations", 100)
-    experiments.setdefault("enable_weetbix_queries", 100)
-
     merged_resultdb_bigquery_exports = [
         resultdb.export_test_results(
             bq_table = "chrome-luci-data.chromium.try_test_results",
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index 84fd82df..b0c74bc 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -786,6 +786,26 @@
 )
 
 ci.builder(
+    name = "linux-wpt-content-shell-leak-detection",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+        ),
+    ),
+    os = os.LINUX_DEFAULT,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    experimental = True,
+)
+
+ci.builder(
     name = "linux-wpt-fyi-rel",
     builder_spec = builder_config.builder_spec(
         gclient_config = builder_config.gclient_config(config = "chromium"),
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index e50e012c..279eb913 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -675,6 +675,9 @@
       <message name="IDS_IOS_CONTENT_CONTEXT_PINTAB" desc="The iOS context menu item for pinning a tab.[iOS only]">
         Pin Tab
       </message>
+      <message name="IDS_IOS_CONTENT_CONTEXT_UNPINTAB" desc="The iOS context menu item for unpinning a tab.[iOS only]">
+        Unpin Tab
+      </message>
       <message name="IDS_IOS_CONTENT_SUGGESTIONS_ACCESSIBILITY_LABEL_SUGGESTION" desc="The accessibility label of a suggestion. Summarizes fields in the reading list entry (title, publisher informations, status and informations). Read by Text-to-Speech.">
         <ph name="TITLE"><ex>Learn about the new Chromium Projects</ex>$1</ph>, <ph name="PUBLISHER_INFORMATION"><ex>The Chromium organization</ex>$2</ph>, <ph name="PUBLICATION_DATE"><ex>January 1 2017</ex>$3</ph>
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_CONTEXT_UNPINTAB.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_CONTEXT_UNPINTAB.png.sha1
new file mode 100644
index 0000000..2e0ad21
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_CONTEXT_UNPINTAB.png.sha1
@@ -0,0 +1 @@
+45a3f7bb9dd56e79450a3ea53c7090a42b41d78e
\ No newline at end of file
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index bad0a781..1f876a0 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -229,6 +229,8 @@
 
   registry->RegisterDictionaryPref(prefs::kOverflowMenuDestinationUsageHistory,
                                    PrefRegistry::LOSSY_PREF);
+  registry->RegisterListPref(prefs::kOverflowMenuNewDestinations,
+                             PrefRegistry::LOSSY_PREF);
 
   // Preferences related to Enterprise policies.
   registry->RegisterListPref(prefs::kRestrictAccountsToPatterns);
diff --git a/ios/chrome/browser/prefs/pref_names.cc b/ios/chrome/browser/prefs/pref_names.cc
index f9ad265..73d7606c 100644
--- a/ios/chrome/browser/prefs/pref_names.cc
+++ b/ios/chrome/browser/prefs/pref_names.cc
@@ -162,6 +162,10 @@
 const char kOverflowMenuDestinationUsageHistory[] =
     "overflow_menu.destination_usage_history";
 
+// List preference which tracks new destinations added to the overflow menu
+// carousel.
+const char kOverflowMenuNewDestinations[] = "overflow_menu.new_destinations";
+
 // Boolean that is true when Suggest support is enabled.
 const char kSearchSuggestEnabled[] = "search.suggest_enabled";
 
diff --git a/ios/chrome/browser/prefs/pref_names.h b/ios/chrome/browser/prefs/pref_names.h
index f611833f..b7077db 100644
--- a/ios/chrome/browser/prefs/pref_names.h
+++ b/ios/chrome/browser/prefs/pref_names.h
@@ -46,6 +46,7 @@
 extern const char kNTPFollowingFeedSortType[];
 extern const char kDefaultFollowingFeedSortTypeChanged[];
 extern const char kOverflowMenuDestinationUsageHistory[];
+extern const char kOverflowMenuNewDestinations[];
 extern const char kPrintingEnabled[];
 extern const char kSearchSuggestEnabled[];
 extern const char kTrackPricesOnTabsEnabled[];
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow.mm b/ios/chrome/browser/ui/authentication/authentication_flow.mm
index ef98e2a2..7f98eb27 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_flow.mm
@@ -99,6 +99,7 @@
   BOOL _didSignIn;
   BOOL _failedOrCancelled;
   BOOL _shouldSignOut;
+  BOOL _alreadySignedInWithTheSameAccount;
   // YES if the signed in account is a managed account and the sign-in flow
   // includes sync.
   BOOL _shouldShowManagedConfirmation;
@@ -375,7 +376,7 @@
 - (void)isSubjectToParentalControlCapabilityFetched:
     (SystemIdentityCapabilityResult)result {
   if (result == SystemIdentityCapabilityResult::kTrue) {
-    [self didChooseClearDataPolicy:SHOULD_CLEAR_DATA_CLEAR_DATA];
+    [self checkMergeCaseForSupervisedAccounts];
     return;
   }
   switch (self.postSignInAction) {
@@ -388,6 +389,19 @@
   }
 }
 
+// Checks if data should be merged or cleared when `_identityToSignIn`
+// is subject to parental controls and then continues sign-in.
+- (void)checkMergeCaseForSupervisedAccounts {
+  // Always clear the data for supervised accounts if the account
+  // is not already signed in.
+  self.localDataClearingStrategy = _alreadySignedInWithTheSameAccount
+                                       ? SHOULD_CLEAR_DATA_MERGE_DATA
+                                       : SHOULD_CLEAR_DATA_CLEAR_DATA;
+  [self continueSignin];
+}
+
+// Checks if data should be merged or cleared when `_identityToSignIn`
+// is not subject to parental controls and then continues sign-in.
 - (void)checkMergeCaseForUnsupervisedAccounts {
   if (([_performer shouldHandleMergeCaseForIdentity:_identityToSignIn
                                   browserStatePrefs:_browser->GetBrowserState()
@@ -413,6 +427,8 @@
     // sign-out is required.
     _shouldSignOut = YES;
   }
+  _alreadySignedInWithTheSameAccount =
+      [currentIdentity isEqual:_identityToSignIn];
 }
 
 - (void)signInIdentity:(id<SystemIdentity>)identity {
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_mediator.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_mediator.mm
index da17af6..7c43c3c 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_mediator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_mediator.mm
@@ -49,7 +49,7 @@
 @property(nonatomic, assign) PrefService* userPrefService;
 @property(nonatomic, assign, readonly) signin_metrics::AccessPoint accessPoint;
 // Identity for the sign-in in progress.
-@property(nonatomic, assign) id<SystemIdentity> signingIdentity;
+@property(nonatomic, weak) id<SystemIdentity> signingIdentity;
 // Duration before sign-in timeout. The property is overwritten in unittests.
 @property(nonatomic, assign, readonly) NSInteger signinTimeoutDurationSeconds;
 
diff --git a/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_egtest.mm b/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_egtest.mm
index 8ded6f8..5271e66b 100644
--- a/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_egtest.mm
@@ -14,6 +14,7 @@
 #import "ios/chrome/browser/prefs/pref_names.h"
 #import "ios/chrome/browser/signin/fake_system_identity.h"
 #import "ios/chrome/browser/signin/test_constants.h"
+#import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h"
 #import "ios/chrome/browser/ui/authentication/signin_matchers.h"
@@ -80,15 +81,14 @@
 // Checks that the forced sign-in prompt is fully dismissed by making sure
 // that there isn't any forced sign-in screen displayed.
 void VerifyForcedSigninFullyDismissed() {
-  [[EarlGrey
-      selectElementWithMatcher:
-          grey_accessibilityID(
-              first_run::kFirstRunLegacySignInScreenAccessibilityIdentifier)]
-      assertWithMatcher:grey_nil()];
-
   [[EarlGrey selectElementWithMatcher:
                  grey_accessibilityID(
-                     first_run::kFirstRunSyncScreenAccessibilityIdentifier)]
+                     first_run::kFirstRunSignInScreenAccessibilityIdentifier)]
+      assertWithMatcher:grey_nil()];
+
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kTangibleSyncViewAccessibilityIdentifier)]
       assertWithMatcher:grey_nil()];
 }
 
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_constants.h b/ios/chrome/browser/ui/authentication/signin/signin_constants.h
index 3cfc1817..45703046 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_constants.h
+++ b/ios/chrome/browser/ui/authentication/signin/signin_constants.h
@@ -53,6 +53,8 @@
 // Name of accessibility identifier for "Skip" button in the web sign-in
 // consistency sheet.
 extern NSString* const kWebSigninSkipButtonAccessibilityIdentifier;
+// Name of the accessibility identifier for the Tangible Sync view.
+extern NSString* const kTangibleSyncViewAccessibilityIdentifier;
 
 // Action that is required to do to complete the sign-in, or instead of sign-in.
 // This action is in charge of the SigninCoordinator's owner.
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_constants.mm b/ios/chrome/browser/ui/authentication/signin/signin_constants.mm
index 9351f265..ebd8fbbe 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_constants.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_constants.mm
@@ -21,6 +21,8 @@
     @"WebSigninContinueAsButtonAccessibilityIdentifier";
 NSString* const kWebSigninSkipButtonAccessibilityIdentifier =
     @"WebSigninSkipButtonAccessibilityIdentifier";
+NSString* const kTangibleSyncViewAccessibilityIdentifier =
+    @"TangibleSyncViewAccessibilityIdentifier";
 
 const char* kWebSigninConsistencyConsecutiveActiveDismissalLimitParam =
     "consecutive_active_dismissal_limit";
diff --git a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_mediator.mm b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_mediator.mm
index 77c7dd1..ff38ea1 100644
--- a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_mediator.mm
+++ b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_mediator.mm
@@ -147,6 +147,12 @@
     (const signin::PrimaryAccountChangeEvent&)event {
   if (event.GetEventTypeFor(signin::ConsentLevel::kSignin) ==
       signin::PrimaryAccountChangeEvent::Type::kCleared) {
+    // In rare cases, the auth flow may change the primary account.
+    // Ignore any primary account cleared event if a sign-in operation
+    // is in progress.
+    if (_authenticationFlow) {
+      return;
+    }
     [self.delegate tangibleSyncMediatorUserRemoved:self];
   }
 }
diff --git a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller.mm b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller.mm
index 3c2b3a0..22f3e465 100644
--- a/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/tangible_sync/tangible_sync_view_controller.mm
@@ -7,6 +7,7 @@
 #import "base/metrics/histogram_functions.h"
 #import "base/notreached.h"
 #import "components/signin/public/base/signin_metrics.h"
+#import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
 #import "ios/chrome/browser/ui/elements/instruction_view.h"
 #import "ios/chrome/browser/ui/first_run/fre_field_trial.h"
 #import "ios/chrome/browser/ui/icons/symbols.h"
@@ -69,6 +70,7 @@
 #pragma mark - UIViewController
 
 - (void)viewDidLoad {
+  self.view.accessibilityIdentifier = kTangibleSyncViewAccessibilityIdentifier;
   self.shouldHideBanner = YES;
   self.hasAvatarImage = YES;
   self.scrollToEndMandatory = YES;
diff --git a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_controller.mm b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_controller.mm
index d606d71..b2c9669 100644
--- a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_controller.mm
@@ -339,7 +339,7 @@
   TableViewTextEditItem* CVCInputItem =
       [[TableViewTextEditItem alloc] initWithType:ItemTypeCVCInput];
   CVCInputItem.delegate = self;
-  CVCInputItem.textFieldName =
+  CVCInputItem.fieldNameLabelText =
       l10n_util::GetNSString(IDS_AUTOFILL_CARD_UNMASK_PROMPT_CVC_FIELD_TITLE);
   CVCInputItem.keyboardType = UIKeyboardTypeNumberPad;
   CVCInputItem.hideIcon = YES;
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.mm
index 281c7d7..552b185 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.mm
@@ -93,7 +93,7 @@
 @property(nonatomic, weak) id<CardListDelegate> navigationDelegate;
 
 // The credit card data for this cell.
-@property(nonatomic, assign) ManualFillCreditCard* card;
+@property(nonatomic, weak) ManualFillCreditCard* card;
 
 @end
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index 2baae512..65b184c 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -268,13 +268,13 @@
 @property(nonatomic, strong)
     ActivityOverlayCoordinator* activityOverlayCoordinator;
 
-// Presents a QLPreviewController in order to display USDZ format 3D models.
-@property(nonatomic, strong) ARQuickLookCoordinator* ARQuickLookCoordinator;
-
 // Coordinator to add new credit card.
 @property(nonatomic, strong)
     AutofillAddCreditCardCoordinator* addCreditCardCoordinator;
 
+// Presents a QLPreviewController in order to display USDZ format 3D models.
+@property(nonatomic, strong) ARQuickLookCoordinator* ARQuickLookCoordinator;
+
 // Coordinator for the badge popup menu.
 @property(nonatomic, strong)
     BadgePopupMenuCoordinator* badgePopupMenuCoordinator;
@@ -283,6 +283,18 @@
 @property(nonatomic, strong)
     ContextMenuConfigurationProvider* contextMenuProvider;
 
+// Coordinator that manages the default browser promo modal.
+@property(nonatomic, strong)
+    DefaultBrowserPromoCoordinator* defaultBrowserPromoCoordinator;
+
+// Coordinator that manages the presentation of Download Manager UI.
+@property(nonatomic, strong)
+    DownloadManagerCoordinator* downloadManagerCoordinator;
+
+// The coordinator that manages enterprise prompts.
+@property(nonatomic, strong)
+    EnterprisePromptCoordinator* enterprisePromptCoordinator;
+
 // Coordinator for the find bar.
 @property(nonatomic, strong) FindBarCoordinator* findBarCoordinator;
 
@@ -297,16 +309,19 @@
 @property(nonatomic, strong)
     FormInputAccessoryCoordinator* formInputAccessoryCoordinator;
 
-// Presents a SFSafariViewController in order to download .mobileconfig file.
+// The container coordinators for the infobar modalities.
 @property(nonatomic, strong)
-    SafariDownloadCoordinator* SafariDownloadCoordinator;
-
-// Opens downloaded Vcard.
-@property(nonatomic, strong) VcardCoordinator* vcardCoordinator;
+    OverlayContainerCoordinator* infobarBannerOverlayContainerCoordinator;
+@property(nonatomic, strong)
+    OverlayContainerCoordinator* infobarModalOverlayContainerCoordinator;
 
 // The coordinator that manages net export.
 @property(nonatomic, strong) NetExportCoordinator* netExportCoordinator;
 
+// Coordinator for the non-modal default promo.
+@property(nonatomic, strong)
+    DefaultBrowserPromoNonModalCoordinator* nonModalPromoCoordinator;
+
 // Coordinator for Page Info UI.
 @property(nonatomic, strong) ChromeCoordinator* pageInfoCoordinator;
 
@@ -329,6 +344,9 @@
 @property(nonatomic, strong)
     PasswordSuggestionCoordinator* passwordSuggestionCoordinator;
 
+// Coordinator for the popup menu.
+@property(nonatomic, strong) PopupMenuCoordinator* popupMenuCoordinator;
+
 // Coordinator for the price notifications IPH feature.
 @property(nonatomic, strong)
     PriceNotificationsIPHCoordinator* priceNotificationsIPHCoordinator;
@@ -347,9 +365,6 @@
 // Coordinator for the QR scanner.
 @property(nonatomic, strong) QRScannerLegacyCoordinator* qrScannerCoordinator;
 
-// Coordinator for the popup menu.
-@property(nonatomic, strong) PopupMenuCoordinator* popupMenuCoordinator;
-
 // Coordinator for displaying the Reading List.
 @property(nonatomic, strong) ReadingListCoordinator* readingListCoordinator;
 
@@ -362,53 +377,38 @@
 // Coordinator for displaying Sad Tab.
 @property(nonatomic, strong) SadTabCoordinator* sadTabCoordinator;
 
+// Presents a SFSafariViewController in order to download .mobileconfig file.
+@property(nonatomic, strong)
+    SafariDownloadCoordinator* SafariDownloadCoordinator;
+
 // Coordinator for Safe Browsing.
 @property(nonatomic, strong) SafeBrowsingCoordinator* safeBrowsingCoordinator;
 
 // Coordinator for sharing scenarios.
 @property(nonatomic, strong) SharingCoordinator* sharingCoordinator;
 
+// The coordinator used for Spotlight Debugger.
+@property(nonatomic, strong)
+    SpotlightDebuggerCoordinator* spotlightDebuggerCoordinator;
+
 // Coordinator for presenting SKStoreProductViewController.
 @property(nonatomic, strong) StoreKitCoordinator* storeKitCoordinator;
 
-// Coordinator for Text Zoom.
-@property(nonatomic, strong) TextZoomCoordinator* textZoomCoordinator;
-
-// Coordinator that manages the default browser promo modal.
-@property(nonatomic, strong)
-    DefaultBrowserPromoCoordinator* defaultBrowserPromoCoordinator;
-
-// Coordinator that manages the presentation of Download Manager UI.
-@property(nonatomic, strong)
-    DownloadManagerCoordinator* downloadManagerCoordinator;
-
 // Coordinator that manages the tailored promo modals.
 @property(nonatomic, strong) TailoredPromoCoordinator* tailoredPromoCoordinator;
 
-// The container coordinators for the infobar modalities.
-@property(nonatomic, strong)
-    OverlayContainerCoordinator* infobarBannerOverlayContainerCoordinator;
-@property(nonatomic, strong)
-    OverlayContainerCoordinator* infobarModalOverlayContainerCoordinator;
-
-// Coordinator for the non-modal default promo.
-@property(nonatomic, strong)
-    DefaultBrowserPromoNonModalCoordinator* nonModalPromoCoordinator;
-
-// The coordinator that manages enterprise prompts.
-@property(nonatomic, strong)
-    EnterprisePromptCoordinator* enterprisePromptCoordinator;
-
 // The coordinator used for the Text Fragments feature.
 @property(nonatomic, strong) TextFragmentsCoordinator* textFragmentsCoordinator;
 
+// Coordinator for Text Zoom.
+@property(nonatomic, strong) TextZoomCoordinator* textZoomCoordinator;
+
+// Opens downloaded Vcard.
+@property(nonatomic, strong) VcardCoordinator* vcardCoordinator;
+
 // The coordinator used for What's New feature.
 @property(nonatomic, strong) WhatsNewCoordinator* whatsNewCoordinator;
 
-// The coordinator used for Spotlight Debugger.
-@property(nonatomic, strong)
-    SpotlightDebuggerCoordinator* spotlightDebuggerCoordinator;
-
 @end
 
 @implementation BrowserCoordinator {
diff --git a/ios/chrome/browser/ui/first_run/BUILD.gn b/ios/chrome/browser/ui/first_run/BUILD.gn
index f717e77..ff052696 100644
--- a/ios/chrome/browser/ui/first_run/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/BUILD.gn
@@ -229,7 +229,6 @@
     "enterprise_loading_screen_egtest.mm",
     "first_run_egtest.mm",
     "first_run_two_steps_egtest.mm",
-    "first_run_uma_dialog_egtest.mm",
   ]
   deps = [
     ":constants",
diff --git a/ios/chrome/browser/ui/first_run/first_run_two_steps_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_two_steps_egtest.mm
index a5e96590..d4aafa2b 100644
--- a/ios/chrome/browser/ui/first_run/first_run_two_steps_egtest.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_two_steps_egtest.mm
@@ -14,6 +14,7 @@
 #import "ios/chrome/browser/policy/policy_util.h"
 #import "ios/chrome/browser/signin/capabilities_types.h"
 #import "ios/chrome/browser/signin/fake_system_identity.h"
+#import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h"
 #import "ios/chrome/browser/ui/authentication/signin_matchers.h"
@@ -146,7 +147,7 @@
       "--force-fieldtrial-params=" +
       std::string(signin::kNewMobileIdentityConsistencyFRE.name) +
       ".Test:" + std::string(kNewMobileIdentityConsistencyFREParam) + "/" +
-      kNewMobileIdentityConsistencyFREParamTwoSteps);
+      kNewMobileIdentityConsistencyFREParamTangibleSyncA);
   // Show the First Run UI at startup.
   config.additional_args.push_back("-FirstRunForceEnabled");
   config.additional_args.push_back("true");
@@ -353,9 +354,9 @@
                        kPromoStyleScrollViewAccessibilityIdentifier]
       performAction:grey_tap()];
   // Accept sync.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     first_run::kFirstRunSyncScreenAccessibilityIdentifier)]
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kTangibleSyncViewAccessibilityIdentifier)]
       assertWithMatcher:grey_notNil()];
   [[self
       elementInteractionWithGreyMatcher:PromoStylePrimaryActionButtonMatcher()
@@ -389,9 +390,9 @@
                        kPromoStyleScrollViewAccessibilityIdentifier]
       performAction:grey_tap()];
   // Accept sync.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     first_run::kFirstRunSyncScreenAccessibilityIdentifier)]
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kTangibleSyncViewAccessibilityIdentifier)]
       assertWithMatcher:grey_notNil()];
   [[self
       elementInteractionWithGreyMatcher:PromoStylePrimaryActionButtonMatcher()
@@ -486,9 +487,9 @@
   GREYAssertFalse([FirstRunAppInterface isSyncFirstSetupComplete],
                   @"Sync shouldn't start when discarding advanced settings.");
   // Accept sync.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     first_run::kFirstRunSyncScreenAccessibilityIdentifier)]
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kTangibleSyncViewAccessibilityIdentifier)]
       assertWithMatcher:grey_notNil()];
   [[self
       elementInteractionWithGreyMatcher:PromoStylePrimaryActionButtonMatcher()
@@ -558,9 +559,9 @@
                  chrome_test_util::AdvancedSyncSettingsDoneButtonMatcher()]
       performAction:grey_tap()];
   // Accept sync.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     first_run::kFirstRunSyncScreenAccessibilityIdentifier)]
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kTangibleSyncViewAccessibilityIdentifier)]
       assertWithMatcher:grey_notNil()];
   [[self
       elementInteractionWithGreyMatcher:PromoStylePrimaryActionButtonMatcher()
@@ -613,9 +614,9 @@
                        kPromoStyleScrollViewAccessibilityIdentifier]
       performAction:grey_tap()];
   // Accept sync.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     first_run::kFirstRunSyncScreenAccessibilityIdentifier)]
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kTangibleSyncViewAccessibilityIdentifier)]
       assertWithMatcher:grey_notNil()];
   [[self
       elementInteractionWithGreyMatcher:PromoStylePrimaryActionButtonMatcher()
@@ -742,9 +743,9 @@
                  chrome_test_util::AdvancedSyncSettingsDoneButtonMatcher()]
       performAction:grey_tap()];
   // Accept sync.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     first_run::kFirstRunSyncScreenAccessibilityIdentifier)]
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kTangibleSyncViewAccessibilityIdentifier)]
       assertWithMatcher:grey_notNil()];
   [[self
       elementInteractionWithGreyMatcher:PromoStylePrimaryActionButtonMatcher()
@@ -807,9 +808,9 @@
                        kPromoStyleScrollViewAccessibilityIdentifier]
       performAction:grey_tap()];
   // Accept sync.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     first_run::kFirstRunSyncScreenAccessibilityIdentifier)]
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kTangibleSyncViewAccessibilityIdentifier)]
       assertWithMatcher:grey_notNil()];
   [[self
       elementInteractionWithGreyMatcher:PromoStylePrimaryActionButtonMatcher()
@@ -853,9 +854,8 @@
   // Sometimes EG continues before the Sync screen is displayed. Make sure to
   // wait for it.
   [ChromeEarlGrey
-      waitForSufficientlyVisibleElementWithMatcher:
-          grey_accessibilityID(
-              first_run::kFirstRunSyncScreenAccessibilityIdentifier)];
+      waitForUIElementToAppearWithMatcher:
+          grey_accessibilityID(kTangibleSyncViewAccessibilityIdentifier)];
   [[self
       elementInteractionWithGreyMatcher:PromoStylePrimaryActionButtonMatcher()
                    scrollViewIdentifier:
diff --git a/ios/chrome/browser/ui/first_run/first_run_uma_dialog_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_uma_dialog_egtest.mm
deleted file mode 100644
index 979a86ea..0000000
--- a/ios/chrome/browser/ui/first_run/first_run_uma_dialog_egtest.mm
+++ /dev/null
@@ -1,349 +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 "components/signin/ios/browser/features.h"
-#import "ios/chrome/browser/signin/fake_system_identity.h"
-#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
-#import "ios/chrome/browser/ui/first_run/field_trial_constants.h"
-#import "ios/chrome/browser/ui/first_run/first_run_app_interface.h"
-#import "ios/chrome/browser/ui/first_run/first_run_constants.h"
-#import "ios/chrome/browser/ui/ui_feature_flags.h"
-#import "ios/chrome/common/ui/promo_style/constants.h"
-#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
-#import "ios/chrome/grit/ios_strings.h"
-#import "ios/chrome/test/earl_grey/chrome_actions.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_matchers.h"
-#import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#import "ios/chrome/test/earl_grey/test_switches.h"
-#import "ios/testing/earl_grey/app_launch_configuration.h"
-#import "ios/testing/earl_grey/app_launch_manager.h"
-#import "ios/testing/earl_grey/earl_grey_test.h"
-#import "ui/base/l10n/l10n_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-// Returns a matcher for the welcome screen accept button.
-id<GREYMatcher> GetAcceptButton() {
-  return grey_allOf(grey_text(l10n_util::GetNSString(
-                        IDS_IOS_FIRST_RUN_WELCOME_SCREEN_ACCEPT_BUTTON)),
-                    grey_sufficientlyVisible(), nil);
-}
-
-// Returns matcher for the primary action button.
-id<GREYMatcher> PromoStylePrimaryActionButtonMatcher() {
-  return grey_accessibilityID(kPromoStylePrimaryActionAccessibilityIdentifier);
-}
-
-// Returns matcher for the secondary action button.
-id<GREYMatcher> PromoStyleSecondaryActionButtonMatcher() {
-  return grey_accessibilityID(
-      kPromoStyleSecondaryActionAccessibilityIdentifier);
-}
-
-// Returns matcher for UMA manage link.
-id<GREYMatcher> ManageUMALinkMatcher() {
-  return grey_accessibilityLabel(@"Manage");
-}
-
-// Checks that the welcome screen is displayed.
-void VerifyWelcomeScreenIsDisplayed() {
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     first_run::kFirstRunWelcomeScreenAccessibilityIdentifier)]
-      assertWithMatcher:grey_notNil()];
-}
-
-// Returns GREYElementInteraction for `matcher`, using `scrollViewMatcher` to
-// scroll.
-GREYElementInteraction* ElementInteractionWithGreyMatcher(
-    id<GREYMatcher> matcher,
-    NSString* scrollViewIdentifier) {
-  id<GREYMatcher> scrollViewMatcher =
-      grey_accessibilityID(scrollViewIdentifier);
-  // Needs to scroll slowly to make sure to not miss a cell if it is not
-  // currently on the screen. It should not be bigger than the visible part
-  // of the collection view.
-  id<GREYAction> searchAction = grey_scrollInDirection(kGREYDirectionDown, 200);
-  return [[EarlGrey selectElementWithMatcher:matcher]
-         usingSearchAction:searchAction
-      onElementWithMatcher:scrollViewMatcher];
-}
-
-// Dismiss default browser promo.
-void DismissDefaultBrowserPromo() {
-  id<GREYMatcher> buttonMatcher = grey_allOf(
-      grey_ancestor(grey_accessibilityID(
-          first_run::kFirstRunDefaultBrowserScreenAccessibilityIdentifier)),
-      grey_accessibilityLabel(l10n_util::GetNSString(
-          IDS_IOS_FIRST_RUN_DEFAULT_BROWSER_SCREEN_SECONDARY_ACTION)),
-      nil);
-  [[[EarlGrey selectElementWithMatcher:buttonMatcher]
-      assertWithMatcher:grey_notNil()] performAction:grey_tap()];
-}
-
-}  // namespace
-
-// Test first run with UMA dialog MICe FRE. Those tests are only related to the
-// new UMA dialog. The tests for the rest of the features are in
-// FirstRunTestCase.
-@interface FirstRunUMADialogTestCase : ChromeTestCase
-
-@end
-
-@implementation FirstRunUMADialogTestCase
-
-- (void)setUp {
-  [[self class] testForStartup];
-  [super setUp];
-}
-
-- (AppLaunchConfiguration)appConfigurationForTestCase {
-  AppLaunchConfiguration config;
-  config.additional_args.push_back(std::string("-") +
-                                   test_switches::kSignInAtStartup);
-  // Show the First Run UI at startup.
-  config.additional_args.push_back("-FirstRunForceEnabled");
-  config.additional_args.push_back("true");
-  // Relaunch app at each test to rewind the startup state.
-  config.relaunch_policy = ForceRelaunchByKilling;
-
-  return config;
-}
-
-#pragma mark Tests
-
-// Tests FRE with UMA default value and without sign-in.
-- (void)testWithUMACheckedAndNoSignin {
-  // Verify FRE.
-  VerifyWelcomeScreenIsDisplayed();
-  // Accept welcome screen.
-  [ElementInteractionWithGreyMatcher(
-      GetAcceptButton(), kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Skip sign-in.
-  [ElementInteractionWithGreyMatcher(
-      PromoStyleSecondaryActionButtonMatcher(),
-      kPromoStyleScrollViewAccessibilityIdentifier) performAction:grey_tap()];
-  // Check that UMA is on.
-  GREYAssertTrue(
-      [FirstRunAppInterface isUMACollectionEnabled],
-      @"kMetricsReportingEnabled pref was unexpectedly false by default.");
-  // Check signed out.
-  [SigninEarlGrey verifySignedOut];
-}
-
-// Tests FRE with UMA off and without sign-in.
-- (void)testWithUMAUncheckedAndNoSignin {
-  // Verify FRE.
-  VerifyWelcomeScreenIsDisplayed();
-  // Scroll down and open the UMA dialog.
-  [ElementInteractionWithGreyMatcher(
-      grey_allOf(ManageUMALinkMatcher(), grey_sufficientlyVisible(), nil),
-      kPromoStyleScrollViewAccessibilityIdentifier) performAction:grey_tap()];
-  // Turn off UMA.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
-                                   kImproveChromeItemAccessibilityIdentifier,
-                                   /*is_toggled_on=*/YES,
-                                   /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
-  // Close UMA dialog.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::NavigationBarDoneButton()]
-      performAction:grey_tap()];
-  // Accept welcome screen.
-  [ElementInteractionWithGreyMatcher(
-      GetAcceptButton(), kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Skip sign-in.
-  [ElementInteractionWithGreyMatcher(
-      PromoStyleSecondaryActionButtonMatcher(),
-      kPromoStyleScrollViewAccessibilityIdentifier) performAction:grey_tap()];
-  // Check that UMA is off.
-  GREYAssertFalse(
-      [FirstRunAppInterface isUMACollectionEnabled],
-      @"kMetricsReportingEnabled pref was unexpectedly true by default.");
-  // Check signed out.
-  [SigninEarlGrey verifySignedOut];
-}
-
-// Tests FRE with UMA off, reopen UMA dialog and close the FRE without sign-in.
-- (void)testUMAUncheckedWhenOpenedSecondTime {
-  // Verify FRE.
-  VerifyWelcomeScreenIsDisplayed();
-  // Scroll down and open the UMA dialog.
-  id<GREYMatcher> manageUMALinkMatcher =
-      grey_allOf(ManageUMALinkMatcher(), grey_sufficientlyVisible(), nil);
-  [ElementInteractionWithGreyMatcher(
-      manageUMALinkMatcher, kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Turn off UMA.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
-                                   kImproveChromeItemAccessibilityIdentifier,
-                                   /*is_toggled_on=*/YES,
-                                   /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
-  // Close UMA dialog.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::NavigationBarDoneButton()]
-      performAction:grey_tap()];
-  // Open UMA dialog again.
-  [ElementInteractionWithGreyMatcher(
-      manageUMALinkMatcher, kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Check UMA off.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
-                                   kImproveChromeItemAccessibilityIdentifier,
-                                   /*is_toggled_on=*/NO,
-                                   /*enabled=*/YES)]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  // Close UMA dialog.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::NavigationBarDoneButton()]
-      performAction:grey_tap()];
-  // Accept welcome screen.
-  [ElementInteractionWithGreyMatcher(
-      GetAcceptButton(), kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Skip sign-in.
-  [ElementInteractionWithGreyMatcher(
-      PromoStyleSecondaryActionButtonMatcher(),
-      kPromoStyleScrollViewAccessibilityIdentifier) performAction:grey_tap()];
-  // Check that UMA is off.
-  GREYAssertFalse(
-      [FirstRunAppInterface isUMACollectionEnabled],
-      @"kMetricsReportingEnabled pref was unexpectedly true by default.");
-  // Check signed out.
-  [SigninEarlGrey verifySignedOut];
-}
-
-// Tests to turn off UMA, and open the UMA dialog to turn it back on.
-- (void)testUMAUncheckedAndCheckItAgain {
-  // Verify FRE.
-  VerifyWelcomeScreenIsDisplayed();
-  // Scroll down and open the UMA dialog.
-  id<GREYMatcher> manageUMALinkMatcher =
-      grey_allOf(ManageUMALinkMatcher(), grey_sufficientlyVisible(), nil);
-  [ElementInteractionWithGreyMatcher(
-      manageUMALinkMatcher, kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Turn off UMA.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
-                                   kImproveChromeItemAccessibilityIdentifier,
-                                   /*is_toggled_on=*/YES,
-                                   /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
-  // Close UMA dialog.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::NavigationBarDoneButton()]
-      performAction:grey_tap()];
-  // Open UMA dialog again.
-  [ElementInteractionWithGreyMatcher(
-      manageUMALinkMatcher, kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Turn UMA back on.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
-                                   kImproveChromeItemAccessibilityIdentifier,
-                                   /*is_toggled_on=*/NO,
-                                   /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(YES)];
-  // Close UMA dialog.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::NavigationBarDoneButton()]
-      performAction:grey_tap()];
-  // Accept welcome screen.
-  [ElementInteractionWithGreyMatcher(
-      GetAcceptButton(), kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Skip sign-in.
-  [ElementInteractionWithGreyMatcher(
-      PromoStyleSecondaryActionButtonMatcher(),
-      kPromoStyleScrollViewAccessibilityIdentifier) performAction:grey_tap()];
-  // Check that UMA is on.
-  GREYAssertTrue(
-      [FirstRunAppInterface isUMACollectionEnabled],
-      @"kMetricsReportingEnabled pref was unexpectedly false by default.");
-  // Check signed out.
-  [SigninEarlGrey verifySignedOut];
-}
-
-// Tests FRE with UMA off and without sign-in.
-- (void)testWithUMAUncheckedAndSignin {
-  // Add identity.
-  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
-  [SigninEarlGrey addFakeIdentity:fakeIdentity];
-  // Verify FRE.
-  VerifyWelcomeScreenIsDisplayed();
-  // Scroll down and open the UMA dialog.
-  [ElementInteractionWithGreyMatcher(
-      grey_allOf(ManageUMALinkMatcher(), grey_sufficientlyVisible(), nil),
-      kPromoStyleScrollViewAccessibilityIdentifier) performAction:grey_tap()];
-  // Turn off UMA.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
-                                   kImproveChromeItemAccessibilityIdentifier,
-                                   /*is_toggled_on=*/YES,
-                                   /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
-  // Close UMA dialog.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::NavigationBarDoneButton()]
-      performAction:grey_tap()];
-  // Accept welcome screen.
-  [ElementInteractionWithGreyMatcher(
-      GetAcceptButton(), kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Accept sign-in.
-  [ElementInteractionWithGreyMatcher(
-      PromoStylePrimaryActionButtonMatcher(),
-      kPromoStyleScrollViewAccessibilityIdentifier) performAction:grey_tap()];
-  // Check that UMA is off.
-  GREYAssertFalse(
-      [FirstRunAppInterface isUMACollectionEnabled],
-      @"kMetricsReportingEnabled pref was unexpectedly true by default.");
-  // Check signed in.
-  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
-  // Check sync is on.
-  DismissDefaultBrowserPromo();
-  [ChromeEarlGreyUI openSettingsMenu];
-  [SigninEarlGrey verifySyncUIEnabled:YES];
-}
-
-// Tests FRE with UMA default value and with sign-in.
-- (void)testWithUMACheckedAndSignin {
-  // Add identity.
-  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
-  [SigninEarlGrey addFakeIdentity:fakeIdentity];
-  // Verify FRE.
-  VerifyWelcomeScreenIsDisplayed();
-  // Accept welcome screen.
-  [ElementInteractionWithGreyMatcher(
-      GetAcceptButton(), kPromoStyleScrollViewAccessibilityIdentifier)
-      performAction:grey_tap()];
-  // Accept sign-in.
-  [ElementInteractionWithGreyMatcher(
-      PromoStylePrimaryActionButtonMatcher(),
-      kPromoStyleScrollViewAccessibilityIdentifier) performAction:grey_tap()];
-  // Check that UMA is on.
-  GREYAssertTrue(
-      [FirstRunAppInterface isUMACollectionEnabled],
-      @"kMetricsReportingEnabled pref was unexpectedly false by default.");
-  // Check signed in.
-  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
-  // Check sync is on.
-  DismissDefaultBrowserPromo();
-  [ChromeEarlGreyUI openSettingsMenu];
-  [SigninEarlGrey verifySyncUIEnabled:YES];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/infobars/modals/autofill_address_profile/infobar_edit_address_profile_table_view_controller.mm b/ios/chrome/browser/ui/infobars/modals/autofill_address_profile/infobar_edit_address_profile_table_view_controller.mm
index 695c64d2..b599e21 100644
--- a/ios/chrome/browser/ui/infobars/modals/autofill_address_profile/infobar_edit_address_profile_table_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/modals/autofill_address_profile/infobar_edit_address_profile_table_view_controller.mm
@@ -125,7 +125,7 @@
 
     AutofillEditItem* item =
         [[AutofillEditItem alloc] initWithType:ItemTypeTextField];
-    item.textFieldName = l10n_util::GetNSString(field.displayStringID);
+    item.fieldNameLabelText = l10n_util::GetNSString(field.displayStringID);
     item.autofillUIType = AutofillUITypeFromAutofillType(field.autofillType);
     item.textFieldValue = _profileData[@(item.autofillUIType)];
     item.textFieldEnabled = YES;
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm
index 4355d9c2..9beabf3 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.mm
@@ -163,7 +163,7 @@
 
   TableViewTextEditItem* URLItem =
       [[TableViewTextEditItem alloc] initWithType:ItemTypeURL];
-  URLItem.textFieldName =
+  URLItem.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
   URLItem.textFieldValue = self.URL;
   URLItem.hideIcon = YES;
@@ -172,7 +172,7 @@
   self.originalUsername = self.username;
   self.usernameItem =
       [[TableViewTextEditItem alloc] initWithType:ItemTypeUsername];
-  self.usernameItem.textFieldName =
+  self.usernameItem.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME);
   self.usernameItem.textFieldValue = self.username;
   self.usernameItem.returnKeyType = UIReturnKeyDone;
@@ -183,7 +183,7 @@
 
   self.passwordItem =
       [[TableViewTextEditItem alloc] initWithType:ItemTypePassword];
-  self.passwordItem.textFieldName =
+  self.passwordItem.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
   self.passwordItem.textFieldValue = self.maskedPassword;
   self.passwordItem.identifyingIcon =
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
index cb8cf5b..2f119a2c 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
@@ -157,7 +157,7 @@
 
   TableViewTextEditItem* cardLastDigitsItem = [self
       textEditItemWithType:ItemTypeCardLastDigits
-             textFieldName:l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARD_NUMBER)
+        fieldNameLabelText:l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARD_NUMBER)
             textFieldValue:self.cardNumber
           textFieldEnabled:NO];
   cardLastDigitsItem.identifyingIcon = self.cardIssuerIcon;
@@ -166,7 +166,7 @@
 
   self.cardholderNameItem =
       [self textEditItemWithType:ItemTypeCardHolderName
-                   textFieldName:l10n_util::GetNSString(
+              fieldNameLabelText:l10n_util::GetNSString(
                                      IDS_IOS_AUTOFILL_CARDHOLDER_NAME)
                   textFieldValue:self.cardholderName
                 textFieldEnabled:self.supportsEditing];
@@ -175,7 +175,7 @@
 
   self.expirationMonthItem = [self
       textEditItemWithType:ItemTypeCardExpireMonth
-             textFieldName:l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_MONTH)
+        fieldNameLabelText:l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_MONTH)
             textFieldValue:self.expirationMonth
           textFieldEnabled:self.supportsEditing];
   [model addItem:self.expirationMonthItem
@@ -183,7 +183,7 @@
 
   self.expirationYearItem = [self
       textEditItemWithType:ItemTypeCardExpireYear
-             textFieldName:l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_YEAR)
+        fieldNameLabelText:l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_YEAR)
             textFieldValue:self.expirationYear
           textFieldEnabled:self.supportsEditing];
   [model addItem:self.expirationYearItem
@@ -452,12 +452,12 @@
 #pragma mark - Helpers
 
 - (TableViewTextEditItem*)textEditItemWithType:(ItemType)type
-                                 textFieldName:(NSString*)name
+                            fieldNameLabelText:(NSString*)name
                                 textFieldValue:(NSString*)value
                               textFieldEnabled:(BOOL)enabled {
   TableViewTextEditItem* textEditItem =
       [[TableViewTextEditItem alloc] initWithType:type];
-  textEditItem.textFieldName = name;
+  textEditItem.fieldNameLabelText = name;
   textEditItem.textFieldValue = value;
   textEditItem.textFieldEnabled = enabled;
   textEditItem.hideIcon = !enabled;
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_translate_table_view_controller.mm b/ios/chrome/browser/ui/infobars/modals/infobar_translate_table_view_controller.mm
index 0f19fee..fc1a8bf8 100644
--- a/ios/chrome/browser/ui/infobars/modals/infobar_translate_table_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_translate_table_view_controller.mm
@@ -160,23 +160,23 @@
   TableViewModel* model = self.tableViewModel;
   [model addSectionWithIdentifier:SectionIdentifierContent];
 
-  TableViewTextEditItem* sourceLanguageItem = [self
-      textEditItemForType:ItemTypeSourceLanguage
-            textFieldName:
-                l10n_util::GetNSString(
-                    IDS_IOS_TRANSLATE_INFOBAR_MODAL_SOURCE_LANGUAGE_FIELD_NAME)
-           textFieldValue:self.sourceLanguage];
+  TableViewTextEditItem* sourceLanguageItem =
+      [self textEditItemForType:ItemTypeSourceLanguage
+             fieldNameLabelText:
+                 l10n_util::GetNSString(
+                     IDS_IOS_TRANSLATE_INFOBAR_MODAL_SOURCE_LANGUAGE_FIELD_NAME)
+                 textFieldValue:self.sourceLanguage];
   sourceLanguageItem.accessibilityIdentifier =
       kTranslateInfobarModalTranslateSourceLanguageItemAXId;
   [model addItem:sourceLanguageItem
       toSectionWithIdentifier:SectionIdentifierContent];
 
-  TableViewTextEditItem* targetLanguageItem = [self
-      textEditItemForType:ItemTypeTargetLanguage
-            textFieldName:
-                l10n_util::GetNSString(
-                    IDS_IOS_TRANSLATE_INFOBAR_MODAL_TARGET_LANGUAGE_FIELD_NAME)
-           textFieldValue:self.targetLanguage];
+  TableViewTextEditItem* targetLanguageItem =
+      [self textEditItemForType:ItemTypeTargetLanguage
+             fieldNameLabelText:
+                 l10n_util::GetNSString(
+                     IDS_IOS_TRANSLATE_INFOBAR_MODAL_TARGET_LANGUAGE_FIELD_NAME)
+                 textFieldValue:self.targetLanguage];
   targetLanguageItem.accessibilityIdentifier =
       kTranslateInfobarModalTranslateTargetLanguageItemAXId;
   [model addItem:targetLanguageItem
@@ -468,12 +468,12 @@
 #pragma mark - Private
 
 - (TableViewTextEditItem*)textEditItemForType:(ItemType)itemType
-                                textFieldName:(NSString*)name
+                           fieldNameLabelText:(NSString*)name
                                textFieldValue:(NSString*)value {
   TableViewTextEditItem* item =
       [[TableViewTextEditItem alloc] initWithType:itemType];
   item.textFieldEnabled = NO;
-  item.textFieldName = name;
+  item.fieldNameLabelText = name;
   item.textFieldValue = value;
   return item;
 }
diff --git a/ios/chrome/browser/ui/menu/BUILD.gn b/ios/chrome/browser/ui/menu/BUILD.gn
index 3d06d2b..5686a91 100644
--- a/ios/chrome/browser/ui/menu/BUILD.gn
+++ b/ios/chrome/browser/ui/menu/BUILD.gn
@@ -99,6 +99,7 @@
     "resources:search_image",
     "resources:select",
     "resources:share",
+    "resources:unpin",
     "//base",
     "//base/test:test_support",
     "//components/sync_preferences:test_support",
diff --git a/ios/chrome/browser/ui/menu/action_factory.h b/ios/chrome/browser/ui/menu/action_factory.h
index 0d011c0d..a3e7a42 100644
--- a/ios/chrome/browser/ui/menu/action_factory.h
+++ b/ios/chrome/browser/ui/menu/action_factory.h
@@ -35,6 +35,10 @@
 // the given `block` upon execution.
 - (UIAction*)actionToPinTabWithBlock:(ProceduralBlock)block;
 
+// Creates a UIAction instance configured for unpinning a tab which will invoke
+// the given `block` upon execution.
+- (UIAction*)actionToUnpinTabWithBlock:(ProceduralBlock)block;
+
 // Creates a UIAction instance configured for deletion which will invoke
 // the given delete `block` when executed.
 - (UIAction*)actionToDeleteWithBlock:(ProceduralBlock)block;
diff --git a/ios/chrome/browser/ui/menu/action_factory.mm b/ios/chrome/browser/ui/menu/action_factory.mm
index 6158922d..e5f0ca0d 100644
--- a/ios/chrome/browser/ui/menu/action_factory.mm
+++ b/ios/chrome/browser/ui/menu/action_factory.mm
@@ -87,6 +87,17 @@
                 block:block];
 }
 
+- (UIAction*)actionToUnpinTabWithBlock:(ProceduralBlock)block {
+  UIImage* image = UseSymbols() ? DefaultSymbolWithPointSize(
+                                      kPinSlashSymbol, kSymbolActionPointSize)
+                                : [UIImage imageNamed:@"unpin"];
+  return [self
+      actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_UNPINTAB)
+                image:image
+                 type:MenuActionType::UnpinTab
+                block:block];
+}
+
 - (UIAction*)actionToDeleteWithBlock:(ProceduralBlock)block {
   UIImage* image = UseSymbols()
                        ? DefaultSymbolWithPointSize(kDeleteActionSymbol,
diff --git a/ios/chrome/browser/ui/menu/tab_context_menu_delegate.h b/ios/chrome/browser/ui/menu/tab_context_menu_delegate.h
index a032c11..c6a7743 100644
--- a/ios/chrome/browser/ui/menu/tab_context_menu_delegate.h
+++ b/ios/chrome/browser/ui/menu/tab_context_menu_delegate.h
@@ -51,6 +51,9 @@
 // Tells the delegate to pin a tab with the item identifier `identifier`.
 - (void)pinTabWithIdentifier:(NSString*)identifier;
 
+// Tells the delegate to unpin a tab with the item identifier `identifier`.
+- (void)unpinTabWithIdentifier:(NSString*)identifier;
+
 // Tells the delegate to close the tab with the item identifier `identifier`.
 // `incognito`tracks the incognito state of the tab.
 // `pinned` tracks the pinned state of the tab.
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
index b093a5fe..33c61b1 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
@@ -59,11 +59,11 @@
 @property(nonatomic, assign, getter=isFeedVisible) BOOL feedVisible;
 
 // The view controller representing the NTP feed header.
-@property(nonatomic, assign) FeedHeaderViewController* feedHeaderViewController;
+@property(nonatomic, weak) FeedHeaderViewController* feedHeaderViewController;
 
 // The view controller representing the Feed top section (between the feed
 // header and the feed collection).
-@property(nonatomic, assign) UIViewController* feedTopSectionViewController;
+@property(nonatomic, strong) UIViewController* feedTopSectionViewController;
 
 // Bubble presenter for displaying IPH bubbles relating to the NTP.
 @property(nonatomic, strong) BubblePresenter* bubblePresenter;
diff --git a/ios/chrome/browser/ui/omnibox/popup/autocomplete_suggestion.h b/ios/chrome/browser/ui/omnibox/popup/autocomplete_suggestion.h
index c73c62b..dc84cb23 100644
--- a/ios/chrome/browser/ui/omnibox/popup/autocomplete_suggestion.h
+++ b/ios/chrome/browser/ui/omnibox/popup/autocomplete_suggestion.h
@@ -69,7 +69,7 @@
 @property(nonatomic, readonly) BOOL isTailSuggestion;
 
 /// Common prefix for tail suggestions. Empty otherwise.
-@property(nonatomic, readonly) NSString* commonPrefix;
+@property(nonatomic, readonly, copy) NSString* commonPrefix;
 
 @end
 
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell_unittest.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell_unittest.mm
index d36b9aa..6edcc18c 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell_unittest.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell_unittest.mm
@@ -26,8 +26,8 @@
 @property(nonatomic, assign) NSInteger numberOfLines;
 @property(nonatomic, strong) id<OmniboxIcon> icon;
 @property(nonatomic, assign) BOOL isTailSuggestion;
-@property(nonatomic, assign) NSString* commonPrefix;
-@property(nonatomic, assign) id<OmniboxPedal, OmniboxIcon> pedal;
+@property(nonatomic, copy) NSString* commonPrefix;
+@property(nonatomic, weak) id<OmniboxPedal, OmniboxIcon> pedal;
 @property(nonatomic, strong) NSAttributedString* omniboxPreviewText;
 @property(nonatomic, strong) UIImage* matchTypeIcon;
 @property(nonatomic, getter=isMatchTypeSearch) BOOL matchTypeSearch;
diff --git a/ios/chrome/browser/ui/open_in/open_in_controller.mm b/ios/chrome/browser/ui/open_in/open_in_controller.mm
index 5d2dee92..c451da7 100644
--- a/ios/chrome/browser/ui/open_in/open_in_controller.mm
+++ b/ios/chrome/browser/ui/open_in/open_in_controller.mm
@@ -191,7 +191,7 @@
 @property(nonatomic, assign) CGFloat previousScrollViewOffset;
 
 // The base view controller from which to present UI.
-@property(nonatomic, assign) UIViewController* baseViewController;
+@property(nonatomic, weak) UIViewController* baseViewController;
 
 // Task runner on which file operations should happen.
 @property(nonatomic, assign) scoped_refptr<base::SequencedTaskRunner>
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.h b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.h
index c9e9b509..5e25263 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.h
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.h
@@ -64,7 +64,7 @@
 // The current style of the overscroll actions UI.
 @property(nonatomic, assign) OverscrollStyle style;
 
-@property(nonatomic, assign) id<OverscrollActionsViewDelegate> delegate;
+@property(nonatomic, weak) id<OverscrollActionsViewDelegate> delegate;
 
 // Add a snapshot view on top of the background image view. The previous
 // snapshot view if any will be removed.
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn b/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
index 43cbbe5..c4fc07ca 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
@@ -5,11 +5,13 @@
 import("//build/config/ios/swift_source_set.gni")
 
 source_set("overflow_menu_constants") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "overflow_menu_constants.cc",
     "overflow_menu_constants.h",
+    "overflow_menu_constants.mm",
   ]
   deps = [ "//base" ]
+  frameworks = [ "UIKit.framework" ]
 }
 
 source_set("overflow_menu") {
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/BUILD.gn b/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/BUILD.gn
index 1aa82a8..7357ec8 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/BUILD.gn
@@ -9,6 +9,7 @@
     "//base",
     "//components/prefs",
     "//ios/chrome/browser/prefs:pref_names",
+    "//ios/chrome/browser/ui/popup_menu/overflow_menu:feature_flags",
     "//ios/chrome/browser/ui/popup_menu/overflow_menu:overflow_menu_constants",
     "//ios/chrome/browser/ui/popup_menu/overflow_menu:overflow_menu_swift",
   ]
@@ -20,10 +21,13 @@
   deps = [
     ":destination_usage_history",
     "//base",
+    "//base/test:test_support",
     "//components/prefs",
     "//components/prefs:test_support",
     "//ios/chrome/browser/prefs:pref_names",
+    "//ios/chrome/browser/ui/popup_menu/overflow_menu:feature_flags",
     "//ios/chrome/browser/ui/popup_menu/overflow_menu:overflow_menu_constants",
+    "//ios/chrome/browser/ui/popup_menu/overflow_menu:overflow_menu_swift",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.h b/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.h
index cb0dedd3..ab3854f9 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.h
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.h
@@ -41,6 +41,10 @@
         (std::vector<overflow_menu::Destination>&)previousRanking
          numAboveFoldDestinations:(int)numAboveFoldDestinations;
 
+// [Publicly exposed for testing purposes only]
+// Fetches the current ranking saved in prefs and returns it.
+- (const base::Value::List*)fetchCurrentRanking;
+
 // Designated initializer. Initializes with `prefService`.
 - (instancetype)initWithPrefService:(PrefService*)prefService
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.mm b/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.mm
index 996392a..f2a2416 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.mm
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.mm
@@ -4,7 +4,9 @@
 
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.h"
 
+#import <algorithm>
 #import <ostream>
+#import <set>
 
 #import "base/ranges/algorithm.h"
 #import "base/strings/string_number_conversions.h"
@@ -13,7 +15,9 @@
 #import "components/prefs/pref_service.h"
 #import "components/prefs/scoped_user_pref_update.h"
 #import "ios/chrome/browser/prefs/pref_names.h"
+#import "ios/chrome/browser/ui/popup_menu/overflow_menu/feature_flags.h"
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.h"
+#import "ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_swift.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -25,6 +29,11 @@
 // during the presentation of the overflow menu.
 constexpr int kDataExpirationWindow = 365;  // days (inclusive)
 
+// kNewDestinationsInsertionIndex represents the index new destinations are
+// inserted into the current ranking. Assumes the overflow menu carousel always
+// has at least four items in it.
+constexpr int kNewDestinationsInsertionIndex = 3;
+
 // kRecencyWindow represents the number of days before the present where usage
 // is considered recent.
 constexpr int kRecencyWindow = 7;  // days (inclusive)
@@ -40,6 +49,9 @@
 // The dictionary key used for storing rankings.
 const char kRankingKey[] = "ranking";
 
+// TODO(crbug.com/989694): Remove `kDefaultRanking` below after feature
+// `kSmartSortingPriceTrackingDestination` fully launches.
+
 // The default destinations ranking, based on statistical usage of the old
 // overflow menu.
 const overflow_menu::Destination kDefaultRanking[] = {
@@ -152,6 +164,21 @@
   return vec;
 }
 
+// Converts base::Value::List ranking into std::set ranking.
+std::set<overflow_menu::Destination> Set(const base::Value::List& ranking) {
+  std::set<overflow_menu::Destination> set;
+
+  for (auto&& rank : ranking) {
+    if (!rank.is_string()) {
+      NOTREACHED();
+    }
+
+    set.insert(overflow_menu::DestinationForStringName(rank.GetString()));
+  }
+
+  return set;
+}
+
 // Converts iterable of overflow_menu::Destination `ranking` into
 // base::Value::List ranking.
 template <typename Range>
@@ -165,6 +192,21 @@
   return list;
 }
 
+// Returns the difference between two vectors. Given vectors `x` and `y`,
+// returns all elements present in `x`, but not in `y`.
+template <typename T>
+std::vector<T> FindDiff(std::vector<T> x, std::vector<T> y) {
+  std::vector<T> diff;
+
+  std::sort(x.begin(), x.end());
+  std::sort(y.begin(), y.end());
+
+  std::set_difference(x.begin(), x.end(), y.begin(), y.end(),
+                      std::back_inserter(diff));
+
+  return diff;
+}
+
 }  // namespace
 
 // Tracks destination usage from the new overflow menu and implements a frecency
@@ -255,6 +297,7 @@
 
   const base::Value::Dict& history =
       self.prefService->GetDict(prefs::kOverflowMenuDestinationUsageHistory);
+
   const std::string path = base::NumberToString(TodaysDay()) + "." +
                            overflow_menu::StringNameForDestination(destination);
 
@@ -262,11 +305,24 @@
 
   ScopedDictPrefUpdate update(self.prefService,
                               prefs::kOverflowMenuDestinationUsageHistory);
+
   update->SetByDottedPath(path, numClicks);
 
-  // User's very first time using Smart Sorting.
-  if (history.size() == 0)
-    [self injectDefaultNumClicksForAllDestinations];
+  if (IsSmartSortingPriceTrackingDestinationEnabled()) {
+    ScopedListPrefUpdate newDestinationsUpdate(
+        self.prefService, prefs::kOverflowMenuNewDestinations);
+
+    newDestinationsUpdate->EraseValue(
+        base::Value(overflow_menu::StringNameForDestination(destination)));
+  } else {
+    // TODO(crbug.com/989694): Remove this code and surrounding else-check after
+    // feature `kSmartSortingPriceTrackingDestination` fully launches.
+
+    // User's very first time using Smart Sorting.
+    if (history.size() == 0) {
+      [self injectDefaultNumClicksForAllDestinations];
+    }
+  }
 
   // Calculate new ranking and store to prefs; Calculate the new ranking
   // ahead of time so overflow menu presentation needn't run ranking algorithm
@@ -280,6 +336,9 @@
 
 #pragma mark - Private
 
+// TODO(crbug.com/989694): Remove `injectDefaultNumClicksForAllDestinations`
+// below after feature `kSmartSortingPriceTrackingDestination` fully launches.
+
 // Injects a default number of clicks for all destinations in the history
 // dictonary.
 - (void)injectDefaultNumClicksForAllDestinations {
@@ -307,6 +366,67 @@
   }
 }
 
+// Adds `destinations` to the locally-stored usage history with a
+// calculated number of initial clicks.
+//
+// NOTE: This method skips seeding history for destinations that already exist
+// in the usage history.
+- (void)seedHistoryWithDestinations:
+    (std::vector<overflow_menu::Destination>&)destinations {
+  // Exit early if there's no pref service. May happen during the application
+  // shutdown.
+  if (!self.prefService) {
+    return;
+  }
+
+  DCHECK_GT(kDampening, 1.0);
+  DCHECK_GT(kInitialBufferNumClicks, 1);
+
+  int defaultNumClicks =
+      (kInitialBufferNumClicks - 1) * (kDampening - 1.0) * 100.0;
+
+  std::string today = base::NumberToString(TodaysDay());
+
+  ScopedDictPrefUpdate update(self.prefService,
+                              prefs::kOverflowMenuDestinationUsageHistory);
+
+  const base::Value::Dict& history =
+      self.prefService->GetDict(prefs::kOverflowMenuDestinationUsageHistory);
+
+  const base::Value::Dict flattenedHistory =
+      [self flattenedHistoryWithinWindow:kDataExpirationWindow];
+
+  for (overflow_menu::Destination destination : destinations) {
+    std::string destinationName =
+        overflow_menu::StringNameForDestination(destination);
+
+    // Does not seed history for destinations that already exist in the usage
+    // history.
+    if (!flattenedHistory.Find(destinationName)) {
+      const std::string path = today + "." + destinationName;
+      update->SetByDottedPath(
+          path,
+          history.FindIntByDottedPath(path).value_or(0) + defaultNumClicks);
+    }
+  }
+}
+
+// Updates the locally-stored ranking to `ranking`.
+- (void)updateStoredRanking:(std::vector<overflow_menu::Destination>&)ranking {
+  // Exit early if there's no pref service. May happen during the application
+  // shutdown.
+  if (!self.prefService) {
+    return;
+  }
+
+  ScopedDictPrefUpdate update(self.prefService,
+                              prefs::kOverflowMenuDestinationUsageHistory);
+
+  base::Value::List newRanking = List(ranking);
+
+  update->Set(kRankingKey, std::move(newRanking));
+}
+
 // Delete expired usage data (data older than `kDataExpirationWindow` days) and
 // saves back to prefs. Returns true if expired usage data was found/removed,
 // false otherwise.
@@ -326,8 +446,9 @@
     if (day == kRankingKey)
       continue;
 
-    if (!ValidDay(day, kDataExpirationWindow))
+    if (!ValidDay(day, kDataExpirationWindow)) {
       prunedHistory.Remove(day);
+    }
   }
 
   self.prefService->SetDict(prefs::kOverflowMenuDestinationUsageHistory,
@@ -347,12 +468,122 @@
   return history.FindList(kRankingKey);
 }
 
+// Compares the current list of carousel items to the current ranking.
+//
+// When the carousel has destinations not found in the current ranking, those
+// destinations are considered new. New destinations are inserted into the
+// current ranking starting at `kNewDestinationsInsertionIndex` and seeded with
+// usage history.
+//
+// When the current ranking has destinations not found in carousel, those
+// destinations are considered removed. Removed destinations are removed from
+// the current ranking.
+//
+// This method tracks new and removed destinations via Pref lists.
+- (void)runHistoryDiagnostic:
+    (std::vector<overflow_menu::Destination>&)currentDestinations {
+  // Exit early if there's no pref service. May happen during the application
+  // shutdown.
+  if (!self.prefService) {
+    return;
+  }
+  const base::Value::List* storedRanking = [self fetchCurrentRanking];
+
+  // If `currentRanking` is invalid, this is the user's first time using Smart
+  // Sorting. This means no ranking or usage history exist on the device, yet,
+  // so a ranking and usage history must be created and seeded.
+  if (!storedRanking) {
+    [self updateStoredRanking:currentDestinations];
+    [self seedHistoryWithDestinations:currentDestinations];
+
+    return;
+  }
+
+  std::vector<overflow_menu::Destination> currentRanking =
+      Vector(storedRanking);
+
+  std::vector<overflow_menu::Destination> newDestinations =
+      FindDiff(currentDestinations, currentRanking);
+
+  [self seedHistoryWithDestinations:newDestinations];
+
+  ScopedListPrefUpdate newDestinationsUpdate(
+      self.prefService, prefs::kOverflowMenuNewDestinations);
+
+  // Make other parts of Smart Sorting infrastructure aware of the newly added
+  // destinations. Newly added destinations are those that exist in the
+  // carousel, but don't exist in the current ranking.
+  for (overflow_menu::Destination newDestination : newDestinations) {
+    newDestinationsUpdate->EraseValue(
+        base::Value(overflow_menu::StringNameForDestination(newDestination)));
+    newDestinationsUpdate->Append(
+        base::Value(overflow_menu::StringNameForDestination(newDestination)));
+  }
+
+  std::vector<overflow_menu::Destination> removedDestinations =
+      FindDiff(currentRanking, currentDestinations);
+
+  std::set<overflow_menu::Destination> removed(removedDestinations.begin(),
+                                               removedDestinations.end());
+
+  // Newly-added and newly-removed destinations need to be added to, and removed
+  // from, the current ranking, respectively. This ensures the logic for
+  // detecting returning destinations works in future evaluations.
+  std::vector<overflow_menu::Destination> updatedRanking;
+
+  for (overflow_menu::Destination destination : currentRanking) {
+    if (!removed.count(destination)) {
+      updatedRanking.push_back(destination);
+    }
+  }
+
+  DCHECK_GE(currentDestinations.size(), size_t(4));
+
+  auto pos =
+      updatedRanking.begin() +
+      std::max(0, std::min(kNewDestinationsInsertionIndex,
+                           static_cast<int>(currentDestinations.size()) - 1));
+  updatedRanking.insert(pos, newDestinations.begin(), newDestinations.end());
+
+  [self updateStoredRanking:updatedRanking];
+}
+
 // Fetches the current ranking stored in Chrome Prefs and returns a sorted list
 // of OverflowMenuDestination* which match the ranking.
 - (NSArray<OverflowMenuDestination*>*)generateDestinationsList:
     (NSArray<OverflowMenuDestination*>*)unrankedDestinations {
-  return [self destinationList:[self fetchCurrentRanking]
-                       options:unrankedDestinations];
+  if (IsSmartSortingPriceTrackingDestinationEnabled()) {
+    std::vector<overflow_menu::Destination> destinations;
+
+    for (OverflowMenuDestination* destination : unrankedDestinations) {
+      overflow_menu::Destination currDestination =
+          overflow_menu::DestinationForNSStringName(
+              destination.destinationName);
+
+      destinations.push_back(currDestination);
+    }
+
+    [self runHistoryDiagnostic:destinations];
+
+    NSMutableArray<OverflowMenuDestination*>* carouselItems =
+        [[self destinationList:[self fetchCurrentRanking]
+                       options:unrankedDestinations] mutableCopy];
+
+    std::set<overflow_menu::Destination> newDestinations =
+        Set(self.prefService->GetList(prefs::kOverflowMenuNewDestinations));
+
+    for (OverflowMenuDestination* carouselItem : carouselItems) {
+      if (newDestinations.count(overflow_menu::DestinationForNSStringName(
+              carouselItem.destinationName))) {
+        carouselItem.badge = BadgeTypeNewLabel;
+      }
+    }
+
+    return carouselItems;
+  } else {
+    return [self destinationList:[self fetchCurrentRanking]
+                         options:unrankedDestinations];
+  }
 }
 
 // Runs the ranking algorithm given a `previousRanking`. If `previousRanking` is
@@ -361,7 +592,13 @@
 - (const base::Value::List)calculateNewRanking:
                                (const base::Value::List*)previousRanking
                       numAboveFoldDestinations:(int)numAboveFoldDestinations {
-  if (!previousRanking) {
+  if (IsSmartSortingPriceTrackingDestinationEnabled()) {
+    DCHECK_NE(previousRanking, nullptr);
+  }
+
+  // TODO(crbug.com/1405245): Remove if-else check below after feature
+  // `kSmartSortingPriceTrackingDestination` fully launches.
+  if (!IsSmartSortingPriceTrackingDestinationEnabled() && !previousRanking) {
     return List(kDefaultRanking);
   }
 
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history_unittest.mm b/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history_unittest.mm
index 7dc7d62..7508485 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history_unittest.mm
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history_unittest.mm
@@ -4,12 +4,16 @@
 
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/destination_usage_history/destination_usage_history.h"
 #import "base/strings/string_number_conversions.h"
+#import "base/strings/sys_string_conversions.h"
+#import "base/test/scoped_feature_list.h"
 #import "base/time/time.h"
 #import "base/values.h"
 #import "components/prefs/pref_registry_simple.h"
 #import "components/prefs/testing_pref_service.h"
 #import "ios/chrome/browser/prefs/pref_names.h"
+#import "ios/chrome/browser/ui/popup_menu/overflow_menu/feature_flags.h"
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.h"
+#import "ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_swift.h"
 #import "testing/platform_test.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -24,6 +28,42 @@
   return (base::Time::Now() - base::Time::UnixEpoch()).InDays();
 }
 
+// Converts base::Value::List* ranking into
+// std::vector<overflow_menu::Destination> ranking.
+std::vector<overflow_menu::Destination> Vector(
+    const base::Value::List* ranking) {
+  std::vector<overflow_menu::Destination> vec;
+
+  if (!ranking) {
+    return vec;
+  }
+
+  for (auto&& rank : *ranking) {
+    if (!rank.is_string()) {
+      NOTREACHED();
+    }
+
+    vec.push_back(overflow_menu::DestinationForStringName(rank.GetString()));
+  }
+
+  return vec;
+}
+
+// Converts NSArray<OverflowMenuDestination*>* list of overflow menu
+// destinations into their std::vector<overflow_menu::Destination> list of
+// destination IDs.
+std::vector<overflow_menu::Destination> Vector(
+    NSArray<OverflowMenuDestination*>* destinations) {
+  std::vector<overflow_menu::Destination> vec;
+
+  for (OverflowMenuDestination* destination in destinations) {
+    vec.push_back(
+        overflow_menu::DestinationForNSStringName(destination.destinationName));
+  }
+
+  return vec;
+}
+
 }  // namespace
 
 class DestinationUsageHistoryTest : public PlatformTest {
@@ -37,19 +77,26 @@
   }
 
   // Creates CreateDestinationUsageHistory with empty pref data.
-  DestinationUsageHistory* CreateDestinationUsageHistory() {
+  DestinationUsageHistory* CreateDestinationUsageHistory(
+      NSArray<OverflowMenuDestination*>* default_destinations) {
     CreatePrefs();
 
     destination_usage_history_ =
         [[DestinationUsageHistory alloc] initWithPrefService:prefs_.get()];
 
+    if (IsSmartSortingPriceTrackingDestinationEnabled()) {
+      [destination_usage_history_
+          generateDestinationsList:default_destinations];
+    }
+
     return destination_usage_history_;
   }
 
   // Creates CreateDestinationUsageHistory with past data and `ranking`.
   DestinationUsageHistory* CreateDestinationUsageHistoryWithData(
       std::vector<overflow_menu::Destination>& ranking,
-      base::Value::Dict& history) {
+      base::Value::Dict& history,
+      NSArray<OverflowMenuDestination*>* default_destinations) {
     base::Value::List prevRanking = RankingAsListValue(ranking);
 
     CreatePrefsWithData(prevRanking, history);
@@ -57,6 +104,11 @@
     destination_usage_history_ =
         [[DestinationUsageHistory alloc] initWithPrefService:prefs_.get()];
 
+    if (IsSmartSortingPriceTrackingDestinationEnabled()) {
+      [destination_usage_history_
+          generateDestinationsList:default_destinations];
+    }
+
     return destination_usage_history_;
   }
 
@@ -65,6 +117,8 @@
     prefs_ = std::make_unique<TestingPrefServiceSimple>();
     prefs_->registry()->RegisterDictionaryPref(
         prefs::kOverflowMenuDestinationUsageHistory, PrefRegistry::LOSSY_PREF);
+    prefs_->registry()->RegisterListPref(prefs::kOverflowMenuNewDestinations,
+                                         PrefRegistry::LOSSY_PREF);
   }
 
   // Helper for CreateDestinationUsageHistoryWithData(), inserts day history
@@ -113,6 +167,57 @@
                          // return here.
   }
 
+  OverflowMenuDestination* CreateOverflowMenuDestination(
+      overflow_menu::Destination destination) {
+    OverflowMenuDestination* result = [[OverflowMenuDestination alloc]
+                   initWithName:@"Foobar"
+                          image:[UIImage
+                                    imageNamed:
+                                        @"overflow_menu_destination_settings"]
+        accessibilityIdentifier:@"Foobar"
+             enterpriseDisabled:NO
+                        handler:^{
+                            // Do nothing
+                        }];
+
+    result.destinationName = base::SysUTF8ToNSString(
+        overflow_menu::StringNameForDestination(destination));
+
+    return result;
+  }
+
+  NSArray<OverflowMenuDestination*>* SampleDestinations() {
+    OverflowMenuDestination* bookmarksDestination =
+        CreateOverflowMenuDestination(overflow_menu::Destination::Bookmarks);
+    OverflowMenuDestination* historyDestination =
+        CreateOverflowMenuDestination(overflow_menu::Destination::History);
+    OverflowMenuDestination* readingListDestination =
+        CreateOverflowMenuDestination(overflow_menu::Destination::ReadingList);
+    OverflowMenuDestination* passwordsDestination =
+        CreateOverflowMenuDestination(overflow_menu::Destination::Passwords);
+    OverflowMenuDestination* downloadsDestination =
+        CreateOverflowMenuDestination(overflow_menu::Destination::Downloads);
+    OverflowMenuDestination* recentTabsDestination =
+        CreateOverflowMenuDestination(overflow_menu::Destination::RecentTabs);
+    OverflowMenuDestination* siteInfoDestination =
+        CreateOverflowMenuDestination(overflow_menu::Destination::SiteInfo);
+    OverflowMenuDestination* settingsDestination =
+        CreateOverflowMenuDestination(overflow_menu::Destination::Settings);
+
+    NSArray<OverflowMenuDestination*>* destinations = @[
+      bookmarksDestination,
+      historyDestination,
+      readingListDestination,
+      passwordsDestination,
+      downloadsDestination,
+      recentTabsDestination,
+      siteInfoDestination,
+      settingsDestination,
+    ];
+
+    return destinations;
+  }
+
   std::unique_ptr<TestingPrefServiceSimple> prefs_;
   DestinationUsageHistory* destination_usage_history_;
   static constexpr int numAboveFoldDestinations = 5;
@@ -122,7 +227,7 @@
 // specified Pref service.
 TEST_F(DestinationUsageHistoryTest, InitWithPrefService) {
   DestinationUsageHistory* destination_usage_history =
-      CreateDestinationUsageHistory();
+      CreateDestinationUsageHistory(SampleDestinations());
 
   PrefService* pref_service = destination_usage_history.prefService;
 
@@ -156,7 +261,8 @@
 
   // Create DestinationUsageHistory.
   DestinationUsageHistory* destination_usage_history =
-      CreateDestinationUsageHistoryWithData(ranking, history);
+      CreateDestinationUsageHistoryWithData(ranking, history,
+                                            SampleDestinations());
 
   // Grab its pref service.
   PrefService* pref_service = destination_usage_history.prefService;
@@ -172,7 +278,7 @@
 // prefService.
 TEST_F(DestinationUsageHistoryTest, DestroysPrefServiceOnDisconnect) {
   DestinationUsageHistory* destination_usage_history =
-      CreateDestinationUsageHistory();
+      CreateDestinationUsageHistory(SampleDestinations());
 
   [destination_usage_history disconnect];
 
@@ -183,7 +289,7 @@
 // Prefs.
 TEST_F(DestinationUsageHistoryTest, HandlesNewDestinationClickAndAddToPrefs) {
   DestinationUsageHistory* destination_usage_history =
-      CreateDestinationUsageHistory();
+      CreateDestinationUsageHistory(SampleDestinations());
 
   // Click bookmarks destination.
   [destination_usage_history
@@ -210,7 +316,7 @@
 // of clicks.
 TEST_F(DestinationUsageHistoryTest, InjectsDefaultNumClicksForAllDestinations) {
   DestinationUsageHistory* destination_usage_history =
-      CreateDestinationUsageHistory();
+      CreateDestinationUsageHistory(SampleDestinations());
 
   // Click bookmarks destination.
   [destination_usage_history
@@ -272,7 +378,8 @@
 
   // Create DestinationUsageHistory.
   DestinationUsageHistory* destination_usage_history =
-      CreateDestinationUsageHistoryWithData(ranking, prefHistory);
+      CreateDestinationUsageHistoryWithData(ranking, prefHistory,
+                                            SampleDestinations());
 
   // Click bookmarks destination.
   [destination_usage_history
@@ -309,7 +416,8 @@
   base::Value::Dict history;
 
   DestinationUsageHistory* destination_usage_history =
-      CreateDestinationUsageHistoryWithData(ranking, history);
+      CreateDestinationUsageHistoryWithData(ranking, history,
+                                            SampleDestinations());
 
   // Click bookmarks Reading List (currently in ranking position 3) five times.
   [destination_usage_history
@@ -369,7 +477,8 @@
   history.Set(base::NumberToString(TodaysDay()), std::move(dayHistory));
 
   DestinationUsageHistory* destination_usage_history =
-      CreateDestinationUsageHistoryWithData(ranking, history);
+      CreateDestinationUsageHistoryWithData(ranking, history,
+                                            SampleDestinations());
 
   // Click Recent Tabs (currently in ranking position 6) once.
   [destination_usage_history
@@ -432,7 +541,8 @@
               std::move(expired_day_history));
 
   DestinationUsageHistory* destination_usage_history =
-      CreateDestinationUsageHistoryWithData(ranking, history);
+      CreateDestinationUsageHistoryWithData(ranking, history,
+                                            SampleDestinations());
 
   // Click destination to trigger ranking algorithm which removes expired data.
   [destination_usage_history
@@ -452,3 +562,155 @@
                                          base::NumberToString(TodaysDay())};
   ASSERT_EQ(expected_keys, seen_keys);
 }
+
+TEST_F(DestinationUsageHistoryTest, InsertsNewDestinationInMiddleOfRanking) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      kSmartSortingPriceTrackingDestination);
+
+  NSArray<OverflowMenuDestination*>* allDestinations = SampleDestinations();
+  NSArray<OverflowMenuDestination*>* currentDestinations = @[
+    allDestinations[0],
+    allDestinations[1],
+    allDestinations[2],
+    allDestinations[3],
+    allDestinations[4],
+    allDestinations[5],
+    allDestinations[6],
+  ];
+
+  // Creates `DestinationUsageHistory` with initial ranking
+  // `currentDestinations`.
+  DestinationUsageHistory* destinationUsageHistory =
+      CreateDestinationUsageHistory(currentDestinations);
+
+  std::vector<overflow_menu::Destination> currentRanking =
+      Vector(currentDestinations);
+  std::vector<overflow_menu::Destination> storedRanking =
+      Vector([destinationUsageHistory fetchCurrentRanking]);
+
+  ASSERT_EQ(currentRanking, storedRanking);
+
+  // Same as `currentDestinations`, but has a new element, `allDestinations[7]`,
+  // inserted starting at position 4 in the carousel (this is the expected
+  // behavior defined by product).
+  NSArray<OverflowMenuDestination*>* updatedDestinations = @[
+    allDestinations[0],
+    allDestinations[1],
+    allDestinations[2],
+    // New destination
+    allDestinations[7],
+    allDestinations[3],
+    allDestinations[4],
+    allDestinations[5],
+    allDestinations[6],
+  ];
+
+  [destinationUsageHistory generateDestinationsList:updatedDestinations];
+
+  std::vector<overflow_menu::Destination> updatedRanking =
+      Vector(updatedDestinations);
+  std::vector<overflow_menu::Destination> updatedStoredRanking =
+      Vector([destinationUsageHistory fetchCurrentRanking]);
+
+  ASSERT_EQ(updatedRanking, updatedStoredRanking);
+}
+
+TEST_F(DestinationUsageHistoryTest, InsertsNewDestinationsInMiddleOfRanking) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      kSmartSortingPriceTrackingDestination);
+
+  NSArray<OverflowMenuDestination*>* allDestinations = SampleDestinations();
+  NSArray<OverflowMenuDestination*>* currentDestinations = @[
+    allDestinations[0],
+    allDestinations[1],
+    allDestinations[2],
+    allDestinations[3],
+    allDestinations[4],
+    allDestinations[5],
+  ];
+
+  // Creates `DestinationUsageHistory` with initial ranking
+  // `currentDestinations`.
+  DestinationUsageHistory* destinationUsageHistory =
+      CreateDestinationUsageHistory(currentDestinations);
+
+  std::vector<overflow_menu::Destination> currentRanking =
+      Vector([destinationUsageHistory fetchCurrentRanking]);
+  std::vector<overflow_menu::Destination> storedRanking =
+      Vector([destinationUsageHistory fetchCurrentRanking]);
+
+  ASSERT_EQ(currentRanking, storedRanking);
+
+  // Same as `currentDestinations`, but has new elements (`allDestinations[6]`
+  // and `allDestinations[7]`) inserted starting at position 4 in the carousel
+  // (this is the expected behavior defined by product).
+  NSArray<OverflowMenuDestination*>* updatedDestinations = @[
+    allDestinations[0],
+    allDestinations[1],
+    allDestinations[2],
+    // New destinations (start)
+    allDestinations[6],
+    allDestinations[7],
+    // New destinations (end)
+    allDestinations[3],
+    allDestinations[4],
+    allDestinations[5],
+  ];
+
+  [destinationUsageHistory generateDestinationsList:updatedDestinations];
+
+  std::vector<overflow_menu::Destination> updatedRanking =
+      Vector(updatedDestinations);
+  std::vector<overflow_menu::Destination> updatedStoredRanking =
+      Vector([destinationUsageHistory fetchCurrentRanking]);
+
+  ASSERT_EQ(updatedRanking, updatedStoredRanking);
+}
+
+TEST_F(DestinationUsageHistoryTest, InsertsAndRemovesNewDestinationsInRanking) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      kSmartSortingPriceTrackingDestination);
+
+  NSArray<OverflowMenuDestination*>* allDestinations = SampleDestinations();
+  NSArray<OverflowMenuDestination*>* currentDestinations = @[
+    allDestinations[0],
+    allDestinations[1],
+    allDestinations[2],
+    allDestinations[3],
+    allDestinations[4],
+    allDestinations[5],
+  ];
+
+  DestinationUsageHistory* destinationUsageHistory =
+      CreateDestinationUsageHistory(currentDestinations);
+
+  std::vector<overflow_menu::Destination> currentRanking =
+      Vector(currentDestinations);
+  std::vector<overflow_menu::Destination> storedRanking =
+      Vector([destinationUsageHistory fetchCurrentRanking]);
+
+  ASSERT_EQ(currentRanking, storedRanking);
+
+  NSArray<OverflowMenuDestination*>* updatedDestinations = @[
+    allDestinations[2],
+    allDestinations[3],
+    allDestinations[4],
+    // New destinations (start)
+    allDestinations[6],
+    allDestinations[7],
+    // New destinations (end)
+    allDestinations[5],
+  ];
+
+  [destinationUsageHistory generateDestinationsList:updatedDestinations];
+
+  std::vector<overflow_menu::Destination> updatedRanking =
+      Vector(updatedDestinations);
+  std::vector<overflow_menu::Destination> updatedStoredRanking =
+      Vector([destinationUsageHistory fetchCurrentRanking]);
+
+  ASSERT_EQ(updatedRanking, updatedStoredRanking);
+}
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.h b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.h
index 2cdf8c7..087fbac 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.h
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.h
@@ -5,7 +5,9 @@
 #ifndef IOS_CHROME_BROWSER_UI_POPUP_MENU_OVERFLOW_MENU_OVERFLOW_MENU_CONSTANTS_H_
 #define IOS_CHROME_BROWSER_UI_POPUP_MENU_OVERFLOW_MENU_OVERFLOW_MENU_CONSTANTS_H_
 
-#include <string>
+#import <UIKit/UIKit.h>
+
+#import <string>
 
 namespace overflow_menu {
 enum class Destination {
@@ -25,10 +27,18 @@
 // overflow_menu::Destination enum.
 Destination DestinationForStringName(std::string destination);
 
+// Ingests `destination` NSString* representation and returns corresponding
+// overflow_menu::Destination enum.
+Destination DestinationForNSStringName(NSString* destination);
+
 // Ingests overflow_menu::Destination `destination` and returns its string
 // representation.
 std::string StringNameForDestination(Destination destination);
 
+// Ingests overflow_menu::Destination `destination` and returns its NSString*
+// representation.
+NSString* NSStringNameForDestination(Destination destination);
+
 // Ingests overflow_menu::Destination `destination` and records the
 // corresponding UMA action.
 void RecordUmaActionForDestination(Destination destination);
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.cc b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.mm
similarity index 87%
rename from ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.cc
rename to ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.mm
index d5f5db7..7916c17 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.cc
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.mm
@@ -4,9 +4,14 @@
 
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_constants.h"
 
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#include "base/notreached.h"
+#import "base/metrics/user_metrics.h"
+#import "base/metrics/user_metrics_action.h"
+#import "base/notreached.h"
+#import "base/strings/sys_string_conversions.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
 
 namespace overflow_menu {
 // WARNING - PLEASE READ: Sadly, we cannot switch over strings in C++, so be
@@ -38,6 +43,12 @@
   }
 }
 
+Destination DestinationForNSStringName(NSString* destination) {
+  std::string name = base::SysNSStringToUTF8(destination);
+
+  return DestinationForStringName(name);
+}
+
 std::string StringNameForDestination(Destination destination) {
   switch (destination) {
     case overflow_menu::Destination::Bookmarks:
@@ -63,6 +74,12 @@
   }
 }
 
+NSString* NSStringNameForDestination(Destination destination) {
+  std::string name = StringNameForDestination(destination);
+
+  return base::SysUTF8ToNSString(name);
+}
+
 void RecordUmaActionForDestination(Destination destination) {
   switch (destination) {
     case Destination::Bookmarks:
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h
index 1490d40..c6335b3 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.h
@@ -59,7 +59,7 @@
 @property(nonatomic, weak) id<RecentTabsMenuProvider> menuProvider;
 
 // Multi-window session for this vc's recent tabs.
-@property(nonatomic, assign) UISceneSession* session;
+@property(nonatomic, weak) UISceneSession* session;
 
 // Initializers.
 - (instancetype)init NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller.mm
index d246143..d39723d 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller.mm
@@ -306,7 +306,7 @@
 
 // Returns initialized tableViewItem with passed arguments.
 - (AutofillEditItem*)createTableViewItemWithType:(NSInteger)itemType
-                                   textFieldName:(NSString*)textFieldName
+                              fieldNameLabelText:(NSString*)fieldNameLabelText
                                   textFieldValue:(NSString*)textFieldValue
                             textFieldPlaceholder:(NSString*)textFieldPlaceholder
                                     keyboardType:(UIKeyboardType)keyboardType
@@ -314,7 +314,7 @@
                                       (AutofillUIType)autofillUIType {
   AutofillEditItem* item = [[AutofillEditItem alloc] initWithType:itemType];
   item.delegate = self;
-  item.textFieldName = textFieldName;
+  item.fieldNameLabelText = fieldNameLabelText;
   item.textFieldValue = textFieldValue;
   item.textFieldPlaceholder = textFieldPlaceholder;
   item.keyboardType = keyboardType;
@@ -327,7 +327,7 @@
 - (AutofillEditItem*)expirationYearItem {
   AutofillEditItem* expirationYearItem =
       [self createTableViewItemWithType:ItemTypeExpirationYear
-                          textFieldName:l10n_util::GetNSString(
+                     fieldNameLabelText:l10n_util::GetNSString(
                                             IDS_IOS_AUTOFILL_EXP_YEAR)
                          textFieldValue:self.expirationYear
                    textFieldPlaceholder:
@@ -341,7 +341,7 @@
 - (AutofillEditItem*)expirationMonthItem {
   AutofillEditItem* expirationMonthItem =
       [self createTableViewItemWithType:ItemTypeExpirationMonth
-                          textFieldName:l10n_util::GetNSString(
+                     fieldNameLabelText:l10n_util::GetNSString(
                                             IDS_IOS_AUTOFILL_EXP_MONTH)
                          textFieldValue:self.expirationMonth
                    textFieldPlaceholder:
@@ -355,7 +355,7 @@
 - (AutofillEditItem*)cardNumberItem {
   AutofillEditItem* cardNumberItem =
       [self createTableViewItemWithType:ItemTypeCardNumber
-                          textFieldName:l10n_util::GetNSString(
+                     fieldNameLabelText:l10n_util::GetNSString(
                                             IDS_IOS_AUTOFILL_CARD_NUMBER)
                          textFieldValue:self.cardNumber
                    textFieldPlaceholder:
@@ -369,7 +369,7 @@
 - (AutofillEditItem*)cardHolderNameItem {
   AutofillEditItem* cardHolderNameItem =
       [self createTableViewItemWithType:ItemTypeName
-                          textFieldName:l10n_util::GetNSString(
+                     fieldNameLabelText:l10n_util::GetNSString(
                                             IDS_IOS_AUTOFILL_CARDHOLDER)
                          textFieldValue:self.cardHolderName
                    textFieldPlaceholder:
@@ -383,7 +383,7 @@
 - (AutofillEditItem*)cardNicknameItem {
   AutofillEditItem* cardNicknameItem =
       [self createTableViewItemWithType:ItemTypeCardNickname
-                          textFieldName:l10n_util::GetNSString(
+                     fieldNameLabelText:l10n_util::GetNSString(
                                             IDS_IOS_AUTOFILL_NICKNAME)
                          textFieldValue:self.cardNickname
                    textFieldPlaceholder:
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
index 705b85ec..d024991 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
@@ -349,7 +349,7 @@
 - (AutofillEditItem*)cardholderNameItem:(bool)isEditing {
   AutofillEditItem* cardholderNameItem =
       [[AutofillEditItem alloc] initWithType:ItemTypeCardholderName];
-  cardholderNameItem.textFieldName =
+  cardholderNameItem.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARDHOLDER);
   cardholderNameItem.textFieldValue = autofill::GetCreditCardName(
       _creditCard, GetApplicationContext()->GetApplicationLocale());
@@ -362,7 +362,7 @@
 - (AutofillEditItem*)cardNumberItem:(bool)isEditing {
   AutofillEditItem* cardNumberItem =
       [[AutofillEditItem alloc] initWithType:ItemTypeCardNumber];
-  cardNumberItem.textFieldName =
+  cardNumberItem.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARD_NUMBER);
   // Never show full card number for Wallet cards, even if copied locally.
   cardNumberItem.textFieldValue =
@@ -386,7 +386,7 @@
 - (AutofillEditItem*)expirationMonthItem:(bool)isEditing {
   AutofillEditItem* expirationMonthItem =
       [[AutofillEditItem alloc] initWithType:ItemTypeExpirationMonth];
-  expirationMonthItem.textFieldName =
+  expirationMonthItem.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_MONTH);
   expirationMonthItem.textFieldValue =
       [NSString stringWithFormat:@"%02d", _creditCard.expiration_month()];
@@ -402,7 +402,7 @@
   // Expiration year.
   AutofillEditItem* expirationYearItem =
       [[AutofillEditItem alloc] initWithType:ItemTypeExpirationYear];
-  expirationYearItem.textFieldName =
+  expirationYearItem.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_EXP_YEAR);
   expirationYearItem.textFieldValue =
       [NSString stringWithFormat:@"%04d", _creditCard.expiration_year()];
@@ -418,7 +418,7 @@
 - (AutofillEditItem*)nicknameItem:(bool)isEditing {
   AutofillEditItem* nicknameItem =
       [[AutofillEditItem alloc] initWithType:ItemTypeNickname];
-  nicknameItem.textFieldName =
+  nicknameItem.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_NICKNAME);
   nicknameItem.textFieldValue =
       autofill::GetCreditCardNicknameString(_creditCard);
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm
index 56d464e..ffae24fb 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_edit_table_view_controller.mm
@@ -155,7 +155,7 @@
 
     AutofillEditItem* item =
         [[AutofillEditItem alloc] initWithType:ItemTypeField];
-    item.textFieldName = l10n_util::GetNSString(field.displayStringID);
+    item.fieldNameLabelText = l10n_util::GetNSString(field.displayStringID);
     item.textFieldValue = base::SysUTF16ToNSString(_autofillProfile.GetInfo(
         autofill::AutofillType(field.autofillType), locale));
     item.autofillUIType = AutofillUITypeFromAutofillType(field.autofillType);
diff --git a/ios/chrome/browser/ui/settings/password/password_details/add_password_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_details/add_password_view_controller.mm
index a88f37b..03f343e4 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/add_password_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/add_password_view_controller.mm
@@ -210,7 +210,8 @@
   TableViewTextEditItem* item =
       [[TableViewTextEditItem alloc] initWithType:ItemTypeWebsite];
   item.textFieldBackgroundColor = [UIColor clearColor];
-  item.textFieldName = l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
+  item.fieldNameLabelText =
+      l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
   item.textFieldEnabled = YES;
   item.autoCapitalizationType = UITextAutocapitalizationTypeNone;
   item.hideIcon = NO;
@@ -225,7 +226,7 @@
   TableViewTextEditItem* item =
       [[TableViewTextEditItem alloc] initWithType:ItemTypeUsername];
   item.textFieldBackgroundColor = [UIColor clearColor];
-  item.textFieldName =
+  item.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME);
   item.textFieldEnabled = YES;
   item.hideIcon = NO;
@@ -240,7 +241,7 @@
   TableViewTextEditItem* item =
       [[TableViewTextEditItem alloc] initWithType:ItemTypePassword];
   item.textFieldBackgroundColor = [UIColor clearColor];
-  item.textFieldName =
+  item.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
   item.textFieldSecureTextEntry = ![self isPasswordShown];
   item.textFieldEnabled = YES;
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
index 3bee782..1ae176e 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
@@ -235,7 +235,7 @@
   TableViewTextEditItem* item = [[TableViewTextEditItem alloc]
       initWithType:PasswordDetailsItemTypeUsername];
   item.textFieldBackgroundColor = [UIColor clearColor];
-  item.textFieldName =
+  item.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME);
   item.textFieldValue = passwordDetails.username;  // Empty for a new form.
   // If password is missing (federated credential) don't allow to edit username.
@@ -258,7 +258,7 @@
   TableViewTextEditItem* item = [[TableViewTextEditItem alloc]
       initWithType:PasswordDetailsItemTypePassword];
   item.textFieldBackgroundColor = [UIColor clearColor];
-  item.textFieldName =
+  item.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
   item.textFieldValue = [self isPasswordShown] || self.tableView.editing
                             ? passwordDetails.password
@@ -300,7 +300,7 @@
   TableViewTextEditItem* item = [[TableViewTextEditItem alloc]
       initWithType:PasswordDetailsItemTypeFederation];
   item.textFieldBackgroundColor = [UIColor clearColor];
-  item.textFieldName =
+  item.fieldNameLabelText =
       l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_FEDERATION);
   item.textFieldValue = passwordDetails.federation;
   item.textFieldEnabled = NO;
@@ -551,7 +551,7 @@
 }
 
 - (void)tableViewItemDidEndEditing:(TableViewTextEditItem*)tableViewItem {
-  if ([tableViewItem.textFieldName
+  if ([tableViewItem.fieldNameLabelText
           isEqualToString:l10n_util::GetNSString(
                               IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD)]) {
     [self checkIfValidPasswords];
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_unittest.mm
index 23fae0d..29f08bd 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller_unittest.mm
@@ -155,7 +155,7 @@
 
 @interface FakeSnackbarImplementation : NSObject <SnackbarCommands>
 
-@property(nonatomic, assign) NSString* snackbarMessage;
+@property(nonatomic, copy) NSString* snackbarMessage;
 
 @end
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_mediator_unittest.mm b/ios/chrome/browser/ui/settings/password/passwords_mediator_unittest.mm
index be880e1c..a99bcab8 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_mediator_unittest.mm
@@ -70,7 +70,7 @@
 // causes the update to occur.
 @property(nonatomic, assign) NSInteger numberOfCallToChangeOnDeviceEncryption;
 
-@property(nonatomic, assign) NSString* detailedText;
+@property(nonatomic, copy) NSString* detailedText;
 
 @end
 
diff --git a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
index 74f3548..0378cb31 100644
--- a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
@@ -319,7 +319,7 @@
 
   TableViewTextEditItem* textEditItem =
       [[TableViewTextEditItem alloc] initWithType:ItemTypeTextEditItem];
-  textEditItem.textFieldName = @"Edit Text Item";
+  textEditItem.fieldNameLabelText = @"Edit Text Item";
   textEditItem.textFieldValue = @" with no icons";
   textEditItem.hideIcon = YES;
   textEditItem.textFieldEnabled = YES;
@@ -327,7 +327,7 @@
 
   TableViewTextEditItem* textEditItemEditIcon =
       [[TableViewTextEditItem alloc] initWithType:ItemTypeTextEditItem];
-  textEditItemEditIcon.textFieldName = @"Edit Text Item";
+  textEditItemEditIcon.fieldNameLabelText = @"Edit Text Item";
   textEditItemEditIcon.textFieldValue = @" with edit icon";
   textEditItemEditIcon.textFieldEnabled = YES;
   [model addItem:textEditItemEditIcon
@@ -335,7 +335,7 @@
 
   TableViewTextEditItem* textEditItemBothIcons =
       [[TableViewTextEditItem alloc] initWithType:ItemTypeTextEditItem];
-  textEditItemBothIcons.textFieldName = @"Edit Text Item";
+  textEditItemBothIcons.fieldNameLabelText = @"Edit Text Item";
   textEditItemBothIcons.textFieldValue = @" with edit and custom icons";
   textEditItemBothIcons.identifyingIcon =
       [UIImage imageNamed:@"table_view_cell_check_mark"];
@@ -345,7 +345,7 @@
 
   TableViewTextEditItem* textEditItemIconButton =
       [[TableViewTextEditItem alloc] initWithType:ItemTypeTextEditItem];
-  textEditItemIconButton.textFieldName = @"Edit Text Item";
+  textEditItemIconButton.fieldNameLabelText = @"Edit Text Item";
   textEditItemIconButton.textFieldValue = @" icon is a button.";
   textEditItemIconButton.identifyingIcon =
       [UIImage imageNamed:@"table_view_cell_check_mark"];
diff --git a/ios/chrome/browser/ui/sharing/activity_services/activities/bookmark_activity.mm b/ios/chrome/browser/ui/sharing/activity_services/activities/bookmark_activity.mm
index 68a6164d..f9395ec 100644
--- a/ios/chrome/browser/ui/sharing/activity_services/activities/bookmark_activity.mm
+++ b/ios/chrome/browser/ui/sharing/activity_services/activities/bookmark_activity.mm
@@ -35,7 +35,7 @@
 // The URL of the page to be bookmarked.
 @property(nonatomic, assign) GURL URL;
 // The title of the page to be bookmarked.
-@property(nonatomic, assign) NSString* title;
+@property(nonatomic, copy) NSString* title;
 // The handler invoked when the activity is performed.
 @property(nonatomic, weak) id<BookmarksCommands> handler;
 // User's preferences service.
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/tab_context_menu_helper.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/tab_context_menu_helper.mm
index 43a43784..c9c51ab 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/tab_context_menu_helper.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_context_menu/tab_context_menu_helper.mm
@@ -120,7 +120,13 @@
 
     if (IsPinnedTabsEnabled()) {
       if (pinned) {
-        // TODO(crbug.com/1382015): Implement this.
+        if ([self.contextMenuDelegate
+                respondsToSelector:@selector(unpinTabWithIdentifier:)]) {
+          [menuElements addObject:[actionFactory actionToUnpinTabWithBlock:^{
+                          [self.contextMenuDelegate
+                              unpinTabWithIdentifier:cell.itemIdentifier];
+                        }]];
+        }
       } else {
         if ([self.contextMenuDelegate
                 respondsToSelector:@selector(pinTabWithIdentifier:)]) {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
index 298d9ca9..730c41f 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
@@ -1168,6 +1168,10 @@
   [self.regularTabsMediator setPinState:YES forItemWithIdentifier:identifier];
 }
 
+- (void)unpinTabWithIdentifier:(NSString*)identifier {
+  [self.pinnedTabsMediator setPinState:NO forItemWithIdentifier:identifier];
+}
+
 - (void)closeTabWithIdentifier:(NSString*)identifier
                      incognito:(BOOL)incognito
                         pinned:(BOOL)pinned {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator_unittest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator_unittest.mm
index 2a599f7d..b3b44861 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator_unittest.mm
@@ -101,7 +101,7 @@
 @interface FakeConsumer : NSObject <TabCollectionConsumer>
 // The fake consumer only keeps the identifiers of items for simplicity
 @property(nonatomic, strong) NSMutableArray<NSString*>* items;
-@property(nonatomic, assign) NSString* selectedItemID;
+@property(nonatomic, copy) NSString* selectedItemID;
 @end
 @implementation FakeConsumer
 @synthesize items = _items;
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h
index 0ac639e9..6e8a5dd 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.h
@@ -25,8 +25,9 @@
 // The delegate for this table view text edit item.
 @property(nonatomic, weak) id<TableViewTextEditItemDelegate> delegate;
 
-// The name of the text field.
-@property(nonatomic, copy) NSString* textFieldName;
+// Name of the text field.
+// Displayed in a label next to the text field.
+@property(nonatomic, copy) NSString* fieldNameLabelText;
 
 // The placeholder of the text field.
 @property(nonatomic, copy) NSString* textFieldPlaceholder;
@@ -82,7 +83,8 @@
 // and a text field.
 @interface TableViewTextEditCell : TableViewCell
 
-// Label at the leading edge of the cell. It displays the item's textFieldName.
+// Label at the leading edge of the cell. It displays the item's
+// fieldNameLabelText.
 @property(nonatomic, strong) UILabel* textLabel;
 
 // Text field at the trailing edge of the cell. It displays the item's
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm
index 1d6fe5b..1739786 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item.mm
@@ -61,7 +61,7 @@
 
   NSString* textLabelFormat = self.required ? @"%@*" : @"%@";
   cell.textLabel.text =
-      [NSString stringWithFormat:textLabelFormat, self.textFieldName];
+      [NSString stringWithFormat:textLabelFormat, self.fieldNameLabelText];
   if (self.textFieldPlaceholder) {
     cell.textField.attributedPlaceholder = [[NSAttributedString alloc]
         initWithString:self.textFieldPlaceholder
@@ -72,9 +72,9 @@
   }
   cell.textField.text = self.textFieldValue;
   cell.textField.secureTextEntry = self.textFieldSecureTextEntry;
-  if (self.textFieldName.length) {
+  if (self.fieldNameLabelText.length) {
     cell.textField.accessibilityIdentifier =
-        [NSString stringWithFormat:@"%@_textField", self.textFieldName];
+        [NSString stringWithFormat:@"%@_textField", self.fieldNameLabelText];
   }
 
   if (self.textFieldBackgroundColor) {
@@ -103,15 +103,15 @@
 
     if (!self.hasValidText) {
       cell.iconView.accessibilityIdentifier =
-          [NSString stringWithFormat:@"%@_errorIcon", self.textFieldName];
+          [NSString stringWithFormat:@"%@_errorIcon", self.fieldNameLabelText];
       [cell setIcon:TableViewTextEditItemIconTypeError];
     } else if (cell.textField.editing && cell.textField.text.length > 0) {
       cell.iconView.accessibilityIdentifier =
-          [NSString stringWithFormat:@"%@_noIcon", self.textFieldName];
+          [NSString stringWithFormat:@"%@_noIcon", self.fieldNameLabelText];
       [cell setIcon:TableViewTextEditItemIconTypeNone];
     } else {
       cell.iconView.accessibilityIdentifier =
-          [NSString stringWithFormat:@"%@_editIcon", self.textFieldName];
+          [NSString stringWithFormat:@"%@_editIcon", self.fieldNameLabelText];
       [cell setIcon:TableViewTextEditItemIconTypeEdit];
     }
   }
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item_unittest.mm
index 59a3dc2e..972b6a7 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item_unittest.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_text_edit_item_unittest.mm
@@ -24,7 +24,7 @@
   NSString* value = @"Value";
   BOOL enabled = NO;
 
-  item.textFieldName = name;
+  item.fieldNameLabelText = name;
   item.textFieldValue = value;
   item.textFieldEnabled = enabled;
 
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_controller.h b/ios/chrome/browser/ui/tabs/tab_strip_controller.h
index c8c8f3a..0ca47d1 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_controller.h
+++ b/ios/chrome/browser/ui/tabs/tab_strip_controller.h
@@ -33,7 +33,7 @@
 @property(nonatomic, assign) NSTimeInterval animationWaitDuration;
 
 // Used to check if the tabstrip is visible before starting an animation.
-@property(nonatomic, assign) id<TabStripPresentation> presentationProvider;
+@property(nonatomic, weak) id<TabStripPresentation> presentationProvider;
 
 // Pan gesture handler for the tab strip.
 @property(nonatomic, weak) ViewRevealingVerticalPanHandler* panGestureHandler;
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_legacy_coordinator.h b/ios/chrome/browser/ui/tabs/tab_strip_legacy_coordinator.h
index bb5dbb3..da0daa6 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_legacy_coordinator.h
+++ b/ios/chrome/browser/ui/tabs/tab_strip_legacy_coordinator.h
@@ -31,7 +31,7 @@
 
 // Provides methods for presenting the tab strip and checking the visibility
 // of the tab strip in the containing object.
-@property(nonatomic, assign) id<TabStripPresentation> presentationProvider;
+@property(nonatomic, weak) id<TabStripPresentation> presentationProvider;
 
 // The duration to wait before starting tab strip animations. Used to
 // synchronize animations.
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_mediator_unittest.mm b/ios/chrome/credential_provider_extension/ui/new_password_mediator_unittest.mm
index 9d8924107..9071c17 100644
--- a/ios/chrome/credential_provider_extension/ui/new_password_mediator_unittest.mm
+++ b/ios/chrome/credential_provider_extension/ui/new_password_mediator_unittest.mm
@@ -31,7 +31,7 @@
 // Whether the `-alertSavePasswordFailed` method was called.
 @property(nonatomic, assign) BOOL alertedSaveFailed;
 // Password passed to the consumer.
-@property(nonatomic, assign) NSString* password;
+@property(nonatomic, copy) NSString* password;
 
 @end
 
diff --git a/ios/showcase/omnibox_popup/fake_autocomplete_suggestion.h b/ios/showcase/omnibox_popup/fake_autocomplete_suggestion.h
index 3cc5515..09d90c0f 100644
--- a/ios/showcase/omnibox_popup/fake_autocomplete_suggestion.h
+++ b/ios/showcase/omnibox_popup/fake_autocomplete_suggestion.h
@@ -26,7 +26,7 @@
 @property(nonatomic) id<OmniboxIcon> icon;
 @property(nonatomic) id<OmniboxPedal, OmniboxIcon> pedal;
 @property(nonatomic) BOOL isTailSuggestion;
-@property(nonatomic, readonly) NSString* commonPrefix;
+@property(nonatomic, readonly, copy) NSString* commonPrefix;
 @property(nonatomic, strong) NSNumber* suggestionGroupId;
 @property(nonatomic, strong) NSNumber* suggestionSectionId;
 
diff --git a/media/gpu/v4l2/v4l2_image_processor_backend.cc b/media/gpu/v4l2/v4l2_image_processor_backend.cc
index dc72a4a..f3049f4 100644
--- a/media/gpu/v4l2/v4l2_image_processor_backend.cc
+++ b/media/gpu/v4l2/v4l2_image_processor_backend.cc
@@ -925,35 +925,16 @@
 
   switch (input_memory_type_) {
     case V4L2_MEMORY_USERPTR: {
-      VideoFrame& frame = *job_record->input_frame;
       const size_t num_planes = V4L2Device::GetNumPlanesOfV4L2PixFmt(
           input_config_.fourcc.ToV4L2PixFmt());
       std::vector<void*> user_ptrs(num_planes);
-      if (frame.storage_type() == VideoFrame::STORAGE_SHMEM) {
-        // TODO(b/243883312): This copies the video frame to a writable buffer
-        // since the USERPTR API requires writable permission. Remove this
-        // workaround once the unreasonable permission is fixed.
-        const size_t buffer_size = frame.shm_region()->GetSize();
-        std::vector<uint8_t> writable_buffer(buffer_size);
-        std::memcpy(writable_buffer.data(), frame.data(0), buffer_size);
-        for (size_t i = 0; i < num_planes; ++i) {
-          const std::intptr_t plane_offset =
-              reinterpret_cast<std::intptr_t>(frame.data(i)) -
-              reinterpret_cast<std::intptr_t>(frame.data(0));
-          user_ptrs[i] = writable_buffer.data() + plane_offset;
-        }
-        job_record->input_frame->AddDestructionObserver(base::BindOnce(
-            [](std::vector<uint8_t>) {}, std::move(writable_buffer)));
-      } else {
-        for (size_t i = 0; i < num_planes; ++i)
-          user_ptrs[i] = frame.writable_data(i);
-      }
-
       for (size_t i = 0; i < num_planes; ++i) {
         int bytes_used =
-            VideoFrame::PlaneSize(frame.format(), i, input_config_.size)
+            VideoFrame::PlaneSize(job_record->input_frame->format(), i,
+                                  input_config_.size)
                 .GetArea();
         buffer.SetPlaneBytesUsed(i, bytes_used);
+        user_ptrs[i] = const_cast<uint8_t*>(job_record->input_frame->data(i));
       }
       if (!std::move(buffer).QueueUserPtr(user_ptrs)) {
         VPLOGF(1) << "Failed to queue a DMABUF buffer to input queue";
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 56389ea3ee..d452ba4 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -783,7 +783,7 @@
     const bool is_expected_storage_type =
         native_input_mode_
             ? frame->storage_type() == VideoFrame::STORAGE_GPU_MEMORY_BUFFER
-            : frame->storage_type() == VideoFrame::STORAGE_SHMEM;
+            : frame->IsMappable();
     if (!is_expected_storage_type) {
       VLOGF(1) << "Unexpected storage: "
                << VideoFrame::StorageTypeToString(frame->storage_type());
@@ -1392,27 +1392,16 @@
         NOTIFY_ERROR(kPlatformFailureError);
       }
 
-      // TODO(b/243883312): This copies the video frame to a writable buffer
-      // since the USERPTR API requires writable permission. Remove this
-      // workaround once the unreasonable permission is fixed.
-      const size_t buffer_size = frame->shm_region()->GetSize();
-      std::vector<uint8_t> writable_buffer(buffer_size);
-      std::memcpy(writable_buffer.data(), frame->data(0), buffer_size);
+      // The frame data is readable only and the driver doesn't actually write
+      // the buffer. But USRPTR buffer needs void*. So const_cast<> is required.
       std::vector<void*> user_ptrs(num_planes);
       for (size_t i = 0; i < num_planes; ++i) {
-        const std::intptr_t plane_offset =
-            reinterpret_cast<std::intptr_t>(frame->data(i)) -
-            reinterpret_cast<std::intptr_t>(frame->data(0));
-        user_ptrs[i] = writable_buffer.data() + plane_offset;
+        user_ptrs[i] = const_cast<uint8_t*>(frame->data(i));
       }
-
       if (!std::move(input_buf).QueueUserPtr(std::move(user_ptrs))) {
-        VPLOGF(1) << "Failed to queue a USRPTR buffer to input queue";
-        NOTIFY_ERROR(kPlatformFailureError);
+        VPLOGF(1) << "Failed queue a USRPTR buffer to input queue";
         return false;
       }
-      frame->AddDestructionObserver(
-          base::DoNothingWithBoundArgs(std::move(writable_buffer)));
       break;
     }
     case V4L2_MEMORY_DMABUF: {
@@ -1421,6 +1410,7 @@
         VPLOGF(1) << "Failed queue a DMABUF buffer to input queue";
         return false;
       }
+
       // Keep |gmb_handle| alive as long as |frame| is alive so that fds passed
       // to the driver are valid during encoding.
       frame->AddDestructionObserver(base::BindOnce(
diff --git a/net/cert/pki/mock_signature_verify_cache.h b/net/cert/pki/mock_signature_verify_cache.h
index 4a92c592..cc1820c 100644
--- a/net/cert/pki/mock_signature_verify_cache.h
+++ b/net/cert/pki/mock_signature_verify_cache.h
@@ -23,7 +23,7 @@
  public:
   MockSignatureVerifyCache();
 
-  ~MockSignatureVerifyCache();
+  ~MockSignatureVerifyCache() override;
 
   void Store(const std::string& key,
              SignatureVerifyCache::Value value) override;
diff --git a/net/cert/pki/signature_verify_cache.h b/net/cert/pki/signature_verify_cache.h
index cd9dc6c1..eae7c5f 100644
--- a/net/cert/pki/signature_verify_cache.h
+++ b/net/cert/pki/signature_verify_cache.h
@@ -17,6 +17,8 @@
     kUnknown,  // Cache has no information.
   };
 
+  virtual ~SignatureVerifyCache() = default;
+
   // This interface uses a const std::string reference instead of
   // std::string_view because any implementation that may reasonably want to use
   // std::unordered_map or similar can run into problems with std::hash before
diff --git a/net/dns/address_sorter_posix.cc b/net/dns/address_sorter_posix.cc
index ec3391d..38251ba6 100644
--- a/net/dns/address_sorter_posix.cc
+++ b/net/dns/address_sorter_posix.cc
@@ -197,7 +197,7 @@
   AddressSorterPosix::AddressScope scope;
   unsigned precedence;
   unsigned label;
-  raw_ptr<const AddressSorterPosix::SourceAddressInfo> src;
+  AddressSorterPosix::SourceAddressInfo src;
   std::unique_ptr<DatagramClientSocket> socket;
   size_t common_prefix_length;
   bool failed = false;
@@ -208,27 +208,27 @@
 bool CompareDestinations(const DestinationInfo& dst_a,
                          const DestinationInfo& dst_b) {
   // Rule 1: Avoid unusable destinations.
-  // Unusable destinations are already filtered out.
-  DCHECK(dst_a.src);
-  DCHECK(dst_b.src);
+  // Nothing to do here because unusable destinations are already filtered out.
 
   // Rule 2: Prefer matching scope.
-  bool scope_match1 = (dst_a.src->scope == dst_a.scope);
-  bool scope_match2 = (dst_b.src->scope == dst_b.scope);
+  bool scope_match1 = (dst_a.src.scope == dst_a.scope);
+  bool scope_match2 = (dst_b.src.scope == dst_b.scope);
   if (scope_match1 != scope_match2)
     return scope_match1;
 
   // Rule 3: Avoid deprecated addresses.
-  if (dst_a.src->deprecated != dst_b.src->deprecated)
-    return !dst_a.src->deprecated;
+  if (dst_a.src.deprecated != dst_b.src.deprecated) {
+    return !dst_a.src.deprecated;
+  }
 
   // Rule 4: Prefer home addresses.
-  if (dst_a.src->home != dst_b.src->home)
-    return dst_a.src->home;
+  if (dst_a.src.home != dst_b.src.home) {
+    return dst_a.src.home;
+  }
 
   // Rule 5: Prefer matching label.
-  bool label_match1 = (dst_a.src->label == dst_a.label);
-  bool label_match2 = (dst_b.src->label == dst_b.label);
+  bool label_match1 = (dst_a.src.label == dst_a.label);
+  bool label_match2 = (dst_b.src.label == dst_b.label);
   if (label_match1 != label_match2)
     return label_match1;
 
@@ -237,8 +237,9 @@
     return dst_a.precedence > dst_b.precedence;
 
   // Rule 7: Prefer native transport.
-  if (dst_a.src->native != dst_b.src->native)
-    return dst_a.src->native;
+  if (dst_a.src.native != dst_b.src.native) {
+    return dst_a.src.native;
+  }
 
   // Rule 8: Prefer smaller scope.
   if (dst_a.scope != dst_b.scope)
@@ -301,19 +302,24 @@
     for (auto& info : sort_list_) {
       IPEndPoint src;
       info.socket->GetLocalAddress(&src);
-      AddressSorterPosix::SourceAddressInfo& src_info =
-          sorter_->source_map_[src.address()];
-      if (src_info.scope == AddressSorterPosix::SCOPE_UNDEFINED) {
-        // If |source_info_| is out of date, |src| might be missing, but we
-        // still want to sort, even though the HostCache will be cleared soon.
-        sorter_->FillPolicy(src.address(), &src_info);
+      auto iter = sorter_->source_map_.find(src.address());
+      if (iter == sorter_->source_map_.end()) {
+        //  |src.address| may not be in the map if |source_info_| has not been
+        //  updated from the OS yet. It will be updated and HostCache cleared
+        //  soon, but we still want to sort, so fill in an empty
+        info.src = AddressSorterPosix::SourceAddressInfo();
+      } else {
+        info.src = iter->second;
       }
-      info.src = &src_info;
+
+      if (info.src.scope == AddressSorterPosix::SCOPE_UNDEFINED) {
+        sorter_->FillPolicy(src.address(), &info.src);
+      }
 
       if (info.endpoint.address().size() == src.address().size()) {
         info.common_prefix_length =
             std::min(CommonPrefixLength(info.endpoint.address(), src.address()),
-                     info.src->prefix_length);
+                     info.src.prefix_length);
       }
     }
     std::stable_sort(sort_list_.begin(), sort_list_.end(), CompareDestinations);
diff --git a/net/dns/address_sorter_posix.h b/net/dns/address_sorter_posix.h
index f315364..fe42d6c 100644
--- a/net/dns/address_sorter_posix.h
+++ b/net/dns/address_sorter_posix.h
@@ -48,14 +48,14 @@
 
   struct SourceAddressInfo {
     // Values read from policy tables.
-    AddressScope scope;
-    unsigned label;
+    AddressScope scope = SCOPE_UNDEFINED;
+    unsigned label = 0;
 
     // Values from the OS, matter only if more than one source address is used.
-    size_t prefix_length;
-    bool deprecated;  // vs. preferred RFC4862
-    bool home;        // vs. care-of RFC6275
-    bool native;
+    size_t prefix_length = 0;
+    bool deprecated = false;  // vs. preferred RFC4862
+    bool home = false;        // vs. care-of RFC6275
+    bool native = false;
   };
 
   typedef std::map<IPAddress, SourceAddressInfo> SourceAddressMap;
diff --git a/printing/print_settings.cc b/printing/print_settings.cc
index 5c3f569a..882842c4c 100644
--- a/printing/print_settings.cc
+++ b/printing/print_settings.cc
@@ -31,7 +31,6 @@
 void GetColorModelForModel(mojom::ColorModel color_model,
                            std::string* color_setting_name,
                            std::string* color_value) {
-#if BUILDFLAG(IS_MAC)
   constexpr char kCUPSColorMode[] = "ColorMode";
   constexpr char kCUPSColorModel[] = "ColorModel";
   constexpr char kCUPSPrintoutMode[] = "PrintoutMode";
@@ -41,17 +40,6 @@
   constexpr char kCUPSEpsonInk[] = "Ink";
   constexpr char kCUPSSharpARCMode[] = "ARCMode";
   constexpr char kCUPSXeroxXRXColor[] = "XRXColor";
-#else
-  constexpr char kCUPSColorMode[] = "cups-ColorMode";
-  constexpr char kCUPSColorModel[] = "cups-ColorModel";
-  constexpr char kCUPSPrintoutMode[] = "cups-PrintoutMode";
-  constexpr char kCUPSProcessColorModel[] = "cups-ProcessColorModel";
-  constexpr char kCUPSBrotherMonoColor[] = "cups-BRMonoColor";
-  constexpr char kCUPSBrotherPrintQuality[] = "cups-BRPrintQuality";
-  constexpr char kCUPSEpsonInk[] = "cups-Ink";
-  constexpr char kCUPSSharpARCMode[] = "cups-ARCMode";
-  constexpr char kCUPSXeroxXRXColor[] = "cups-XRXColor";
-#endif  // BUILDFLAG(IS_MAC)
 
   *color_setting_name = kCUPSColorModel;
 
diff --git a/remoting/host/linux/linux_me2me_host.py b/remoting/host/linux/linux_me2me_host.py
index 66a0262..153df79 100755
--- a/remoting/host/linux/linux_me2me_host.py
+++ b/remoting/host/linux/linux_me2me_host.py
@@ -30,6 +30,7 @@
 import pwd
 import re
 import shlex
+import shutil
 import signal
 import socket
 import struct
@@ -158,6 +159,9 @@
 # wayland compositor/server for clients to connect to.
 RUNTIME_DIR_TEMPLATE = "/run/user/%s"
 
+# Binary name for the gnome-session.
+GNOME_SESSION = "gnome-session"
+
 # Globals needed by the atexit cleanup() handler.
 g_desktop = None
 g_host_hash = hashlib.md5(socket.gethostname().encode()).hexdigest()
@@ -813,7 +817,7 @@
       self.child_env["G_MESSAGES_DEBUG"] = "all"
       self.child_env["GDK_DEBUG"]  = "all"
       self.child_env["G_DEBUG"] = "fatal-criticals"
-      self.child_env["WAYLAND_DEBUG"] = 1
+      self.child_env["WAYLAND_DEBUG"] = "1"
 
   def _get_unused_wayland_socket(self):
     """
@@ -835,25 +839,14 @@
     return "wayland-%s" % socket_num
 
   @staticmethod
-  def _is_gnome_shell_present():
-    try:
-      subprocess.check_output(["gnome-shell", "--help"],
-                              stderr=subprocess.STDOUT)
-    except subprocess.CalledProcessError as err:
-      logging.warning("Unable to find 'gnome-shell' on the host, "
-                      "returncode: %s, output: %s" % (err.returncode,
-                                                      err.output))
+  def _is_gnome_session_present():
+    if not shutil.which(GNOME_SESSION):
+      logging.warning("Unable to find '%s' on the host" % GNOME_SESSION)
       return False
     return True
 
-  def _gnome_shell_cmd(self):
-    gnome_shell_cmd = [
-      "gnome-shell", "--wayland", "--headless", "--wayland-display",
-      self._wayland_socket, "--replace"]
-    return gnome_shell_cmd
-
   def _launch_server(self, *args, **kwargs):
-    if not self._is_gnome_shell_present():
+    if not self._is_gnome_session_present():
       logging.error("Only GNOME based wayland hosts are supported currently. "
                     "If the host is a GNOME host, please ensure that "
                     "'gnome-shell' is installed on it")
@@ -863,10 +856,10 @@
     logging.info("Launching wayland server.")
     if self.ssh_auth_sockname:
       self.child_env["SSH_AUTH_SOCK"] = self.ssh_auth_sockname
-    self.server_proc = subprocess.Popen(self._gnome_shell_cmd(),
-                                         stdout=subprocess.PIPE,
-                                         stderr=subprocess.STDOUT,
-                                         env=self.child_env)
+    self.server_proc = subprocess.Popen([GNOME_SESSION],
+                                        stdout=subprocess.PIPE,
+                                        stderr=subprocess.STDOUT,
+                                        env=self.child_env)
 
     if not self.server_proc.pid:
       raise Exception("Could not start wayland session")
diff --git a/sandbox/policy/linux/sandbox_seccomp_bpf_linux.h b/sandbox/policy/linux/sandbox_seccomp_bpf_linux.h
index 7f4c4c7..2d42a279 100644
--- a/sandbox/policy/linux/sandbox_seccomp_bpf_linux.h
+++ b/sandbox/policy/linux/sandbox_seccomp_bpf_linux.h
@@ -27,6 +27,7 @@
   struct Options {
     bool use_amd_specific_policies = false;     // For ChromiumOS.
     bool use_intel_specific_policies = false;   // For ChromiumOS.
+    bool use_virtio_specific_policies = false;  // For ChromiumOS VM.
     bool use_nvidia_specific_policies = false;  // For Linux.
     bool use_asahi_specific_policies = false;   // For Linux.
 
diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc
index 1ed8dec..6d42ae6 100644
--- a/services/network/public/cpp/resource_request.cc
+++ b/services/network/public/cpp/resource_request.cc
@@ -11,6 +11,7 @@
 #include "net/log/net_log_source.h"
 #include "services/network/public/mojom/cookie_access_observer.mojom.h"
 #include "services/network/public/mojom/devtools_observer.mojom.h"
+#include "services/network/public/mojom/trust_token_access_observer.mojom.h"
 #include "services/network/public/mojom/url_request.mojom.h"
 #include "services/network/public/mojom/web_bundle_handle.mojom.h"
 
@@ -20,8 +21,9 @@
 
 mojo::PendingRemote<mojom::CookieAccessObserver> Clone(
     mojo::PendingRemote<mojom::CookieAccessObserver>* observer) {
-  if (!*observer)
+  if (!*observer) {
     return mojo::NullRemote();
+  }
   mojo::Remote<mojom::CookieAccessObserver> remote(std::move(*observer));
   mojo::PendingRemote<mojom::CookieAccessObserver> new_remote;
   remote->Clone(new_remote.InitWithNewPipeAndPassReceiver());
@@ -29,10 +31,23 @@
   return new_remote;
 }
 
+mojo::PendingRemote<mojom::TrustTokenAccessObserver> Clone(
+    mojo::PendingRemote<mojom::TrustTokenAccessObserver>* observer) {
+  if (!*observer) {
+    return mojo::NullRemote();
+  }
+  mojo::Remote<mojom::TrustTokenAccessObserver> remote(std::move(*observer));
+  mojo::PendingRemote<mojom::TrustTokenAccessObserver> new_remote;
+  remote->Clone(new_remote.InitWithNewPipeAndPassReceiver());
+  *observer = remote.Unbind();
+  return new_remote;
+}
+
 mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver> Clone(
     mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver>* observer) {
-  if (!*observer)
+  if (!*observer) {
     return mojo::NullRemote();
+  }
   mojo::Remote<mojom::URLLoaderNetworkServiceObserver> remote(
       std::move(*observer));
   mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver> new_remote;
@@ -43,8 +58,9 @@
 
 mojo::PendingRemote<mojom::DevToolsObserver> Clone(
     mojo::PendingRemote<mojom::DevToolsObserver>* observer) {
-  if (!*observer)
+  if (!*observer) {
     return mojo::NullRemote();
+  }
   mojo::Remote<mojom::DevToolsObserver> remote(std::move(*observer));
   mojo::PendingRemote<mojom::DevToolsObserver> new_remote;
   remote->Clone(new_remote.InitWithNewPipeAndPassReceiver());
@@ -54,8 +70,9 @@
 
 mojo::PendingRemote<mojom::AcceptCHFrameObserver> Clone(
     mojo::PendingRemote<mojom::AcceptCHFrameObserver>& observer) {
-  if (!observer)
+  if (!observer) {
     return mojo::NullRemote();
+  }
   mojo::Remote<mojom::AcceptCHFrameObserver> remote(std::move(observer));
   mojo::PendingRemote<mojom::AcceptCHFrameObserver> new_remote;
   remote->Clone(new_remote.InitWithNewPipeAndPassReceiver());
@@ -123,6 +140,9 @@
   cookie_observer =
       Clone(&const_cast<mojo::PendingRemote<mojom::CookieAccessObserver>&>(
           other.cookie_observer));
+  trust_token_observer =
+      Clone(&const_cast<mojo::PendingRemote<mojom::TrustTokenAccessObserver>&>(
+          other.trust_token_observer));
   url_loader_network_observer = Clone(
       &const_cast<mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver>&>(
           other.url_loader_network_observer));
diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h
index 7ca1df6..dcd008f 100644
--- a/services/network/public/cpp/resource_request.h
+++ b/services/network/public/cpp/resource_request.h
@@ -30,6 +30,7 @@
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "services/network/public/mojom/ip_address_space.mojom-shared.h"
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "services/network/public/mojom/trust_token_access_observer.mojom-forward.h"
 #include "services/network/public/mojom/trust_tokens.mojom.h"
 #include "services/network/public/mojom/url_loader_network_service_observer.mojom.h"
 #include "services/network/public/mojom/url_request.mojom-forward.h"
@@ -64,6 +65,7 @@
     bool has_user_activation = false;
     bool allow_cookies_from_browser = false;
     mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer;
+    mojo::PendingRemote<mojom::TrustTokenAccessObserver> trust_token_observer;
     mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver>
         url_loader_network_observer;
     mojo::PendingRemote<mojom::DevToolsObserver> devtools_observer;
diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc
index 38c6a61b..c5602f01 100644
--- a/services/network/public/cpp/url_request_mojom_traits.cc
+++ b/services/network/public/cpp/url_request_mojom_traits.cc
@@ -26,6 +26,7 @@
 #include "services/network/public/mojom/data_pipe_getter.mojom.h"
 #include "services/network/public/mojom/devtools_observer.mojom.h"
 #include "services/network/public/mojom/ip_address_space.mojom.h"
+#include "services/network/public/mojom/trust_token_access_observer.mojom.h"
 #include "services/network/public/mojom/trust_tokens.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom-shared.h"
 #include "services/network/public/mojom/url_request.mojom.h"
@@ -91,6 +92,8 @@
   out->allow_cookies_from_browser = data.allow_cookies_from_browser();
   out->cookie_observer = data.TakeCookieObserver<
       mojo::PendingRemote<network::mojom::CookieAccessObserver>>();
+  out->trust_token_observer = data.TakeTrustTokenObserver<
+      mojo::PendingRemote<network::mojom::TrustTokenAccessObserver>>();
   out->url_loader_network_observer = data.TakeUrlLoaderNetworkObserver<
       mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>>();
   out->devtools_observer = data.TakeDevtoolsObserver<
diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h
index 1331e8d1..acee3b7 100644
--- a/services/network/public/cpp/url_request_mojom_traits.h
+++ b/services/network/public/cpp/url_request_mojom_traits.h
@@ -31,6 +31,7 @@
 #include "services/network/public/mojom/data_pipe_getter.mojom.h"
 #include "services/network/public/mojom/devtools_observer.mojom-forward.h"
 #include "services/network/public/mojom/ip_address_space.mojom-forward.h"
+#include "services/network/public/mojom/trust_token_access_observer.mojom-forward.h"
 #include "services/network/public/mojom/trust_tokens.mojom-forward.h"
 #include "services/network/public/mojom/url_loader.mojom-forward.h"
 #include "services/network/public/mojom/url_loader_network_service_observer.mojom-forward.h"
@@ -72,17 +73,29 @@
   static mojo::PendingRemote<network::mojom::CookieAccessObserver>
   cookie_observer(
       const network::ResourceRequest::TrustedParams& trusted_params) {
-    if (!trusted_params.cookie_observer)
+    if (!trusted_params.cookie_observer) {
       return mojo::NullRemote();
+    }
     return std::move(
         const_cast<network::ResourceRequest::TrustedParams&>(trusted_params)
             .cookie_observer);
   }
+  static mojo::PendingRemote<network::mojom::TrustTokenAccessObserver>
+  trust_token_observer(
+      const network::ResourceRequest::TrustedParams& trusted_params) {
+    if (!trusted_params.trust_token_observer) {
+      return mojo::NullRemote();
+    }
+    return std::move(
+        const_cast<network::ResourceRequest::TrustedParams&>(trusted_params)
+            .trust_token_observer);
+  }
   static mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
   url_loader_network_observer(
       const network::ResourceRequest::TrustedParams& trusted_params) {
-    if (!trusted_params.url_loader_network_observer)
+    if (!trusted_params.url_loader_network_observer) {
       return mojo::NullRemote();
+    }
     return std::move(
         const_cast<network::ResourceRequest::TrustedParams&>(trusted_params)
             .url_loader_network_observer);
@@ -90,8 +103,9 @@
   static mojo::PendingRemote<network::mojom::DevToolsObserver>
   devtools_observer(
       const network::ResourceRequest::TrustedParams& trusted_params) {
-    if (!trusted_params.devtools_observer)
+    if (!trusted_params.devtools_observer) {
       return mojo::NullRemote();
+    }
     return std::move(
         const_cast<network::ResourceRequest::TrustedParams&>(trusted_params)
             .devtools_observer);
@@ -103,8 +117,9 @@
   static mojo::PendingRemote<network::mojom::AcceptCHFrameObserver>
   accept_ch_frame_observer(
       const network::ResourceRequest::TrustedParams& trusted_params) {
-    if (!trusted_params.accept_ch_frame_observer)
+    if (!trusted_params.accept_ch_frame_observer) {
       return mojo::NullRemote();
+    }
     return std::move(
         const_cast<network::ResourceRequest::TrustedParams&>(trusted_params)
             .accept_ch_frame_observer);
@@ -128,8 +143,9 @@
   }
   static mojo::PendingRemote<network::mojom::WebBundleHandle> web_bundle_handle(
       const network::ResourceRequest::WebBundleTokenParams& params) {
-    if (!params.handle)
+    if (!params.handle) {
       return mojo::NullRemote();
+    }
     return std::move(
         const_cast<network::ResourceRequest::WebBundleTokenParams&>(params)
             .handle);
diff --git a/services/network/public/cpp/url_request_mojom_traits_unittest.cc b/services/network/public/cpp/url_request_mojom_traits_unittest.cc
index 05abb438..9bc3b34 100644
--- a/services/network/public/cpp/url_request_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/url_request_mojom_traits_unittest.cc
@@ -21,6 +21,7 @@
 #include "services/network/public/mojom/cookie_access_observer.mojom.h"
 #include "services/network/public/mojom/data_pipe_getter.mojom.h"
 #include "services/network/public/mojom/devtools_observer.mojom.h"
+#include "services/network/public/mojom/trust_token_access_observer.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_request.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 59b6a7f..b2254b76 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -49,6 +49,7 @@
 import "services/network/public/mojom/tcp_socket.mojom";
 import "services/network/public/mojom/transferable_directory.mojom";
 import "services/network/public/mojom/trust_tokens.mojom";
+import "services/network/public/mojom/trust_token_access_observer.mojom";
 import "services/network/public/mojom/udp_socket.mojom";
 import "services/network/public/mojom/url_loader.mojom";
 import "services/network/public/mojom/url_loader_factory.mojom";
@@ -746,6 +747,9 @@
   // Used to notify clients about cookie reads or writes.
   pending_remote<CookieAccessObserver>? cookie_observer;
 
+  // Used to notify clients about Trust Token accesses.
+  pending_remote<TrustTokenAccessObserver>? trust_token_observer;
+
   // Used to notify clients about authentication and certificate events.
   pending_remote<URLLoaderNetworkServiceObserver>? url_loader_network_observer;
 
diff --git a/services/network/public/mojom/trust_token_access_observer.mojom b/services/network/public/mojom/trust_token_access_observer.mojom
index ca3710f..30c776cf 100644
--- a/services/network/public/mojom/trust_token_access_observer.mojom
+++ b/services/network/public/mojom/trust_token_access_observer.mojom
@@ -23,7 +23,6 @@
 // Users of TrustTokenAccessObserver should create a dedicated observer for
 // each network request context (URLLoaderFactory) they are interested in
 // observing.
-[RequireContext=sandbox.mojom.Context.kBrowser]
 interface TrustTokenAccessObserver {
   // Called when an attempt has been made to access trust tokens by the
   // NetworkService with the details about the trust token access.
@@ -31,6 +30,5 @@
 
   // Called to create a copy of this observer. (e.g. when cloning observers
   // from ResourceRequest).
-  [AllowedContext=sandbox.mojom.Context.kBrowser]
   Clone(pending_receiver<TrustTokenAccessObserver> listener);
 };
diff --git a/services/network/public/mojom/url_request.mojom b/services/network/public/mojom/url_request.mojom
index ae49a7b..6da8e326 100644
--- a/services/network/public/mojom/url_request.mojom
+++ b/services/network/public/mojom/url_request.mojom
@@ -26,6 +26,7 @@
 import "services/network/public/mojom/request_priority.mojom";
 import "services/network/public/mojom/site_for_cookies.mojom";
 import "services/network/public/mojom/trust_tokens.mojom";
+import "services/network/public/mojom/trust_token_access_observer.mojom";
 import "services/network/public/mojom/url_response_head.mojom";
 import "services/network/public/mojom/web_bundle_handle.mojom";
 import "services/network/public/mojom/accept_ch_frame_observer.mojom";
@@ -75,6 +76,11 @@
   // URLLoaderFactory will be ignored.
   pending_remote<CookieAccessObserver>? cookie_observer;
 
+  // Observer which should be notified when this URLRequest accesses a Trust
+  // Token. If this is set to non-null, the observer passed to
+  // URLLoaderFactory will be ignored.
+  pending_remote<TrustTokenAccessObserver>? trust_token_observer;
+
   // Observer which should be notified when this URLRequest has authentication
   // and certificate events. If this is set to non-null, the observer passed to
   // URLLoaderFactory will be ignored.
diff --git a/services/network/url_loader_factory.cc b/services/network/url_loader_factory.cc
index 7e1c8a9..8a61ea6 100644
--- a/services/network/url_loader_factory.cc
+++ b/services/network/url_loader_factory.cc
@@ -79,6 +79,7 @@
       header_client_(std::move(params_->header_client)),
       cors_url_loader_factory_(cors_url_loader_factory),
       cookie_observer_(std::move(params_->cookie_observer)),
+      trust_token_observer_(std::move(params_->trust_token_observer)),
       url_loader_network_service_observer_(
           std::move(params_->url_loader_network_observer)),
       devtools_observer_(std::move(params_->devtools_observer)) {
@@ -296,9 +297,13 @@
         std::move(const_cast<mojo::PendingRemote<mojom::CookieAccessObserver>&>(
             resource_request.trusted_params->cookie_observer));
   }
-  // TODO(https://crbug.com/1378264): Currently Trust Token Access observer
-  // isn't hooked up through URLLoaderFactory.
   mojo::PendingRemote<mojom::TrustTokenAccessObserver> trust_token_observer;
+  if (resource_request.trusted_params &&
+      resource_request.trusted_params->trust_token_observer) {
+    trust_token_observer = std::move(
+        const_cast<mojo::PendingRemote<mojom::TrustTokenAccessObserver>&>(
+            resource_request.trusted_params->trust_token_observer));
+  }
   mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver>
       url_loader_network_observer;
   if (resource_request.trusted_params &&
@@ -366,8 +371,9 @@
 
 mojom::TrustTokenAccessObserver* URLLoaderFactory::GetTrustTokenAccessObserver()
     const {
-  // TODO(https://crbug.com/1378264): URLLoaderFactory support for the Trust
-  // Token Access Observer is currently unimplemented.
+  if (trust_token_observer_) {
+    return trust_token_observer_.get();
+  }
   return nullptr;
 }
 
diff --git a/services/network/url_loader_factory.h b/services/network/url_loader_factory.h
index fd5a0483..de281673 100644
--- a/services/network/url_loader_factory.h
+++ b/services/network/url_loader_factory.h
@@ -135,6 +135,7 @@
   corb::PerFactoryState corb_state_;
 
   mojo::Remote<mojom::CookieAccessObserver> cookie_observer_;
+  mojo::Remote<mojom::TrustTokenAccessObserver> trust_token_observer_;
   mojo::Remote<mojom::URLLoaderNetworkServiceObserver>
       url_loader_network_service_observer_;
   mojo::Remote<mojom::DevToolsObserver> devtools_observer_;
diff --git a/testing/buildbot/chromium.cft.json b/testing/buildbot/chromium.cft.json
index 5f8aab5c..f0eb52e 100644
--- a/testing/buildbot/chromium.cft.json
+++ b/testing/buildbot/chromium.cft.json
@@ -2552,7 +2552,7 @@
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
+          "shards": 20
         },
         "test": "browser_tests",
         "test_id_prefix": "ninja://chrome/test:browser_tests/"
@@ -4193,6 +4193,26 @@
         },
         "test_id_prefix": "ninja://ui/views:views_perftests/"
       }
+    ],
+    "scripts": [
+      {
+        "isolate_profile_data": true,
+        "name": "check_static_initializers",
+        "script": "check_static_initializers.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "metrics_python_tests",
+        "script": "metrics_python_tests.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "webkit_lint",
+        "script": "blink_lint_expectations.py",
+        "swarming": {}
+      }
     ]
   },
   "win-rel-cft": {
@@ -4451,6 +4471,7 @@
       },
       {
         "args": [
+          "--disable-features=WebRTC-H264WithOpenH264FFmpeg",
           "--test-launcher-filter-file=../../testing/buildbot/filters/win.win-rel-cft.browser_tests.filter"
         ],
         "isolate_profile_data": true,
@@ -6159,7 +6180,9 @@
         "args": [
           "--num-retries=3",
           "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json",
-          "--git-revision=${got_revision}"
+          "--git-revision=${got_revision}",
+          "--target",
+          "Release_x64"
         ],
         "check_flakiness_for_new_tests": false,
         "isolate_name": "blink_web_tests",
@@ -6197,7 +6220,9 @@
         "args": [
           "--num-retries=3",
           "--write-run-histories-to=${ISOLATED_OUTDIR}/run_histories.json",
-          "--git-revision=${got_revision}"
+          "--git-revision=${got_revision}",
+          "--target",
+          "Release_x64"
         ],
         "check_flakiness_for_new_tests": false,
         "isolate_name": "blink_wpt_tests",
@@ -6495,63 +6520,6 @@
       },
       {
         "args": [
-          "--extra-browser-args=--enable-crashpad"
-        ],
-        "isolate_name": "telemetry_perf_unittests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_perf_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-19042|Windows-10-19045"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_perf_unittests/"
-      },
-      {
-        "args": [
-          "--jobs=1",
-          "--extra-browser-args=--disable-gpu"
-        ],
-        "isolate_name": "telemetry_unittests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-19042|Windows-10-19045"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_unittests/"
-      },
-      {
-        "args": [
           "--gtest-benchmark-name=views_perftests"
         ],
         "isolate_name": "views_perftests",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index fe40121..9b06f7c 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -90701,6 +90701,42 @@
       }
     ]
   },
+  "linux-wpt-content-shell-leak-detection": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "--log-wptreport",
+          "--xvfb",
+          "--enable-leak-detection"
+        ],
+        "experiment_percentage": 100,
+        "isolate_name": "wpt_tests_isolate_content_shell",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "wpt_tests_suite",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 15
+        },
+        "test_id_prefix": "ninja://:wpt_tests_isolate_content_shell/"
+      }
+    ]
+  },
   "linux-wpt-fyi-rel": {
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/filters/linux.linux-rel-cft.browser_tests.filter b/testing/buildbot/filters/linux.linux-rel-cft.browser_tests.filter
index 3f11f7c..d137a84 100644
--- a/testing/buildbot/filters/linux.linux-rel-cft.browser_tests.filter
+++ b/testing/buildbot/filters/linux.linux-rel-cft.browser_tests.filter
@@ -42,8 +42,10 @@
 -MultipleTabSharingUIViewsBrowserTest.CloseTabs
 -MultipleTabSharingUIViewsBrowserTest.StopSharing
 -MultipleTabSharingUIViewsBrowserTest.VerifyUi
+-OmniboxPopupViewViewsTest.EmitAccessibilityEventsOnButtonFocusHint
 -SaveCardBubbleViewsFullFormBrowserTest.AlertAccessibleEvent
 -SessionRestoreTest.RestoredTabsHaveCorrectInitialSize
+-SyncConfirmationUIDialogPixelTest.InvokeUi_default*
 -TabSharingUIViewsPreferCurrentTabBrowserTest.VerifyUiWhenCapturingAnotherTab
 -TabSharingUIViewsPreferCurrentTabBrowserTest.VerifyUiWhenSelfCapturing
 -WebAppFrameToolbarBrowserTest_Borderless.*
diff --git a/testing/buildbot/filters/linux.linux-rel-cft.interactive_ui_tests.filter b/testing/buildbot/filters/linux.linux-rel-cft.interactive_ui_tests.filter
index 54edc59..d8b16d5a 100644
--- a/testing/buildbot/filters/linux.linux-rel-cft.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/linux.linux-rel-cft.interactive_ui_tests.filter
@@ -14,6 +14,7 @@
 # https://ci.chromium.org/ui/p/chromium/builders/ci/linux-rel-cft/2321
 -BrowserFocusTest.AppLocationBar
 -BrowserFocusTest.PopupLocationBar
+-CaptionBubbleControllerViewsTest.BubblePositioning
 -PopupBlockerBrowserTest.WindowFeatures
 -SadTabViewInteractiveUITest.SadTabKeyboardAccessibility
 -SitePerProcessInteractiveBrowserTest.FullscreenElementInABAAndExitViaEscapeKey
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index f0c0c92..ed2ff14 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -506,6 +506,12 @@
           'hard_timeout': 2400,
         },
       },
+      'win-rel-cft': {
+        'args': [
+          '--target',
+          'Release_x64',
+        ],
+      },
       'win10-rel-no-external-ip': {
         'args': [
           '--target',
@@ -809,6 +815,12 @@
           'hard_timeout': 2400,
         },
       },
+      'win-rel-cft': {
+        'args': [
+          '--target',
+          'Release_x64',
+        ],
+      },
       'win10-rel-no-external-ip': {
         'args': [
           '--target',
@@ -832,7 +844,7 @@
           '--target',
           'Release_x64',
         ],
-      }
+      },
     },
   },
   'browser_tests': {
@@ -1091,6 +1103,9 @@
         'args': [
           '--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel-cft.browser_tests.filter',
         ],
+        'swarming': {
+          'shards': 20,  # crbug.com/1361887
+        },
       },
       'win-asan': {
         # These are very slow on the ASAN trybot for some reason.
@@ -1106,6 +1121,8 @@
       },
       'win-rel-cft': {
         'args': [
+          # crbug.com/868082
+          '--disable-features=WebRTC-H264WithOpenH264FFmpeg',
           '--test-launcher-filter-file=../../testing/buildbot/filters/win.win-rel-cft.browser_tests.filter',
         ],
       },
@@ -3416,6 +3433,7 @@
       'Win10 Tests x64 (dbg)',
       'Win11 Tests x64',
       'win10-rel-no-external-ip',
+      'win-rel-cft',
     ],
   },
   'telemetry_unittests': {
@@ -3437,6 +3455,7 @@
       'Win10 Tests x64',
       'Win11 Tests x64',
       'win10-rel-no-external-ip',
+      'win-rel-cft',
 
       # TODO(https://crbug.com/1267161): Re-enable when platform is supported.
       'mac11-arm64-rel-tests',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 76ca8cdb..8188138 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -5406,6 +5406,28 @@
       },
     },
 
+    'wpt_web_tests_enable_leak_detection': {
+      'wpt_tests_suite': {
+        'args': [
+          '--log-wptreport',
+          '--xvfb',
+          '--enable-leak-detection',
+        ],
+        'merge': {
+          'args': [
+            '--verbose',
+          ],
+          'script': '//third_party/blink/tools/merge_web_test_results.py',
+        },
+        'isolate_name': 'wpt_tests_isolate_content_shell',
+        'results_handler': 'layout tests',
+        'swarming': {
+          'shards': 15,
+        },
+        'experiment_percentage': 100,
+      },
+    },
+
     'wpt_web_tests_highdpi': {
       'wpt_tests_suite_highdpi': {
         'args': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index dfe9d35..9ce6d58 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1262,7 +1262,7 @@
         }
       },
 
-      # Mirrors Mac Builder and Mac 12 Tests, except that we don't run
+      # Mirrors Mac Builder and Mac12 Tests, except that we don't run
       # 'mac_signing_tests' (it is removed in test_suite_exceptions.pyl),
       # which doesn't exist in the CfT config.
       'mac-rel-cft': {
@@ -1281,6 +1281,7 @@
         'test_suites': {
           'gtest_tests': 'chromium_mac_gtests_no_nacl',
           'isolated_scripts': 'chromium_mac_rel_isolated_scripts',
+          'scripts': 'chromium_mac_scripts',
         },
       },
 
@@ -3527,6 +3528,15 @@
           'isolated_scripts': 'wpt_web_tests_content_shell_multiple_flags',
         },
       },
+      'linux-wpt-content-shell-leak-detection': {
+        'mixins': [
+          'has_native_resultdb_integration',
+          'linux-bionic',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'wpt_web_tests_enable_leak_detection',
+        },
+      },
       'linux-wpt-fyi-rel': {
         'mixins': [
           'has_native_resultdb_integration',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 55d024d..f9316014 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -8802,6 +8802,26 @@
             ]
         }
     ],
+    "PowerBookmarkBackend": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "PowerBookmarkBackend"
+                    ]
+                }
+            ]
+        }
+    ],
     "PreconnectInNetworkService": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 6672da0..b94ee7e 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1517,10 +1517,6 @@
              "WebRtcMetronome",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kSyncAccessHandleAllSyncSurface,
-             "SyncAccessHandleAllSyncSurface",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kRunTextInputUpdatePostLifecycle,
              "RunTextInputUpdatePostLifecycle",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index d1f4c51..2531d5e 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -771,9 +771,6 @@
 
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebRtcMetronome);
 
-// If enabled, all of FileSystemAccessSyncAccessHandle methods are synchronous.
-BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kSyncAccessHandleAllSyncSurface);
-
 // If enabled, IME updates are computed at the end of a lifecycle update rather
 // than the beginning.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kRunTextInputUpdatePostLifecycle);
diff --git a/third_party/blink/public/devtools_protocol/OWNERS b/third_party/blink/public/devtools_protocol/OWNERS
index cacd3c83..f6766ff 100644
--- a/third_party/blink/public/devtools_protocol/OWNERS
+++ b/third_party/blink/public/devtools_protocol/OWNERS
@@ -2,4 +2,3 @@
 caseq@chromium.org
 dgozman@chromium.org
 pfeldman@chromium.org
-sigurds@chromium.org
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index af09fd6..888b105 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -3681,7 +3681,9 @@
   # Missing optional values will be filled in by the target with what it would normally use.
   experimental type UserAgentMetadata extends object
     properties
+      # Brands appearing in Sec-CH-UA.
       optional array of UserAgentBrandVersion brands
+      # Brands appearing in Sec-CH-UA-Full-Version-List.
       optional array of UserAgentBrandVersion fullVersionList
       deprecated optional string fullVersion
       string platform
diff --git a/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.cc.tmpl
index a8a500cf..c3bd3ad 100644
--- a/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.cc.tmpl
@@ -21,4 +21,16 @@
   return g_null_name;
 }
 
+#if defined(USE_INNER_HTML_PARSER_FAST_PATH)
+const QualifiedName& Lookup{{namespace}}AttributeName(const LChar* data, unsigned length) {
+  DCHECK(data);
+  DCHECK(length);
+  {% macro trie_return_statement(tag) -%}
+  {{namespace|lower}}_names::{{tag|symbol}}Attr
+  {%- endmacro %}
+  {{ trie_length_switch(length_tries, trie_return_statement, false) | indent(4) }}
+  return g_null_name;
+}
+#endif
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.h.tmpl b/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.h.tmpl
index cd0acd7..1c1203b 100644
--- a/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.h.tmpl
@@ -18,6 +18,13 @@
 // called if `length` is > 0.
 CORE_EXPORT const QualifiedName& Lookup{{namespace}}AttributeName(const UChar* data, unsigned length);
 
+#if defined(USE_INNER_HTML_PARSER_FAST_PATH)
+// Returns the QualifiedName for the attribute whose name matches `data`.
+// Returns `g_null_name` if there is no match. It is expected this is only
+// called if `length` is > 0.
+CORE_EXPORT const QualifiedName& Lookup{{namespace}}AttributeName(const LChar* data, unsigned length);
+#endif
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_{{namespace|upper}}_ELEMENT_ATTRIBUTE_NAME_LOOKUP_TRIE_H_
diff --git a/third_party/blink/renderer/config.gni b/third_party/blink/renderer/config.gni
index 6962087..5c71aec 100644
--- a/third_party/blink/renderer/config.gni
+++ b/third_party/blink/renderer/config.gni
@@ -47,6 +47,10 @@
 
   # If true, the experimental renderer extensions library will be used.
   use_blink_extensions_chromeos = is_chromeos
+
+  # TODO(https://crbug.com/1407201): temporarily disabled on platforms that
+  # are concerned about binary size. Figure out way to use everywhere.
+  use_inner_html_parser_fast_path = !is_android && !is_fuchsia
 }
 
 # feature_defines_list ---------------------------------------------------------
@@ -69,6 +73,10 @@
   feature_defines_list += [ "WTF_USE_WEBAUDIO_PFFFT=1" ]
 }
 
+if (use_inner_html_parser_fast_path) {
+  feature_defines_list += [ "USE_INNER_HTML_PARSER_FAST_PATH=1" ]
+}
+
 if (blink_symbol_level == 2) {
   blink_symbols_config = [ "//build/config/compiler:symbols" ]
 } else if (blink_symbol_level == 1) {
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 7a734cc..fabf4965 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -413,6 +413,13 @@
     "//third_party/blink/renderer/core/exported",
     "//third_party/blink/renderer/core/probe",
   ]
+
+  if (use_inner_html_parser_fast_path) {
+    sources += [
+      "html/parser/html_document_parser_fastpath.cc",
+      "html/parser/html_document_parser_fastpath.h",
+    ]
+  }
 }
 
 source_set("testing") {
@@ -1443,6 +1450,10 @@
       "//ui/gfx/geometry",
     ]
   }
+
+  if (use_inner_html_parser_fast_path) {
+    sources += [ "html/parser/html_document_parser_fastpath_test.cc" ]
+  }
 }
 
 group("unit_tests_data") {
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index b5501f06..897fe599 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -1024,7 +1024,10 @@
   for (bool& flag : cancel_running_animation_flags)
     flag = true;
 
-  if (animation_data && style_builder.Display() != EDisplay::kNone) {
+  if (animation_data &&
+      (style_builder.Display() != EDisplay::kNone ||
+       (RuntimeEnabledFeatures::CSSDisplayAnimationEnabled() && old_style &&
+        old_style->Display() != EDisplay::kNone))) {
     const Vector<AtomicString>& name_list = animation_data->NameList();
     for (wtf_size_t i = 0; i < name_list.size(); ++i) {
       AtomicString name = name_list[i];
@@ -2464,7 +2467,6 @@
     case CSSPropertyID::kContainerName:
     case CSSPropertyID::kContainerType:
     case CSSPropertyID::kDirection:
-    case CSSPropertyID::kDisplay:
     case CSSPropertyID::kTextCombineUpright:
     case CSSPropertyID::kTextOrientation:
     case CSSPropertyID::kToggleGroup:
@@ -2480,6 +2482,8 @@
     case CSSPropertyID::kWillChange:
     case CSSPropertyID::kWritingMode:
       return true;
+    case CSSPropertyID::kDisplay:
+      return !RuntimeEnabledFeatures::CSSDisplayAnimationEnabled();
     default:
       return false;
   }
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client.cc b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
index b302ed3..d53b645 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
@@ -30,7 +30,6 @@
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
 #include "third_party/blink/renderer/core/editing/visible_units.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
-#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/layout/layout_block.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
@@ -39,6 +38,7 @@
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/core/paint/paint_invalidator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
@@ -160,8 +160,13 @@
     if (layout_block_)
       layout_block_->SetShouldCheckForPaintInvalidation();
     layout_block_ = new_layout_block;
-    if (new_layout_block)
+
+    if (new_layout_block) {
       needs_paint_invalidation_ = true;
+      // The caret property tree space may have changed.
+      layout_block_->GetFrameView()->SetPaintArtifactCompositorNeedsUpdate(
+          PaintArtifactCompositorUpdateReason::kFrameCaretPaint);
+    }
   }
 
   if (!new_layout_block) {
@@ -173,6 +178,10 @@
   const NGPhysicalBoxFragment* const new_box_fragment =
       rect_and_block.box_fragment;
   if (new_box_fragment != box_fragment_) {
+    // The caret property tree space may have changed.
+    layout_block_->GetFrameView()->SetPaintArtifactCompositorNeedsUpdate(
+        PaintArtifactCompositorUpdateReason::kFrameCaretPaint);
+
     if (new_box_fragment)
       needs_paint_invalidation_ = true;
     box_fragment_ = new_box_fragment;
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index fab7ba2..b68a463c 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -30,7 +30,9 @@
 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
 
 #include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/timer/elapsed_timer.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
@@ -85,6 +87,10 @@
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
+#if defined(USE_INNER_HTML_PARSER_FAST_PATH)
+#include "third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.h"
+#endif
+
 namespace blink {
 
 using TaskRunnerHandle = scheduler::WebResourceLoadingTaskRunnerHandle;
@@ -743,7 +749,38 @@
   document.setAllowDeclarativeShadowRoots(include_shadow_roots);
 
   if (IsA<HTMLDocument>(document)) {
+#if defined(USE_INNER_HTML_PARSER_FAST_PATH)
+    const bool fast_path_enabled =
+        RuntimeEnabledFeatures::InnerHTMLParserFastpathEnabled();
+    base::ElapsedTimer parse_timer;
+    if (fast_path_enabled) {
+      const bool parsed_fast_path =
+          TryParsingHTMLFragment(markup, document, *fragment, *context_element,
+                                 parser_content_policy, include_shadow_roots);
+      if (parsed_fast_path) {
+        base::UmaHistogramTimes("Blink.HTMLFastPathParser.TotalParseTime",
+                                parse_timer.Elapsed());
+#if DCHECK_IS_ON()
+        // As a sanity check for the fast-path, create another fragment using
+        // the full parser and compare the results.
+        // See https://bugs.chromium.org/p/chromium/issues/detail?id=1407201
+        // for details.
+        DocumentFragment* fragment2 = DocumentFragment::Create(document);
+        fragment2->ParseHTML(markup, context_element, parser_content_policy);
+        DCHECK_EQ(CreateMarkup(fragment), CreateMarkup(fragment2))
+            << " supplied value " << markup;
+#endif
+        return fragment;
+      } else {
+        fragment = DocumentFragment::Create(document);
+      }
+    }
+#endif
     fragment->ParseHTML(markup, context_element, parser_content_policy);
+#if defined(USE_INNER_HTML_PARSER_FAST_PATH)
+    base::UmaHistogramTimes("Blink.HTMLFastPathParser.TotalParseTime",
+                            parse_timer.Elapsed());
+#endif
     return fragment;
   }
 
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.cc b/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.cc
new file mode 100644
index 0000000..a6e71d67
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.cc
@@ -0,0 +1,1095 @@
+// 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 "third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.h"
+
+#include <algorithm>
+#include <iostream>
+#include <type_traits>
+
+#include "base/metrics/histogram_functions.h"
+#include "base/timer/elapsed_timer.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/core/dom/attribute.h"
+#include "third_party/blink/renderer/core/dom/document_fragment.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/qualified_name.h"
+#include "third_party/blink/renderer/core/dom/text.h"
+#include "third_party/blink/renderer/core/html/forms/html_button_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"
+#include "third_party/blink/renderer/core/html/forms/html_label_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_option_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
+#include "third_party/blink/renderer/core/html/html_anchor_element.h"
+#include "third_party/blink/renderer/core/html/html_br_element.h"
+#include "third_party/blink/renderer/core/html/html_div_element.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/html/html_image_element.h"
+#include "third_party/blink/renderer/core/html/html_li_element.h"
+#include "third_party/blink/renderer/core/html/html_olist_element.h"
+#include "third_party/blink/renderer/core/html/html_paragraph_element.h"
+#include "third_party/blink/renderer/core/html/html_span_element.h"
+#include "third_party/blink/renderer/core/html/html_ulist_element.h"
+#include "third_party/blink/renderer/core/html/parser/atomic_html_token.h"
+#include "third_party/blink/renderer/core/html/parser/html_entity_parser.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_uchar.h"
+
+namespace blink {
+
+namespace {
+
+template <class Char, size_t n>
+bool operator==(base::span<const Char> span, const char (&s)[n]) {
+  if (span.size() != n - 1) {
+    return false;
+  }
+  for (size_t i = 0; i < n - 1; ++i) {
+    if (span[i] != s[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+template <int n>
+constexpr bool OnlyContainsLowercaseASCIILetters(const char (&s)[n]) {
+  for (int i = 0; i < n - 1; ++i) {
+    if (!('a' <= s[i] && s[i] <= 'z')) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// A hash function that is just good enough to distinguish the supported
+// tagnames. It needs to be adapted as soon as we have colliding tagnames.
+// The implementation was chosen to map to a dense integer range to allow for
+// compact switch jump-tables. If adding support for a new tag results in a
+// collision, then pick a new function that minimizes the number of operations
+// and results in a dense integer range. This will require some finesse, feel
+// free to reach out to owners of bug 1407201 for help.
+template <uint32_t n>
+constexpr uint32_t TagnameHash(const char (&s)[n]) {
+  // The fast-path parser only scans for letters in tagnames.
+  DCHECK(OnlyContainsLowercaseASCIILetters<n>(s));
+  DCHECK_EQ('\0', s[n - 1]);
+  // This function is called with null-termined string, which should be used in
+  // the hash implementation, hence the -2.
+  return (s[0] + 17 * s[n - 2]) & 63;
+}
+template <class Char>
+uint32_t TagnameHash(base::span<const Char> s) {
+  return (s[0] + 17 * s[s.size() - 1]) & 63;
+}
+uint32_t TagnameHash(const String& s) {
+  uint32_t l = s.length();
+  return (s[0] + 17 * s[l - 1]) & 63;
+}
+
+#define SUPPORTED_TAGS(V) \
+  V(A)                    \
+  V(B)                    \
+  V(Br)                   \
+  V(Button)               \
+  V(Div)                  \
+  V(Footer)               \
+  V(I)                    \
+  V(Img)                  \
+  V(Input)                \
+  V(Li)                   \
+  V(Label)                \
+  V(Option)               \
+  V(Ol)                   \
+  V(P)                    \
+  V(Select)               \
+  V(Span)                 \
+  V(Strong)               \
+  V(Ul)
+
+// This HTML parser is used as a fast-path for setting innerHTML.
+// It is faster than the general parser by only supporting a subset of valid
+// HTML. This way, it can be spec-compliant without following the algorithm
+// described in the spec. Unsupported features or parse errors lead to bailout,
+// falling back to the general HTML parser.
+// It differs from the general HTML parser in the following ways.
+//
+// Implementation:
+// - It uses recursive descent for better CPU branch prediction.
+// - It merges tokenization with parsing.
+// - Whenever possible, tokens are represented as subsequences of the original
+//   input, avoiding allocating memory for them.
+//
+// Restrictions (these may evolve based on uma data, https://crbug.com/1407201):
+// - No auto-closing of tags.
+// - Wrong nesting of HTML elements (for example nested <p>) leads to bailout
+//   instead of fix-up.
+// - No custom elements, no "is"-attribute.
+// - No duplicate attributes. This restriction could be lifted easily.
+// - Unquoted attribute names are very restricted.
+// - Many tags are unsupported, but we could support more. For example, <table>
+//   because of the complex re-parenting rules
+// - Only a few named "&" character references are supported.
+// - No Images with onload. This is because if parsing were to fail, 'onload'
+//   would be called multiple times.
+// - No '\0'. The handling of '\0' varies depending upon where it is found
+//   and in general the correct handling complicates things.
+// - Fails if Document::IsDirAttributeDirty. This relies on
+//   BeginParsingChildren() and FinishParsingChildren() being called.
+template <class Char>
+class HTMLFastPathParser {
+  STACK_ALLOCATED();
+  using Span = base::span<const Char>;
+  using USpan = base::span<const UChar>;
+  static_assert(std::is_same_v<Char, UChar> || std::is_same_v<Char, LChar>);
+
+ public:
+  HTMLFastPathParser(Span source,
+                     Document& document,
+                     DocumentFragment& fragment)
+      : source_(source), document_(document), fragment_(fragment) {}
+
+  bool Run(Element& context_element) {
+    QualifiedName context_tag = context_element.TagQName();
+    DCHECK(!context_tag.LocalName().empty());
+
+    // This switch checks that the context element is supported and applies the
+    // same restrictions regarding content as the fast-path parser does for a
+    // corresponding nested tag.
+    // This is to ensure that we preserve correct HTML structure with respect
+    // to the context tag.
+    //
+    // If this switch has duplicate cases, then `TagnameHash()` needs to be
+    // updated.
+    switch (TagnameHash(context_tag.LocalName())) {
+#define TAG_CASE(Tagname)                                     \
+  case TagnameHash(TagInfo::Tagname::tagname):                \
+    DCHECK(html_names::k##Tagname##Tag.LocalName().Ascii() == \
+           TagInfo::Tagname::tagname);                        \
+    if constexpr (!TagInfo::Tagname::is_void) {               \
+      /* The hash function won't return collisions for the */ \
+      /* supported tags, but this function takes */           \
+      /* potentially unsupported tags, which may collide. */  \
+      /* Protect against that by checking equality.  */       \
+      if (context_tag == html_names::k##Tagname##Tag) {       \
+        ParseCompleteInput<typename TagInfo::Tagname>();      \
+        return !failed_;                                      \
+      }                                                       \
+    }                                                         \
+    break;
+      SUPPORTED_TAGS(TAG_CASE)
+      default:
+        break;
+#undef TAG_CASE
+    }
+
+    Fail(HtmlFastPathResult::kFailedUnsupportedContextTag);
+    return false;
+  }
+
+  int NumberOfBytesParsed() const {
+    return sizeof(Char) * static_cast<int>(pos_ - source_.data());
+  }
+
+  HtmlFastPathResult parse_result() const { return parse_result_; }
+
+ private:
+  Span source_;
+  Document& document_;
+  DocumentFragment& fragment_;
+
+  const Char* const end_ = source_.data() + source_.size();
+  const Char* pos_ = source_.data();
+
+  bool failed_ = false;
+  bool inside_of_tag_a_ = false;
+  // 32 matches that used by HTMLToken::Attribute.
+  Vector<Char, 32> char_buffer_;
+  Vector<UChar> uchar_buffer_;
+  // Used if the attribute name contains upper case ascii (which must be
+  // mapped to lower case).
+  // 32 matches that used by HTMLToken::Attribute.
+  Vector<Char, 32> attribute_name_buffer_;
+  Vector<Attribute, kAttributePrealloc> attribute_buffer_;
+  Vector<StringImpl*> attribute_names_;
+  HtmlFastPathResult parse_result_ = HtmlFastPathResult::kSucceeded;
+
+  enum class PermittedParents {
+    kPhrasingOrFlowContent,  // allowed in phrasing content or flow content
+    kFlowContent,  // only allowed in flow content, not in phrasing content
+    kSpecial,      // only allowed for special parents
+  };
+
+  struct TagInfo {
+    template <class T, PermittedParents parents>
+    struct Tag {
+      using ElemClass = T;
+      static constexpr PermittedParents kPermittedParents = parents;
+      static ElemClass* Create(Document& document) {
+        return MakeGarbageCollected<ElemClass>(document);
+      }
+      static constexpr bool AllowedInPhrasingOrFlowContent() {
+        return kPermittedParents == PermittedParents::kPhrasingOrFlowContent;
+      }
+      static constexpr bool AllowedInFlowContent() {
+        return kPermittedParents == PermittedParents::kPhrasingOrFlowContent ||
+               kPermittedParents == PermittedParents::kFlowContent;
+      }
+    };
+
+    template <class T, PermittedParents parents>
+    struct VoidTag : Tag<T, parents> {
+      static constexpr bool is_void = true;
+    };
+
+    template <class T, PermittedParents parents>
+    struct ContainerTag : Tag<T, parents> {
+      static constexpr bool is_void = false;
+
+      static Element* ParseChild(HTMLFastPathParser& self) {
+        return self.ParseElement</*non_phrasing_content*/ true>();
+      }
+    };
+
+    // A tag that can only contain phrasing content.
+    // If a tag is considered phrasing content itself is decided by
+    // `allowed_in_phrasing_content`.
+    template <class T, PermittedParents parents>
+    struct ContainsPhrasingContentTag : ContainerTag<T, parents> {
+      static constexpr bool is_void = false;
+
+      static Element* ParseChild(HTMLFastPathParser& self) {
+        return self.ParseElement</*non_phrasing_content*/ false>();
+      }
+    };
+
+    struct A : ContainerTag<HTMLAnchorElement, PermittedParents::kFlowContent> {
+      static constexpr const char tagname[] = "a";
+
+      static Element* ParseChild(HTMLFastPathParser& self) {
+        DCHECK(!self.inside_of_tag_a_);
+        self.inside_of_tag_a_ = true;
+        Element* res =
+            ContainerTag<HTMLAnchorElement,
+                         PermittedParents::kFlowContent>::ParseChild(self);
+        self.inside_of_tag_a_ = false;
+        return res;
+      }
+    };
+
+    struct AWithPhrasingContent
+        : ContainsPhrasingContentTag<HTMLAnchorElement,
+                                     PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "a";
+
+      static Element* ParseChild(HTMLFastPathParser& self) {
+        DCHECK(!self.inside_of_tag_a_);
+        self.inside_of_tag_a_ = true;
+        Element* res = ContainsPhrasingContentTag<
+            HTMLAnchorElement,
+            PermittedParents::kPhrasingOrFlowContent>::ParseChild(self);
+        self.inside_of_tag_a_ = false;
+        return res;
+      }
+    };
+
+    struct B
+        : ContainsPhrasingContentTag<HTMLElement,
+                                     PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "b";
+      static HTMLElement* Create(Document& document) {
+        return MakeGarbageCollected<HTMLElement>(html_names::kBTag, document);
+      }
+    };
+
+    struct Br
+        : VoidTag<HTMLBRElement, PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "br";
+    };
+
+    struct Button
+        : ContainsPhrasingContentTag<HTMLButtonElement,
+                                     PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "button";
+    };
+
+    struct Div : ContainerTag<HTMLDivElement, PermittedParents::kFlowContent> {
+      static constexpr const char tagname[] = "div";
+    };
+
+    struct Footer
+        : ContainerTag<HTMLDivElement, PermittedParents::kFlowContent> {
+      static constexpr const char tagname[] = "footer";
+      static HTMLElement* Create(Document& document) {
+        return MakeGarbageCollected<HTMLElement>(html_names::kFooterTag,
+                                                 document);
+      }
+    };
+
+    struct I
+        : ContainsPhrasingContentTag<HTMLElement,
+                                     PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "i";
+      static HTMLElement* Create(Document& document) {
+        return MakeGarbageCollected<HTMLElement>(html_names::kITag, document);
+      }
+    };
+
+    struct Img
+        : VoidTag<HTMLImageElement, PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "img";
+    };
+
+    struct Input
+        : VoidTag<HTMLInputElement, PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "input";
+      static HTMLInputElement* Create(Document& document) {
+        return MakeGarbageCollected<HTMLInputElement>(
+            document, CreateElementFlags::ByFragmentParser(&document));
+      }
+    };
+
+    struct Li : ContainerTag<HTMLLIElement, PermittedParents::kSpecial> {
+      static constexpr const char tagname[] = "li";
+    };
+
+    struct Label
+        : ContainsPhrasingContentTag<HTMLLabelElement,
+                                     PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "label";
+    };
+
+    struct Option
+        : ContainerTag<HTMLOptionElement, PermittedParents::kSpecial> {
+      static constexpr const char tagname[] = "option";
+      static Element* ParseChild(HTMLFastPathParser& self) {
+        // <option> can only contain a text content.
+        return self.Fail(HtmlFastPathResult::kFailedOptionWithChild, nullptr);
+      }
+    };
+
+    struct Ol : ContainerTag<HTMLOListElement, PermittedParents::kFlowContent> {
+      static constexpr const char tagname[] = "ol";
+
+      static Element* ParseChild(HTMLFastPathParser& self) {
+        return self.ParseSpecificElements<Li>();
+      }
+    };
+
+    struct P : ContainsPhrasingContentTag<HTMLParagraphElement,
+                                          PermittedParents::kFlowContent> {
+      static constexpr const char tagname[] = "p";
+    };
+
+    struct Select : ContainerTag<HTMLSelectElement,
+                                 PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "select";
+      static Element* ParseChild(HTMLFastPathParser& self) {
+        return self.ParseSpecificElements<Option>();
+      }
+    };
+
+    struct Span
+        : ContainsPhrasingContentTag<HTMLSpanElement,
+                                     PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "span";
+    };
+
+    struct Strong
+        : ContainsPhrasingContentTag<HTMLElement,
+                                     PermittedParents::kPhrasingOrFlowContent> {
+      static constexpr const char tagname[] = "strong";
+      static HTMLElement* Create(Document& document) {
+        return MakeGarbageCollected<HTMLElement>(html_names::kStrongTag,
+                                                 document);
+      }
+    };
+
+    struct Ul : ContainerTag<HTMLUListElement, PermittedParents::kFlowContent> {
+      static constexpr const char tagname[] = "ul";
+
+      static Element* ParseChild(HTMLFastPathParser& self) {
+        return self.ParseSpecificElements<Li>();
+      }
+    };
+  };
+
+  template <class ParentTag>
+  void ParseCompleteInput() {
+    ParseChildren<ParentTag>(&fragment_);
+    if (pos_ != end_) {
+      Fail(HtmlFastPathResult::kFailedDidntReachEndOfInput);
+    }
+  }
+
+  // Match ASCII Whitespace according to
+  // https://infra.spec.whatwg.org/#ascii-whitespace
+  bool IsWhitespace(Char c) {
+    switch (c) {
+      case ' ':
+      case '\t':
+      case '\n':
+      case '\r':
+      case '\f':
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  bool IsValidUnquotedAttributeValueChar(Char c) {
+    return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
+           ('0' <= c && c <= '9') || c == '_' || c == '-';
+  }
+
+  // https://html.spec.whatwg.org/#syntax-attribute-name
+  bool IsValidAttributeNameChar(Char c) {
+    if (c == '=') {
+      // Early exit for the most common way to end an attribute.
+      return false;
+    }
+    return ('a' <= c && c <= 'z') || c == '-' || ('A' <= c && c <= 'Z') ||
+           ('0' <= c && c <= '9');
+  }
+
+  bool IsCharAfterTagnameOrAttribute(Char c) {
+    return c == ' ' || c == '>' || IsWhitespace(c) || c == '/';
+  }
+
+  bool IsCharAfterUnquotedAttribute(Char c) {
+    return c == ' ' || c == '>' || IsWhitespace(c);
+  }
+
+  void SkipWhitespace() {
+    while (pos_ != end_ && IsWhitespace(*pos_)) {
+      ++pos_;
+    }
+  }
+
+  // We first try to scan text as an unmodified subsequence of the input.
+  // However, if there are escape sequences, we have to copy the text to a
+  // separate buffer and we might go outside of `Char` range if we are in an
+  // `LChar` parser. Therefore, this function returns either a `Span` or a
+  // `USpan`. Callers distinguish the two cases by checking if the `Span` is
+  // empty, as only one of them can be non-empty.
+  std::pair<Span, USpan> ScanText() {
+    const Char* start = pos_;
+    while (pos_ != end_ && *pos_ != '<') {
+      // '&' indicates escape sequences, '\r' might require
+      // https://infra.spec.whatwg.org/#normalize-newlines
+      if (*pos_ == '&' || *pos_ == '\r') {
+        pos_ = start;
+        return {Span{}, ScanEscapedText()};
+      } else if (UNLIKELY(*pos_ == '\0')) {
+        return Fail(HtmlFastPathResult::kFailedContainsNull,
+                    std::pair{Span{}, USpan{}});
+      }
+      ++pos_;
+    }
+    return {{start, static_cast<size_t>(pos_ - start)}, USpan{}};
+  }
+
+  // Slow-path of `ScanText()`, which supports escape sequences by copying to a
+  // separate buffer.
+  USpan ScanEscapedText() {
+    uchar_buffer_.resize(0);
+    while (pos_ != end_ && *pos_ != '<') {
+      if (*pos_ == '&') {
+        ScanHTMLCharacterReference(&uchar_buffer_);
+        if (failed_) {
+          return USpan{};
+        }
+      } else if (*pos_ == '\r') {
+        // Normalize "\r\n" to "\n" according to
+        // https://infra.spec.whatwg.org/#normalize-newlines.
+        if (pos_ + 1 != end_ && pos_[1] == '\n') {
+          ++pos_;
+        }
+        uchar_buffer_.push_back('\n');
+        ++pos_;
+      } else if (UNLIKELY(*pos_ == '\0')) {
+        return Fail(HtmlFastPathResult::kFailedContainsNull, USpan{});
+      } else {
+        uchar_buffer_.push_back(*pos_);
+        ++pos_;
+      }
+    }
+    return {uchar_buffer_.data(), uchar_buffer_.size()};
+  }
+
+  // Scan a tagname and convert to lowercase if necessary.
+  Span ScanTagname() {
+    const Char* start = pos_;
+    while (pos_ != end_ && 'a' <= *pos_ && *pos_ <= 'z') {
+      ++pos_;
+    }
+    if (pos_ == end_ || !IsCharAfterTagnameOrAttribute(*pos_)) {
+      // Try parsing a case-insensitive tagname.
+      char_buffer_.resize(0);
+      pos_ = start;
+      while (pos_ != end_) {
+        Char c = *pos_;
+        if ('A' <= c && c <= 'Z') {
+          c = c - ('A' - 'a');
+        } else if (!('a' <= c && c <= 'z')) {
+          break;
+        }
+        ++pos_;
+        char_buffer_.push_back(c);
+      }
+      if (pos_ == end_ || !IsCharAfterTagnameOrAttribute(*pos_)) {
+        return Fail(HtmlFastPathResult::kFailedParsingTagName, Span{});
+      }
+      SkipWhitespace();
+      return Span{char_buffer_.data(), char_buffer_.size()};
+    }
+    Span res = Span{start, static_cast<size_t>(pos_ - start)};
+    SkipWhitespace();
+    return res;
+  }
+
+  Span ScanAttrName() {
+    // First look for all lower case. This path doesn't require any mapping of
+    // input. This path could handle other valid attribute name chars, but they
+    // are not as common, so it only looks for lowercase.
+    const Char* start = pos_;
+    while (pos_ != end_ && *pos_ >= 'a' && *pos_ <= 'z') {
+      ++pos_;
+    }
+    if (UNLIKELY(pos_ == end_)) {
+      return Fail(HtmlFastPathResult::kFailedEndOfInputReached, Span());
+    }
+    if (!IsValidAttributeNameChar(*pos_)) {
+      return Span(start, static_cast<size_t>(pos_ - start));
+    }
+
+    // At this point name does not contain lowercase. It may contain upper-case,
+    // which requires mapping. Assume it does.
+    pos_ = start;
+    attribute_name_buffer_.resize(0);
+    Char c;
+    while (c = GetNext(), IsValidAttributeNameChar(c)) {
+      if ('A' <= c && c <= 'Z') {
+        c = c - ('A' - 'a');
+      }
+      attribute_name_buffer_.push_back(c);
+      ++pos_;
+    }
+    return Span(attribute_name_buffer_.data(),
+                static_cast<size_t>(attribute_name_buffer_.size()));
+  }
+
+  std::pair<Span, USpan> ScanAttrValue() {
+    Span result;
+    SkipWhitespace();
+    const Char* start = pos_;
+    if (Char quote_char = GetNext(); quote_char == '"' || quote_char == '\'') {
+      start = ++pos_;
+      while (GetNext() != quote_char) {
+        if (GetNext() == '&' || GetNext() == '\r') {
+          pos_ = start - 1;
+          return {Span{}, ScanEscapedAttrValue()};
+        }
+        ++pos_;
+      }
+      result = Span{start, static_cast<size_t>(pos_ - start)};
+      if (ConsumeNext() != quote_char) {
+        return Fail(HtmlFastPathResult::kFailedParsingQuotedAttributeValue,
+                    std::pair{Span{}, USpan{}});
+      }
+    } else {
+      while (IsValidUnquotedAttributeValueChar(GetNext())) {
+        ++pos_;
+      }
+      result = Span{start, static_cast<size_t>(pos_ - start)};
+      if (!IsCharAfterUnquotedAttribute(GetNext())) {
+        return Fail(HtmlFastPathResult::kFailedParsingUnquotedAttributeValue,
+                    std::pair{Span{}, USpan{}});
+      }
+    }
+    return {result, USpan{}};
+  }
+
+  // Slow path for scanning an attribute value. Used for special cases such
+  // as '&' and '\r'.
+  USpan ScanEscapedAttrValue() {
+    Span result;
+    SkipWhitespace();
+    uchar_buffer_.resize(0);
+    const Char* start = pos_;
+    if (Char quote_char = GetNext(); quote_char == '"' || quote_char == '\'') {
+      start = ++pos_;
+      while (GetNext() != quote_char) {
+        if (failed_) {
+          return USpan{};
+        }
+        if (GetNext() == '&') {
+          ScanHTMLCharacterReference(&uchar_buffer_);
+        } else if (GetNext() == '\r') {
+          // Normalize "\r\n" to "\n" according to
+          // https://infra.spec.whatwg.org/#normalize-newlines.
+          if (pos_ + 1 != end_ && pos_[1] == '\n') {
+            ++pos_;
+          }
+          uchar_buffer_.push_back('\n');
+          ++pos_;
+        } else {
+          uchar_buffer_.push_back(*pos_);
+          ++pos_;
+        }
+      }
+      result = Span{start, static_cast<size_t>(pos_ - start)};
+      if (ConsumeNext() != quote_char) {
+        return Fail(
+            HtmlFastPathResult::kFailedParsingQuotedEscapedAttributeValue,
+            USpan{});
+      }
+    } else {
+      return Fail(
+          HtmlFastPathResult::kFailedParsingUnquotedEscapedAttributeValue,
+          USpan{});
+    }
+    return USpan{uchar_buffer_.data(), uchar_buffer_.size()};
+  }
+
+  void ScanHTMLCharacterReference(Vector<UChar>* out) {
+    DCHECK_EQ(*pos_, '&');
+    ++pos_;
+    const Char* start = pos_;
+    while (true) {
+      // A rather arbitrary constant to prevent unbounded lookahead in the case
+      // of ill-formed input.
+      constexpr int kMaxLength = 20;
+      if (pos_ == end_ || pos_ - start > kMaxLength ||
+          UNLIKELY(*pos_ == '\0')) {
+        return Fail(HtmlFastPathResult::kFailedParsingCharacterReference);
+      }
+      if (ConsumeNext() == ';') {
+        break;
+      }
+    }
+    Span reference = Span{start, static_cast<size_t>(pos_ - start) - 1};
+    // There are no valid character references shorter than that. The check
+    // protects the indexed accesses below.
+    constexpr size_t kMinLength = 2;
+    if (reference.size() < kMinLength) {
+      return Fail(HtmlFastPathResult::kFailedParsingCharacterReference);
+    }
+    if (reference[0] == '#') {
+      UChar32 res = 0;
+      if (reference[1] == 'x' || reference[1] == 'X') {
+        for (size_t i = 2; i < reference.size(); ++i) {
+          Char c = reference[i];
+          res *= 16;
+          if (c >= '0' && c <= '9') {
+            res += c - '0';
+          } else if (c >= 'a' && c <= 'f') {
+            res += c - 'a' + 10;
+          } else if (c >= 'A' && c <= 'F') {
+            res += c - 'A' + 10;
+          } else {
+            return Fail(HtmlFastPathResult::kFailedParsingCharacterReference);
+          }
+          if (res > UCHAR_MAX_VALUE) {
+            return Fail(HtmlFastPathResult::kFailedParsingCharacterReference);
+          }
+        }
+      } else {
+        for (size_t i = 1; i < reference.size(); ++i) {
+          Char c = reference[i];
+          res *= 10;
+          if (c >= '0' && c <= '9') {
+            res += c - '0';
+          } else {
+            return Fail(HtmlFastPathResult::kFailedParsingCharacterReference);
+          }
+          if (res > UCHAR_MAX_VALUE) {
+            return Fail(HtmlFastPathResult::kFailedParsingCharacterReference);
+          }
+        }
+      }
+      DecodedHTMLEntity entity;
+      AppendLegalEntityFor(res, entity);
+      for (size_t i = 0; i < entity.length; ++i) {
+        out->push_back(entity.data[i]);
+      }
+      // Handle the most common named references.
+    } else if (reference == "amp") {
+      out->push_back('&');
+    } else if (reference == "lt") {
+      out->push_back('<');
+    } else if (reference == "gt") {
+      out->push_back('>');
+    } else if (reference == "nbsp") {
+      out->push_back(0xa0);
+    } else {
+      // This handles uncommon named references.
+      String input_string{reference.data(),
+                          static_cast<unsigned>(reference.size())};
+      SegmentedString input_segmented{input_string};
+      DecodedHTMLEntity entity;
+      bool not_enough_characters = false;
+      if (!ConsumeHTMLEntity(input_segmented, entity, not_enough_characters) ||
+          not_enough_characters) {
+        return Fail(HtmlFastPathResult::kFailedParsingCharacterReference);
+      }
+      for (size_t i = 0; i < entity.length; ++i) {
+        out->push_back(entity.data[i]);
+      }
+    }
+  }
+
+  void Fail(HtmlFastPathResult result) {
+    // This function may be called multiple times. Only record the result the
+    // first time it's called.
+    if (failed_) {
+      return;
+    }
+    parse_result_ = result;
+    failed_ = true;
+  }
+
+  template <class R>
+  R Fail(HtmlFastPathResult result, R res) {
+    Fail(result);
+    return res;
+  }
+
+  Char GetNext() {
+    if (pos_ == end_) {
+      Fail(HtmlFastPathResult::kFailedEndOfInputReached);
+      return '\0';
+    }
+    return *pos_;
+  }
+
+  Char ConsumeNext() {
+    if (pos_ == end_) {
+      return Fail(HtmlFastPathResult::kFailedEndOfInputReached, '\0');
+    }
+    return *(pos_++);
+  }
+
+  template <class ParentTag>
+  void ParseChildren(ContainerNode* parent) {
+    while (true) {
+      std::pair<Span, USpan> text = ScanText();
+      if (failed_) {
+        return;
+      }
+      DCHECK(text.first.empty() || text.second.empty());
+      if (!text.first.empty()) {
+        parent->ParserAppendChild(Text::Create(
+            document_, String(text.first.data(),
+                              static_cast<unsigned>(text.first.size()))));
+      } else if (!text.second.empty()) {
+        parent->ParserAppendChild(Text::Create(
+            document_, String(text.second.data(),
+                              static_cast<unsigned>(text.second.size()))));
+      }
+      if (pos_ == end_) {
+        return;
+      }
+      DCHECK_EQ(*pos_, '<');
+      ++pos_;
+      if (GetNext() == '/') {
+        // We assume that we found the closing tag. The tagname will be checked
+        // by the caller `ParseContainerElement()`.
+        return;
+      } else {
+        Element* child = ParentTag::ParseChild(*this);
+        if (failed_) {
+          return;
+        }
+        parent->ParserAppendChild(child);
+      }
+    }
+  }
+
+  Attribute ProcessAttribute(Span name_span,
+                             std::pair<Span, USpan> value_span) {
+    QualifiedName name = LookupHTMLAttributeName(
+        name_span.data(), static_cast<unsigned>(name_span.size()));
+    if (name == g_null_name) {
+      name =
+          QualifiedName(g_null_atom,
+                        AtomicString(name_span.data(),
+                                     static_cast<unsigned>(name_span.size())),
+                        g_null_atom);
+    }
+
+    // The string pointer in |value| is null for attributes with no values, but
+    // the null atom is used to represent absence of attributes; attributes with
+    // no values have the value set to an empty atom instead.
+    AtomicString value;
+    if (value_span.second.empty()) {
+      value = AtomicString(value_span.first.data(),
+                           static_cast<unsigned>(value_span.first.size()));
+    } else {
+      value = AtomicString(value_span.second.data(),
+                           static_cast<unsigned>(value_span.second.size()));
+    }
+    if (value.IsNull()) {
+      value = g_empty_atom;
+    }
+    return Attribute(std::move(name), std::move(value));
+  }
+
+  void ParseAttributes(Element* parent) {
+    DCHECK(attribute_buffer_.empty());
+    DCHECK(attribute_names_.empty());
+    while (true) {
+      Span attr_name = ScanAttrName();
+      if (attr_name.empty()) {
+        if (GetNext() == '>') {
+          ++pos_;
+          break;
+        } else if (GetNext() == '/') {
+          ++pos_;
+          SkipWhitespace();
+          if (ConsumeNext() != '>') {
+            return Fail(HtmlFastPathResult::kFailedParsingAttributes);
+          }
+          break;
+        } else {
+          return Fail(HtmlFastPathResult::kFailedParsingAttributes);
+        }
+      }
+      SkipWhitespace();
+      std::pair<Span, USpan> attr_value = {};
+      if (GetNext() == '=') {
+        ++pos_;
+        attr_value = ScanAttrValue();
+        SkipWhitespace();
+      }
+      Attribute attribute = ProcessAttribute(attr_name, attr_value);
+      attribute_buffer_.push_back(attribute);
+      if (attribute.GetName() == html_names::kIsAttr ||
+          attribute.GetName() == html_names::kOnloadAttr) {
+        return Fail(HtmlFastPathResult::kFailedParsingAttributes);
+      }
+      attribute_names_.push_back(attribute.LocalName().Impl());
+    }
+    std::sort(attribute_names_.begin(), attribute_names_.end());
+    if (std::adjacent_find(attribute_names_.begin(), attribute_names_.end()) !=
+        attribute_names_.end()) {
+      // Found duplicate attributes. We would have to ignore repeated
+      // attributes, but leave this to the general parser instead.
+      return Fail(HtmlFastPathResult::kFailedParsingAttributes);
+    }
+    parent->ParserSetAttributes(attribute_buffer_);
+    attribute_buffer_.clear();
+    attribute_names_.resize(0);
+  }
+
+  template <class... Tags>
+  Element* ParseSpecificElements() {
+    Span tagname = ScanTagname();
+    return ParseSpecificElements<Tags...>(tagname);
+  }
+
+  template <void* = nullptr>
+  Element* ParseSpecificElements(Span tagname) {
+    return Fail(HtmlFastPathResult::kFailedParsingSpecificElements, nullptr);
+  }
+
+  template <class Tag, class... OtherTags>
+  Element* ParseSpecificElements(Span tagname) {
+    if (tagname == Tag::tagname) {
+      return ParseElementAfterTagname<Tag>();
+    }
+    return ParseSpecificElements<OtherTags...>(tagname);
+  }
+
+  template <bool non_phrasing_content>
+  Element* ParseElement() {
+    Span tagname = ScanTagname();
+    if (tagname.empty()) {
+      return Fail(HtmlFastPathResult::kFailedParsingElement, nullptr);
+    }
+    // HTML has complicated rules around auto-closing tags and re-parenting
+    // DOM nodes. We avoid complications with auto-closing rules by disallowing
+    // certain nesting. In particular, we bail out if non-phrasing-content
+    // elements are nested into elements that require phrasing content.
+    // Similarly, we disallow nesting <a> tags. But tables for example have
+    // complex re-parenting rules that cannot be captured in this way, so we
+    // cannot support them.
+    //
+    // If this switch has duplicate cases, then `TagnameHash()` needs to be
+    // updated.
+    switch (TagnameHash(tagname)) {
+#define TAG_CASE(Tagname)                                                     \
+  case TagnameHash(TagInfo::Tagname::tagname):                                \
+    if (std::is_same_v<typename TagInfo::A, typename TagInfo::Tagname>) {     \
+      goto case_a;                                                            \
+    }                                                                         \
+    if constexpr (non_phrasing_content                                        \
+                      ? TagInfo::Tagname::AllowedInFlowContent()              \
+                      : TagInfo::Tagname::AllowedInPhrasingOrFlowContent()) { \
+      /* See comment in Run() for details on why equality is checked */       \
+      /* here. */                                                             \
+      if (tagname == TagInfo::Tagname::tagname) {                             \
+        return ParseElementAfterTagname<typename TagInfo::Tagname>();         \
+      }                                                                       \
+    }                                                                         \
+    break;
+
+      SUPPORTED_TAGS(TAG_CASE)
+#undef TAG_CASE
+
+    case_a:
+      // <a> tags must not be nested, because HTML parsing would auto-close
+      // the outer one when encountering a nested one.
+      if (tagname == TagInfo::A::tagname && !inside_of_tag_a_) {
+        return non_phrasing_content
+                   ? ParseElementAfterTagname<typename TagInfo::A>()
+                   : ParseElementAfterTagname<
+                         typename TagInfo::AWithPhrasingContent>();
+      }
+      break;
+      default:
+        break;
+    }
+    return Fail(HtmlFastPathResult::kFailedUnsupportedTag, nullptr);
+  }
+
+  template <class Tag>
+  Element* ParseElementAfterTagname() {
+    if constexpr (Tag::is_void) {
+      return ParseVoidElement(Tag::Create(document_));
+    } else {
+      return ParseContainerElement<Tag>(Tag::Create(document_));
+    }
+  }
+
+  template <class Tag>
+  Element* ParseContainerElement(Element* element) {
+    ParseAttributes(element);
+    if (failed_) {
+      return element;
+    }
+    ParseChildren<Tag>(element);
+    if (failed_ || pos_ == end_) {
+      return Fail(HtmlFastPathResult::kFailedEndOfInputReachedForContainer,
+                  element);
+    }
+    // ParseChildren<Tag>(element) stops after the (hopefully) closing tag's `<`
+    // and fails if the the current char is not '/'.
+    DCHECK_EQ(*pos_, '/');
+    ++pos_;
+    Span endtag = ScanTagname();
+    if (endtag == Tag::tagname) {
+      if (ConsumeNext() != '>') {
+        return Fail(HtmlFastPathResult::kFailedUnexpectedTagNameCloseState,
+                    element);
+      }
+    } else {
+      return Fail(HtmlFastPathResult::kFailedEndTagNameMismatch, element);
+    }
+    return element;
+  }
+
+  Element* ParseVoidElement(Element* element) {
+    ParseAttributes(element);
+    return element;
+  }
+};
+
+void LogFastPathResult(HtmlFastPathResult result) {
+  base::UmaHistogramEnumeration("Blink.HTMLFastPathParser.ParseResult", result);
+  if (result != HtmlFastPathResult::kSucceeded) {
+    VLOG(2) << "innerHTML fast-path parser failed, "
+            << static_cast<int>(result);
+  }
+}
+
+bool CanUseFastPath(Document& document,
+                    Element& context_element,
+                    ParserContentPolicy policy,
+                    bool include_shadow_roots) {
+  if (include_shadow_roots) {
+    LogFastPathResult(HtmlFastPathResult::kFailedShadowRoots);
+    return false;
+  }
+
+  // Disable when tracing is enabled to preserve trace behavior.
+  bool tracing_enabled = false;
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("devtools.timeline", &tracing_enabled);
+  if (tracing_enabled) {
+    LogFastPathResult(HtmlFastPathResult::kFailedTracingEnabled);
+    return false;
+  }
+
+  // We could probably allow other content policies too, as we do not support
+  // scripts or plugins anyway.
+  if (policy != ParserContentPolicy::kAllowScriptingContent) {
+    LogFastPathResult(HtmlFastPathResult::kFailedParserContentPolicy);
+    return false;
+  }
+  // If we are within a form element, we would need to create associations,
+  // which we do not. Therefore, we do not support this case.
+  // See HTMLConstructionSite::InitFragmentParsing() and
+  // HTMLConstructionSite::CreateElement() for the corresponding code on the
+  // slow-path.
+  if (!context_element.GetDocument().IsTemplateDocument() &&
+      Traversal<HTMLFormElement>::FirstAncestorOrSelf(context_element) !=
+          nullptr) {
+    LogFastPathResult(HtmlFastPathResult::kFailedInForm);
+    return false;
+  }
+
+  // State used for this is updated in BeginParsingChildren() and
+  // FinishParsingChildren(), which this does not call.
+  if (document.IsDirAttributeDirty()) {
+    LogFastPathResult(HtmlFastPathResult::kFailedDirAttributeDirty);
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+bool TryParsingHTMLFragment(const String& source,
+                            Document& document,
+                            DocumentFragment& fragment,
+                            Element& context_element,
+                            ParserContentPolicy policy,
+                            bool include_shadow_roots) {
+  if (!CanUseFastPath(document, context_element, policy,
+                      include_shadow_roots)) {
+    return false;
+  }
+  base::ElapsedTimer parse_timer;
+  bool success;
+  int number_of_bytes_parsed;
+  if (source.Is8Bit()) {
+    HTMLFastPathParser<LChar> parser{source.Span8(), document, fragment};
+    success = parser.Run(context_element);
+    number_of_bytes_parsed = parser.NumberOfBytesParsed();
+    LogFastPathResult(parser.parse_result());
+  } else {
+    HTMLFastPathParser<UChar> parser{source.Span16(), document, fragment};
+    success = parser.Run(context_element);
+    number_of_bytes_parsed = parser.NumberOfBytesParsed();
+    LogFastPathResult(parser.parse_result());
+  }
+  if (success) {
+    base::UmaHistogramTimes("Blink.HTMLFastPathParser.SuccessfulParseTime",
+                            parse_timer.Elapsed());
+  } else {
+    base::UmaHistogramTimes("Blink.HTMLFastPathParser.AbortedParseTime",
+                            parse_timer.Elapsed());
+  }
+  base::UmaHistogramCounts10M(
+      success ? "Blink.HTMLFastPathParser.SuccessfulParseSize"
+              : "Blink.HTMLFastPathParser.AbortedParseSize",
+      number_of_bytes_parsed);
+  return success;
+}
+
+#undef SUPPORTED_TAGS
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.h b/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.h
new file mode 100644
index 0000000..a5cda1f
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.h
@@ -0,0 +1,59 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_HTML_DOCUMENT_PARSER_FASTPATH_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_HTML_DOCUMENT_PARSER_FASTPATH_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/dom/parser_content_policy.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class Document;
+class DocumentFragment;
+class Element;
+
+CORE_EXPORT bool TryParsingHTMLFragment(const String& source,
+                                        Document& document,
+                                        DocumentFragment& fragment,
+                                        Element& context_element,
+                                        ParserContentPolicy policy,
+                                        bool include_shadow_roots);
+
+// Captures the potential outcomes for fast path html parser.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// This is exposed for testing.
+enum class HtmlFastPathResult {
+  kSucceeded = 0,
+  kFailedTracingEnabled = 1,
+  kFailedParserContentPolicy = 2,
+  kFailedInForm = 3,
+  kFailedUnsupportedContextTag = 4,
+  kFailedOptionWithChild = 5,
+  kFailedDidntReachEndOfInput = 6,
+  kFailedContainsNull = 7,
+  kFailedParsingTagName = 8,
+  kFailedParsingQuotedAttributeValue = 9,
+  kFailedParsingUnquotedAttributeValue = 10,
+  kFailedParsingQuotedEscapedAttributeValue = 11,
+  kFailedParsingUnquotedEscapedAttributeValue = 12,
+  kFailedParsingCharacterReference = 13,
+  kFailedEndOfInputReached = 14,
+  kFailedParsingAttributes = 15,
+  kFailedParsingSpecificElements = 16,
+  kFailedParsingElement = 17,
+  kFailedUnsupportedTag = 18,
+  kFailedEndOfInputReachedForContainer = 19,
+  kFailedUnexpectedTagNameCloseState = 20,
+  kFailedEndTagNameMismatch = 21,
+  kFailedShadowRoots = 22,
+  kFailedDirAttributeDirty = 23,
+  kMaxValue = kFailedDirAttributeDirty
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_HTML_DOCUMENT_PARSER_FASTPATH_H_
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath_test.cc b/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath_test.cc
new file mode 100644
index 0000000..d191b75
--- /dev/null
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath_test.cc
@@ -0,0 +1,63 @@
+// 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/renderer/core/html/parser/html_document_parser_fastpath.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/dom/document_fragment.h"
+#include "third_party/blink/renderer/core/html/html_div_element.h"
+#include "third_party/blink/renderer/core/html/html_document.h"
+#include "third_party/blink/renderer/core/testing/null_execution_context.h"
+
+namespace blink {
+namespace {
+
+TEST(HTMLDocumentParserFastpathTest, SanityCheck) {
+  ScopedNullExecutionContext execution_context;
+  auto* document =
+      HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
+  document->write("<body></body>");
+  auto* div = MakeGarbageCollected<HTMLDivElement>(*document);
+  document->body()->AppendChild(div);
+  DocumentFragment* fragment = DocumentFragment::Create(*document);
+  EXPECT_TRUE(TryParsingHTMLFragment(
+      "<div>test</div>", *document, *fragment, *div,
+      ParserContentPolicy::kAllowScriptingContent, false));
+}
+
+TEST(HTMLDocumentParserFastpathTest, SetInnerHTMLUsesFastPathSuccess) {
+  ScopedNullExecutionContext execution_context;
+  auto* document =
+      HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
+  document->write("<body></body>");
+  auto* div = MakeGarbageCollected<HTMLDivElement>(*document);
+
+  base::HistogramTester histogram_tester;
+  div->setInnerHTML("<div>test</div>");
+  // This was html the fast path handled, so there should be one histogram with
+  // success.
+  histogram_tester.ExpectTotalCount("Blink.HTMLFastPathParser.ParseResult", 1);
+  histogram_tester.ExpectUniqueSample("Blink.HTMLFastPathParser.ParseResult",
+                                      HtmlFastPathResult::kSucceeded, 1);
+}
+
+TEST(HTMLDocumentParserFastpathTest, SetInnerHTMLUsesFastPathFailure) {
+  ScopedNullExecutionContext execution_context;
+  auto* document =
+      HTMLDocument::CreateForTest(execution_context.GetExecutionContext());
+  document->write("<body></body>");
+  auto* div = MakeGarbageCollected<HTMLDivElement>(*document);
+
+  base::HistogramTester histogram_tester;
+  div->setInnerHTML("<div");
+  // The fast path should not have handled this, so there should be one
+  // histogram with a value other then success.
+  histogram_tester.ExpectTotalCount("Blink.HTMLFastPathParser.ParseResult", 1);
+  histogram_tester.ExpectBucketCount("Blink.HTMLFastPathParser.ParseResult",
+                                     HtmlFastPathResult::kSucceeded, 0);
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_entity_parser.cc b/third_party/blink/renderer/core/html/parser/html_entity_parser.cc
index 231cd4c5..6d3a77a 100644
--- a/third_party/blink/renderer/core/html/parser/html_entity_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_entity_parser.cc
@@ -52,7 +52,7 @@
   return kWindowsLatin1ExtensionArray[value - 0x80];
 }
 
-static void AppendLegalEntityFor(UChar32 c, DecodedHTMLEntity& decoded_entity) {
+void AppendLegalEntityFor(UChar32 c, DecodedHTMLEntity& decoded_entity) {
   // FIXME: A number of specific entity values generate parse errors.
   if (c <= 0 || c > 0x10FFFF || (c >= 0xD800 && c <= 0xDFFF)) {
     decoded_entity.Append(0xFFFD);
diff --git a/third_party/blink/renderer/core/html/parser/html_entity_parser.h b/third_party/blink/renderer/core/html/parser/html_entity_parser.h
index 4f936a8..b81b7a8 100644
--- a/third_party/blink/renderer/core/html/parser/html_entity_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_entity_parser.h
@@ -63,6 +63,8 @@
   UChar data[kMaxLength];
 };
 
+void AppendLegalEntityFor(UChar32 c, DecodedHTMLEntity& decoded_entity);
+
 CORE_EXPORT bool ConsumeHTMLEntity(SegmentedString&,
                                    DecodedHTMLEntity& decoded_entity,
                                    bool& not_enough_characters,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
index 986e755c..1f42f57f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
@@ -1662,7 +1662,7 @@
       R"HTML(
       <style>
         #multicol {
-          column-count:2; column-fill:auto; column-gap:0px; width:100px;
+          column-count:2; column-gap:0px; width:100px;
         }
         .abs {
           position:absolute; width:50px; height:200px; top:0;
diff --git a/third_party/blink/renderer/core/loader/speculation_rule_loader.cc b/third_party/blink/renderer/core/loader/speculation_rule_loader.cc
index 74972d8..e0d85225 100644
--- a/third_party/blink/renderer/core/loader/speculation_rule_loader.cc
+++ b/third_party/blink/renderer/core/loader/speculation_rule_loader.cc
@@ -85,10 +85,11 @@
   if (auto* rule_set = SpeculationRuleSet::Parse(
           source, document_->GetExecutionContext(), &parse_error)) {
     DocumentSpeculationRules::From(*document_).AddRuleSet(rule_set);
-  }
-  if (!parse_error.IsNull()) {
+  } else {
     CountSpeculationRulesLoadOutcome(
         SpeculationRulesLoadOutcome::kParseErrorFetched);
+  }
+  if (!parse_error.IsNull()) {
     document_->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
         mojom::blink::ConsoleMessageSource::kOther,
         mojom::blink::ConsoleMessageLevel::kWarning,
diff --git a/third_party/blink/renderer/core/script/script_loader.cc b/third_party/blink/renderer/core/script/script_loader.cc
index 50c1e71..d6646bf9 100644
--- a/third_party/blink/renderer/core/script/script_loader.cc
+++ b/third_party/blink/renderer/core/script/script_loader.cc
@@ -1018,10 +1018,11 @@
                                                        &parse_error)) {
           speculation_rule_set_ = rule_set;
           DocumentSpeculationRules::From(element_document).AddRuleSet(rule_set);
-        }
-        if (!parse_error.IsNull()) {
+        } else {
           CountSpeculationRulesLoadOutcome(
               SpeculationRulesLoadOutcome::kParseErrorInline);
+        }
+        if (!parse_error.IsNull()) {
           auto* console_message = MakeGarbageCollected<ConsoleMessage>(
               mojom::ConsoleMessageSource::kOther,
               mojom::ConsoleMessageLevel::kWarning,
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
index 2a4e02d6..f60c95c7 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
+++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
@@ -271,13 +271,13 @@
   // updated document base URL.
   for (Member<SpeculationRuleSet>& rule_set : rule_sets_) {
     SpeculationRuleSet::Source* source = rule_set->source();
-    String parse_error;
     rule_set = SpeculationRuleSet::Parse(
-        source, GetSupplementable()->GetExecutionContext(), &parse_error);
+        source, GetSupplementable()->GetExecutionContext(),
+        /*out_error=*/nullptr);
     // There should not be any parsing errors as these rule sets have already
     // been parsed once without errors, and an updated base URL should not cause
-    // new errors.
-    DCHECK(parse_error.empty());
+    // new errors. There may however still be warnings.
+    DCHECK(rule_set);
   }
   if (initialized_)
     InvalidateAllLinks();
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
index 6b4d5ff..17e53fe 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
@@ -50,9 +50,18 @@
   return false;
 }
 
+// If `out_error` is provided and hasn't already had a message set, sets it to
+// `message`.
+void SetParseErrorMessage(String* out_error, String message) {
+  if (out_error && out_error->IsNull()) {
+    *out_error = message;
+  }
+}
+
 SpeculationRule* ParseSpeculationRule(JSONObject* input,
                                       const KURL& base_url,
-                                      ExecutionContext* context) {
+                                      ExecutionContext* context,
+                                      String* out_error) {
   // https://wicg.github.io/nav-speculation/speculation-rules.html#parse-a-speculation-rule
 
   // If input has any key other than "source", "urls", "requires", "target_hint"
@@ -75,6 +84,8 @@
     const String& input_key = input->at(i).first;
     if (!base::Contains(kKnownKeys, input_key) &&
         !base::Contains(kConditionalKnownKeys, input_key)) {
+      SetParseErrorMessage(
+          out_error, "A rule contains an unknown key: \"" + input_key + "\".");
       return nullptr;
     }
   }
@@ -88,15 +99,24 @@
   // If input["source"] does not exist or is neither the string "list" nor the
   // string "document", then return null.
   String source;
-  if (!input->GetString("source", &source) ||
-      !(source == "list" || (document_rules_enabled && source == "document")))
+  if (!input->GetString("source", &source)) {
+    SetParseErrorMessage(out_error, "A rule must have a source.");
     return nullptr;
+  }
+  if (!(source == "list" || (document_rules_enabled && source == "document"))) {
+    SetParseErrorMessage(out_error,
+                         "A rule has an unknown source: \"" + source + "\".");
+    return nullptr;
+  }
 
   Vector<KURL> urls;
   if (source == "list") {
     // If input["where"] exists, then return null.
-    if (input->Get("where"))
+    if (input->Get("where")) {
+      SetParseErrorMessage(out_error,
+                           "A list rule may not have document rule matchers.");
       return nullptr;
+    }
 
     // For now, use the given base URL to construct the list rules.
     KURL base_url_to_parse = base_url;
@@ -108,6 +128,8 @@
       // "document", then return null.
       if (!relative_to_enabled || !relative_to->AsString(&value) ||
           !base::Contains(kKnownRelativeToValues, value)) {
+        SetParseErrorMessage(out_error,
+                             "A rule has an unknown \"relative_to\" value.");
         return nullptr;
       }
       // If relativeTo is "document", then set baseURL to the document's
@@ -121,15 +143,20 @@
     // If input["urls"] does not exist, is not a list, or has any element which
     // is not a string, then return null.
     JSONArray* input_urls = input->GetArray("urls");
-    if (!input_urls)
+    if (!input_urls) {
+      SetParseErrorMessage(out_error,
+                           "A list rule must have a \"urls\" array.");
       return nullptr;
+    }
 
     // For each urlString of input["urls"]...
     urls.ReserveInitialCapacity(input_urls->size());
     for (wtf_size_t i = 0; i < input_urls->size(); ++i) {
       String url_string;
-      if (!input_urls->at(i)->AsString(&url_string))
+      if (!input_urls->at(i)->AsString(&url_string)) {
+        SetParseErrorMessage(out_error, "URLs must be given as strings.");
         return nullptr;
+      }
 
       // Let parsedURL be the result of parsing urlString with baseURL.
       // If parsedURL is failure, then continue.
@@ -141,6 +168,7 @@
     }
   }
 
+  // TODO(mcnee): Populate `out_error` for document rule warnings.
   DocumentRulePredicate* document_rule_predicate = nullptr;
   if (source == "document") {
     DCHECK(document_rules_enabled);
@@ -171,8 +199,10 @@
   // Let requirements be an empty ordered set.
   // If input["requires"] exists, but is not a list, then return null.
   JSONValue* requirements = input->Get("requires");
-  if (requirements && requirements->GetType() != JSONValue::kTypeArray)
+  if (requirements && requirements->GetType() != JSONValue::kTypeArray) {
+    SetParseErrorMessage(out_error, "\"requires\" must be an array.");
     return nullptr;
+  }
 
   // For each requirement of input["requires"]...
   SpeculationRule::RequiresAnonymousClientIPWhenCrossOrigin
@@ -180,13 +210,18 @@
   if (JSONArray* requirements_array = JSONArray::Cast(requirements)) {
     for (wtf_size_t i = 0; i < requirements_array->size(); ++i) {
       String requirement;
-      if (!requirements_array->at(i)->AsString(&requirement))
+      if (!requirements_array->at(i)->AsString(&requirement)) {
+        SetParseErrorMessage(out_error, "Requirements must be strings.");
         return nullptr;
+      }
 
       if (requirement == "anonymous-client-ip-when-cross-origin") {
         requires_anonymous_client_ip =
             SpeculationRule::RequiresAnonymousClientIPWhenCrossOrigin(true);
       } else {
+        SetParseErrorMessage(
+            out_error,
+            "A rule has an unknown requirement: \"" + requirement + "\".");
         return nullptr;
       }
     }
@@ -202,10 +237,16 @@
     // then return null.
     // Set targetHint to input["target_hint"].
     String target_hint_str;
-    if (!target_hint_value->AsString(&target_hint_str))
+    if (!target_hint_value->AsString(&target_hint_str)) {
+      SetParseErrorMessage(out_error, "\"target_hint\" must be a string.");
       return nullptr;
-    if (!IsValidBrowsingContextNameOrKeyword(target_hint_str))
+    }
+    if (!IsValidBrowsingContextNameOrKeyword(target_hint_str)) {
+      SetParseErrorMessage(out_error,
+                           "A rule has an invalid \"target_hint\": \"" +
+                               target_hint_str + "\".");
       return nullptr;
+    }
     // Currently only "_blank" and "_self" are supported.
     // TODO(https://crbug.com/1354049): Support more browsing context names and
     // keywords.
@@ -226,8 +267,10 @@
         context));
 
     String referrer_policy_str;
-    if (!referrer_policy_value->AsString(&referrer_policy_str))
+    if (!referrer_policy_value->AsString(&referrer_policy_str)) {
+      SetParseErrorMessage(out_error, "A referrer policy must be a string.");
       return nullptr;
+    }
 
     if (!referrer_policy_str.empty()) {
       network::mojom::ReferrerPolicy referrer_policy_out =
@@ -235,6 +278,9 @@
       if (!SecurityPolicy::ReferrerPolicyFromString(
               referrer_policy_str, kDoNotSupportReferrerPolicyLegacyKeywords,
               &referrer_policy_out)) {
+        SetParseErrorMessage(out_error,
+                             "A rule has an invalid referrer policy: \"" +
+                                 referrer_policy_str + "\".");
         return nullptr;
       }
       DCHECK_NE(referrer_policy_out, network::mojom::ReferrerPolicy::kDefault);
@@ -332,11 +378,10 @@
 
   // If parsed is not a map, then return null.
   if (!parsed) {
-    if (out_error) {
-      *out_error = parse_error.type != JSONParseErrorType::kNoError
-                       ? parse_error.message
-                       : "Parsed JSON must be an object.";
-    }
+    SetParseErrorMessage(out_error,
+                         parse_error.type != JSONParseErrorType::kNoError
+                             ? parse_error.message
+                             : "Parsed JSON must be an object.");
     return nullptr;
   }
 
@@ -348,19 +393,22 @@
       [&](const char* key, HeapVector<Member<SpeculationRule>>& destination,
           bool allow_target_hint) {
         JSONArray* array = parsed->GetArray(key);
-        if (!array)
+        if (!array) {
           return;
+        }
 
         for (wtf_size_t i = 0; i < array->size(); ++i) {
           // If prefetch/prerenderRule is not a map, then continue.
           JSONObject* input_rule = JSONObject::Cast(array->at(i));
-          if (!input_rule)
+          if (!input_rule) {
+            SetParseErrorMessage(out_error, "A rule must be an object.");
             continue;
+          }
 
           // Let rule be the result of parsing a speculation rule given
           // prefetch/prerenderRule and baseURL.
           SpeculationRule* rule =
-              ParseSpeculationRule(input_rule, base_url, context);
+              ParseSpeculationRule(input_rule, base_url, context, out_error);
 
           // If rule is null, then continue.
           if (!rule)
@@ -370,6 +418,9 @@
           // continue.
           if (!allow_target_hint &&
               rule->target_browsing_context_name_hint().has_value()) {
+            SetParseErrorMessage(
+                out_error, "\"target_hint\" may not be set for " + String(key) +
+                               " rules.");
             continue;
           }
 
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
index 50334267..c326916 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
@@ -126,7 +126,8 @@
   }
 
   SpeculationRuleSet* CreateSpeculationRuleSetWithTargetHint(
-      const char* target_hint) {
+      const char* target_hint,
+      String* parse_error = nullptr) {
     return CreateRuleSet(String::Format(R"({
         "prefetch": [{
           "source": "list",
@@ -145,7 +146,8 @@
         }]
       })",
                                         target_hint, target_hint, target_hint),
-                         KURL("https://example.com/"), execution_context_);
+                         KURL("https://example.com/"), execution_context_,
+                         parse_error);
   }
 
   NullExecutionContext* execution_context() {
@@ -346,9 +348,13 @@
 }
 
 TEST_F(SpeculationRuleSetTest, DropUnrecognizedRules) {
+  String parse_error;
   auto* rule_set = CreateRuleSet(
       R"({"prefetch": [)"
 
+      // A rule of incorrect type.
+      R"("not an object",)"
+
       // A rule that doesn't elaborate on its source.
       R"({"urls": ["no-source.html"]},)"
 
@@ -364,6 +370,13 @@
       // A rule with an unrecognized requirement.
       R"({"source": "list", "urls": ["/"], "requires": ["more-vespene-gas"]},)"
 
+      // A rule with requirements not given as an array.
+      R"({"source": "list", "urls": ["/"],
+          "requires": "anonymous-client-ip-when-cross-origin"},)"
+
+      // A rule with requirements of incorrect type.
+      R"({"source": "list", "urls": ["/"], "requires": [42]},)"
+
       // A rule with a referrer_policy of incorrect type.
       R"({"source": "list", "urls": ["/"], "referrer_policy": 42},)"
 
@@ -384,6 +397,10 @@
           "urls": ["/no-source.html"],
           "relative_to": "not_document"},)"
 
+      // A rule with a "target_hint" of incorrect type (in addition to being
+      // invalid to use target_hint in a prefetch rule).
+      R"({"source": "list", "urls": ["/"], "target_hint": 42},)"
+
       // Invalid URLs within a list rule should be discarded.
       // This includes totally invalid ones and ones with unacceptable schemes.
       R"({"source": "list",
@@ -392,16 +409,24 @@
             "blob:https://bar"
            ]
          }]})",
-      KURL("https://example.com/"), execution_context());
+      KURL("https://example.com/"), execution_context(), &parse_error);
   ASSERT_TRUE(rule_set);
+  // The rule set itself is valid, however many of the individual rules are
+  // invalid. So we should have populated a warning message.
+  EXPECT_FALSE(parse_error.empty());
   EXPECT_THAT(rule_set->prefetch_rules(),
               ElementsAre(MatchesListOfURLs("https://example.com/valid.html")));
 }
 
 // Test that only prerender rule can process a "_blank" target hint.
 TEST_F(SpeculationRuleSetTest, RulesWithTargetHint_Blank) {
-  auto* rule_set = CreateSpeculationRuleSetWithTargetHint("_blank");
+  String parse_error;
+  auto* rule_set =
+      CreateSpeculationRuleSetWithTargetHint("_blank", &parse_error);
   ASSERT_TRUE(rule_set);
+  EXPECT_TRUE(
+      parse_error.Contains("\"target_hint\" may not be set for prefetch"))
+      << parse_error;
   EXPECT_THAT(rule_set->prefetch_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prefetch_with_subresources_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prerender_rules(),
@@ -412,8 +437,13 @@
 
 // Test that only prerender rule can process a "_self" target hint.
 TEST_F(SpeculationRuleSetTest, RulesWithTargetHint_Self) {
-  auto* rule_set = CreateSpeculationRuleSetWithTargetHint("_self");
+  String parse_error;
+  auto* rule_set =
+      CreateSpeculationRuleSetWithTargetHint("_self", &parse_error);
   ASSERT_TRUE(rule_set);
+  EXPECT_TRUE(
+      parse_error.Contains("\"target_hint\" may not be set for prefetch"))
+      << parse_error;
   EXPECT_THAT(rule_set->prefetch_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prefetch_with_subresources_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prerender_rules(),
@@ -427,8 +457,13 @@
 // TODO(https://crbug.com/1354049): Support the "_parent" keyword for
 // prerendering.
 TEST_F(SpeculationRuleSetTest, RulesWithTargetHint_Parent) {
-  auto* rule_set = CreateSpeculationRuleSetWithTargetHint("_parent");
+  String parse_error;
+  auto* rule_set =
+      CreateSpeculationRuleSetWithTargetHint("_parent", &parse_error);
   ASSERT_TRUE(rule_set);
+  EXPECT_TRUE(
+      parse_error.Contains("\"target_hint\" may not be set for prefetch"))
+      << parse_error;
   EXPECT_THAT(rule_set->prefetch_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prefetch_with_subresources_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prerender_rules(),
@@ -442,8 +477,12 @@
 // Test that rules with a "_top" hint are ignored.
 // TODO(https://crbug.com/1354049): Support the "_top" keyword for prerendering.
 TEST_F(SpeculationRuleSetTest, RulesWithTargetHint_Top) {
-  auto* rule_set = CreateSpeculationRuleSetWithTargetHint("_top");
+  String parse_error;
+  auto* rule_set = CreateSpeculationRuleSetWithTargetHint("_top", &parse_error);
   ASSERT_TRUE(rule_set);
+  EXPECT_TRUE(
+      parse_error.Contains("\"target_hint\" may not be set for prefetch"))
+      << parse_error;
   EXPECT_THAT(rule_set->prefetch_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prefetch_with_subresources_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prerender_rules(),
@@ -454,8 +493,10 @@
 
 // Test that rules with an empty target hint are ignored.
 TEST_F(SpeculationRuleSetTest, RulesWithTargetHint_EmptyString) {
-  auto* rule_set = CreateSpeculationRuleSetWithTargetHint("");
+  String parse_error;
+  auto* rule_set = CreateSpeculationRuleSetWithTargetHint("", &parse_error);
   ASSERT_TRUE(rule_set);
+  EXPECT_TRUE(parse_error.Contains("invalid \"target_hint\"")) << parse_error;
   EXPECT_THAT(rule_set->prefetch_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prefetch_with_subresources_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prerender_rules(), ElementsAre());
@@ -465,8 +506,13 @@
 // but treat it as no hint.
 // TODO(https://crbug.com/1354049): Support valid browsing context names.
 TEST_F(SpeculationRuleSetTest, RulesWithTargetHint_ValidBrowsingContextName) {
-  auto* rule_set = CreateSpeculationRuleSetWithTargetHint("valid");
+  String parse_error;
+  auto* rule_set =
+      CreateSpeculationRuleSetWithTargetHint("valid", &parse_error);
   ASSERT_TRUE(rule_set);
+  EXPECT_TRUE(
+      parse_error.Contains("\"target_hint\" may not be set for prefetch"))
+      << parse_error;
   EXPECT_THAT(rule_set->prefetch_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prefetch_with_subresources_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prerender_rules(),
@@ -478,8 +524,11 @@
 // Test that rules with an invalid browsing context name target hint are
 // ignored.
 TEST_F(SpeculationRuleSetTest, RulesWithTargetHint_InvalidBrowsingContextName) {
-  auto* rule_set = CreateSpeculationRuleSetWithTargetHint("_invalid");
+  String parse_error;
+  auto* rule_set =
+      CreateSpeculationRuleSetWithTargetHint("_invalid", &parse_error);
   ASSERT_TRUE(rule_set);
+  EXPECT_TRUE(parse_error.Contains("invalid \"target_hint\"")) << parse_error;
   EXPECT_THAT(rule_set->prefetch_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prefetch_with_subresources_rules(), ElementsAre());
   EXPECT_THAT(rule_set->prerender_rules(), ElementsAre());
@@ -895,6 +944,32 @@
       [](const String& message) { return message.Contains("Syntax error"); }));
 }
 
+// Tests that errors of individual rules which cause them to be ignored are
+// logged to the console.
+TEST_F(SpeculationRuleSetTest, ConsoleWarningForInvalidRule) {
+  auto* chrome_client = MakeGarbageCollected<ConsoleCapturingChromeClient>();
+  DummyPageHolder page_holder(/*initial_view_size=*/{}, chrome_client);
+  page_holder.GetFrame().GetSettings()->SetScriptEnabled(true);
+
+  Document& document = page_holder.GetDocument();
+  HTMLScriptElement* script =
+      MakeGarbageCollected<HTMLScriptElement>(document, CreateElementFlags());
+  script->setAttribute(html_names::kTypeAttr, "speculationrules");
+  script->setText(
+      R"({
+        "prefetch": [{
+          "source": "list",
+          "urls": [["a", ".", "c", "o", "m"]]
+        }]
+      })");
+  document.head()->appendChild(script);
+
+  EXPECT_TRUE(base::ranges::any_of(
+      chrome_client->ConsoleMessages(), [](const String& message) {
+        return message.Contains("URLs must be given as strings");
+      }));
+}
+
 TEST_F(SpeculationRuleSetTest, RejectsWhereClause) {
   auto* rule_set = CreateRuleSet(
       R"({
@@ -1725,7 +1800,7 @@
 }
 
 // Tests that candidates created for document rules are correct when
-// "anonyomour-client-ip-when-origin" is specified.
+// "anonymous-client-ip-when-cross-origin" is specified.
 TEST_F(DocumentRulesTest, RequiresAnonymousClientIPWhenCrossOrigin) {
   DummyPageHolder page_holder;
   StubSpeculationHost speculation_host;
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index 8942a2c4..aae4b1b0 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -484,15 +484,35 @@
 
     events_data_.pop_front();
 
-    int duration_in_ms = std::round((end_time - entry->startTime()) / 8) * 8;
+    base::TimeTicks entry_presentation_timestamp = presentation_timestamp;
+    DOMHighResTimeStamp entry_end_time = end_time;
+
     base::TimeDelta input_delay =
         base::Milliseconds(entry->processingStart() - entry->startTime());
     base::TimeDelta processing_time =
         base::Milliseconds(entry->processingEnd() - entry->processingStart());
     base::TimeDelta time_to_next_paint =
-        base::Milliseconds(end_time - entry->processingEnd());
-    entry->SetDuration(duration_in_ms);
-    entry->SetUnsafePresentationTimestamp(presentation_timestamp);
+        base::Milliseconds(entry_end_time - entry->processingEnd());
+
+    if (last_visibility_change_timestamp_ > event_timestamp &&
+        last_visibility_change_timestamp_ < presentation_timestamp) {
+      // The page visibility was changed. Ignore the presentation_timestamp and
+      // fallback to processingEnd (as if there was no next paint needed).
+      entry_end_time = entry->processingEnd();
+      // Adjust the entry_presentation_timestamp to also be == processingEnd.
+      // Ideally we could just assign the processingEnd value, except that
+      // processingEnd is already a DOMHighResTimeStamp and we cannot convert
+      // back to TimeTicks.  Subtracting the time_to_next_paint
+      entry_presentation_timestamp -= time_to_next_paint;
+      // Set time_to_next_paint = 0
+      time_to_next_paint = base::TimeDelta();
+    }
+
+    int rounded_duration =
+        std::round((entry_end_time - entry->startTime()) / 8) * 8;
+    entry->SetDuration(rounded_duration);
+    entry->SetUnsafePresentationTimestamp(entry_presentation_timestamp);
+
     if (entry->name() == "pointerdown") {
       pending_pointer_down_input_delay_ = input_delay;
       pending_pointer_down_processing_time_ = processing_time;
@@ -514,17 +534,11 @@
 
     // Event Timing
     ResponsivenessMetrics::EventTimestamps event_timestamps = {
-        event_timestamp, presentation_timestamp};
-    // The page visibility was changed. In this case, we don't care about
-    // the time to next paint.
-    if (last_visibility_change_timestamp_ > event_timestamp &&
-        last_visibility_change_timestamp_ <= presentation_timestamp) {
-      event_timestamps.end_time -= time_to_next_paint;
-    }
+        event_timestamp, entry_presentation_timestamp};
     if (SetInteractionIdAndRecordLatency(entry, key_code, pointer_id,
                                          event_timestamps)) {
       NotifyAndAddEventTimingBuffer(entry);
-    };
+    }
 
     // First Input
     //
diff --git a/third_party/blink/renderer/core/timing/window_performance_test.cc b/third_party/blink/renderer/core/timing/window_performance_test.cc
index daafb98..2ff96bc5 100644
--- a/third_party/blink/renderer/core/timing/window_performance_test.cc
+++ b/third_party/blink/renderer/core/timing/window_performance_test.cc
@@ -792,6 +792,9 @@
 }
 
 TEST_F(WindowPerformanceTest, PageVisibilityChanged) {
+  // The page visibility gets changed.
+  PageVisibilityChanged(GetTimeStamp(18));
+
   // Pointerdown
   base::TimeTicks pointerdown_timestamp = GetTimeOrigin();
   base::TimeTicks processing_start_pointerdown = GetTimeStamp(1);
@@ -803,9 +806,6 @@
                        pointer_id);
   SimulateSwapPromise(swap_time_pointerdown);
 
-  // The page visibility gets changed.
-  PageVisibilityChanged(GetTimeStamp(18));
-
   // Pointerup
   base::TimeTicks pointerup_timestamp = GetTimeStamp(3);
   base::TimeTicks processing_start_pointerup = GetTimeStamp(5);
@@ -815,6 +815,7 @@
                        processing_start_pointerup, processing_end_pointerup,
                        pointer_id);
   SimulateSwapPromise(swap_time_pointerup);
+
   // Click
   base::TimeTicks click_timestamp = GetTimeStamp(13);
   base::TimeTicks processing_start_click = GetTimeStamp(15);
@@ -832,13 +833,16 @@
       ukm::builders::Responsiveness_UserInteraction::kEntryName);
   EXPECT_EQ(1u, entries.size());
   const ukm::mojom::UkmEntry* ukm_entry = entries[0];
-  // The event duration of pointerdown is 5ms. Because the page visibility was
-  // changed after the pointerup, click were created, the event durations of
-  // them are 3ms, 3ms. The maximum event duration is 5ms. The total event
-  // duration is 9ms.
+  // The event duration of pointerdown is 5ms, all the way to presentation.
+  // Because the page visibility was changed after pointerup & click were
+  // created, the event durations fall back to processingEnd.  That means
+  // they are become 3ms duration each. So the max duration is 5ms.
   GetUkmRecorder()->ExpectEntryMetric(
       ukm_entry,
       ukm::builders::Responsiveness_UserInteraction::kMaxEventDurationName, 5);
+  // Because there is overlap with pointerdown and pointerup, the
+  // the non overlapping event duration for pointerup is only 1ms (not 3ms),
+  // So the total non-overlapping total is 5 + 1 + 3 = 9ms.
   GetUkmRecorder()->ExpectEntryMetric(
       ukm_entry,
       ukm::builders::Responsiveness_UserInteraction::kTotalEventDurationName,
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
index 5055cd6..66022c07 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h
@@ -69,10 +69,10 @@
   // Transfer the ArrayBuffer with |detach_key| if it is detachable,
   // otherwise make a copy and transfer that. Rethrows a V8 exception
   // or a TypeError on failure.
-  virtual bool Transfer(v8::Isolate*,
-                        v8::Local<v8::Value> detach_key,
-                        ArrayBufferContents& result,
-                        ExceptionState& exception_state);
+  bool Transfer(v8::Isolate*,
+                v8::Local<v8::Value> detach_key,
+                ArrayBufferContents& result,
+                ExceptionState& exception_state);
 
   bool Transfer(v8::Isolate*,
                 ArrayBufferContents& result,
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h
index c84d815..d3f29d5 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h
@@ -5,11 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_FILE_DELEGATE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_FILE_DELEGATE_H_
 
-#include "base/files/file.h"
 #include "base/files/file_error_or.h"
 #include "base/sequence_checker.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom-blink.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -51,36 +49,17 @@
   // Returns the current size of this file, or a file error on failure.
   virtual base::FileErrorOr<int64_t> GetLength() = 0;
 
-  // Asynchronously get the size of the file. Returns the current size of this
-  // file, or a file error on failure.
-  virtual void GetLengthAsync(
-      base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) = 0;
-
   // Tuncates the file to the given length. `length` cannot be negative.
   // If `length` is greater than the current size of the file, the file is
   // extended with zeros.
   virtual base::FileErrorOr<bool> SetLength(int64_t length) = 0;
 
-  // Asynchronously truncates the file to the given length. `length` cannot be
-  // negative. If `length` is greater than the current size of the file, the
-  // file is extended with zeros.
-  virtual void SetLengthAsync(
-      int64_t length,
-      base::OnceCallback<void(base::File::Error)> callback) = 0;
-
   // Instructs the filesystem to flush the file to disk.
   virtual bool Flush() = 0;
 
-  // Asynchronously instructs the filesystem to flush the file to disk.
-  virtual void FlushAsync(base::OnceCallback<void(bool)> callback) = 0;
-
   // Close the file. Destroying this object will close the file automatically.
   virtual void Close() = 0;
 
-  // Asynchronously close the file. Destroying this object will close the file
-  // automatically.
-  virtual void CloseAsync(base::OnceClosure callback) = 0;
-
   // Returns `true` if the file handle wrapped by this object is valid.
   virtual bool IsValid() const = 0;
 
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
index 0bae69a..a0c64799 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
@@ -7,7 +7,6 @@
 #include "base/files/file.h"
 #include "base/files/file_error_or.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
@@ -171,19 +170,6 @@
   return file_error == base::File::Error::FILE_OK ? length : file_error;
 }
 
-void FileSystemAccessIncognitoFileDelegate::GetLengthAsync(
-    base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  mojo_ptr_->GetLength(WTF::BindOnce(
-      [](base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback,
-         base::File::Error file_error, int64_t length) {
-        CHECK_GE(length, 0);
-        std::move(callback).Run(
-            file_error == base::File::Error::FILE_OK ? length : file_error);
-      },
-      std::move(callback)));
-}
-
 base::FileErrorOr<bool> FileSystemAccessIncognitoFileDelegate::SetLength(
     int64_t length) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -193,15 +179,6 @@
   return file_error == base::File::Error::FILE_OK ? true : file_error;
 }
 
-void FileSystemAccessIncognitoFileDelegate::SetLengthAsync(
-    int64_t length,
-    base::OnceCallback<void(base::File::Error)> callback) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  CHECK_GE(length, 0);
-
-  mojo_ptr_->SetLength(length, WTF::BindOnce(std::move(callback)));
-}
-
 bool FileSystemAccessIncognitoFileDelegate::Flush() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Flush is a no-op for in-memory file systems. Even if the file delegate is
@@ -212,29 +189,9 @@
   return true;
 }
 
-void FileSystemAccessIncognitoFileDelegate::FlushAsync(
-    base::OnceCallback<void(bool)> callback) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  // Flush is a no-op for in-memory file systems. Even if the file delegate is
-  // used for other FS types, writes through the FileSystemOperationRunner are
-  // automatically flushed. If this proves to be too slow, we can consider
-  // changing the FileSystemAccessFileDelegateHostImpl to write with a
-  // FileStreamWriter and only flushing when this method is called.
-  task_runner_->PostTask(FROM_HERE,
-                         WTF::BindOnce(std::move(callback), /*success=*/true));
-}
-
 void FileSystemAccessIncognitoFileDelegate::Close() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   mojo_ptr_.reset();
 }
 
-void FileSystemAccessIncognitoFileDelegate::CloseAsync(
-    base::OnceClosure callback) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  mojo_ptr_.reset();
-
-  task_runner_->PostTask(FROM_HERE, std::move(callback));
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.h
index 66a495d..217b4f4 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.h
@@ -5,12 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_INCOGNITO_FILE_DELEGATE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_INCOGNITO_FILE_DELEGATE_H_
 
-#include "base/files/file.h"
 #include "base/files/file_error_or.h"
 #include "base/task/sequenced_task_runner.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom-blink.h"
-#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -41,16 +39,9 @@
   base::FileErrorOr<int> Write(int64_t offset,
                                const base::span<uint8_t> data) override;
   base::FileErrorOr<int64_t> GetLength() override;
-  void GetLengthAsync(
-      base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) override;
   base::FileErrorOr<bool> SetLength(int64_t new_length) override;
-  void SetLengthAsync(
-      int64_t new_length,
-      base::OnceCallback<void(base::File::Error)> callback) override;
   bool Flush() override;
-  void FlushAsync(base::OnceCallback<void(bool)> callback) override;
   void Close() override;
-  void CloseAsync(base::OnceClosure callback) override;
   bool IsValid() const override { return mojo_ptr_.is_bound(); }
 
   void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
index daa8d55..392bb63 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
@@ -15,15 +15,7 @@
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.h"
-#include "third_party/blink/renderer/platform/heap/cross_thread_persistent.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
-#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-#include "third_party/blink/renderer/platform/wtf/wtf.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "base/mac/mac_util.h"
@@ -124,55 +116,6 @@
   return length >= 0 ? length : base::File::GetLastFileError();
 }
 
-void FileSystemAccessRegularFileDelegate::GetLengthAsync(
-    base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  auto wrapped_callback =
-      CrossThreadOnceFunction<void(base::FileErrorOr<int64_t>)>(
-          std::move(callback));
-
-  // Get file length on a worker thread and reply back to this sequence.
-  worker_pool::PostTask(
-      FROM_HERE, {base::MayBlock()},
-      CrossThreadBindOnce(&FileSystemAccessRegularFileDelegate::DoGetLength,
-                          MakeCrossThreadHandle(this),
-                          std::move(wrapped_callback), std::move(backing_file_),
-                          task_runner_));
-}
-
-// static
-void FileSystemAccessRegularFileDelegate::DoGetLength(
-    CrossThreadHandle<FileSystemAccessRegularFileDelegate> delegate,
-    CrossThreadOnceFunction<void(base::FileErrorOr<int64_t>)> callback,
-    base::File file,
-    scoped_refptr<base::SequencedTaskRunner> task_runner) {
-  DCHECK(!IsMainThread());
-
-  int64_t length = file.GetLength();
-
-  // If the length is negative, the file operation failed. Get the last error
-  // now before another file operation might run.
-  base::FileErrorOr<int64_t> result =
-      length >= 0 ? length : base::File::GetLastFileError();
-
-  PostCrossThreadTask(
-      *task_runner, FROM_HERE,
-      CrossThreadBindOnce(&FileSystemAccessRegularFileDelegate::DidGetLength,
-                          MakeUnwrappingCrossThreadHandle(std::move(delegate)),
-                          std::move(callback), std::move(file),
-                          std::move(result)));
-}
-
-void FileSystemAccessRegularFileDelegate::DidGetLength(
-    CrossThreadOnceFunction<void(base::FileErrorOr<int64_t>)> callback,
-    base::File file,
-    base::FileErrorOr<int64_t> error_or_length) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  backing_file_ = std::move(file);
-
-  std::move(callback).Run(std::move(error_or_length));
-}
-
 base::FileErrorOr<bool> FileSystemAccessRegularFileDelegate::SetLength(
     int64_t new_length) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -212,200 +155,14 @@
   return base::unexpected(base::File::GetLastFileError());
 }
 
-void FileSystemAccessRegularFileDelegate::SetLengthAsync(
-    int64_t new_length,
-    base::OnceCallback<void(base::File::Error)> callback) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  CHECK_GE(new_length, 0);
-
-  capacity_tracker_->RequestFileCapacityChange(
-      new_length,
-      WTF::BindOnce(
-          &FileSystemAccessRegularFileDelegate::DidCheckSetLengthCapacity,
-          WrapWeakPersistent(this), std::move(callback), new_length));
-}
-
-void FileSystemAccessRegularFileDelegate::DidCheckSetLengthCapacity(
-    base::OnceCallback<void(base::File::Error)> callback,
-    int64_t new_length,
-    bool request_capacity_success) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  if (!request_capacity_success) {
-    task_runner_->PostTask(
-        FROM_HERE, WTF::BindOnce(std::move(callback),
-                                 base::File::Error::FILE_ERROR_NO_SPACE));
-    return;
-  }
-
-  auto wrapped_callback =
-      CrossThreadOnceFunction<void(base::File::Error)>(std::move(callback));
-
-#if BUILDFLAG(IS_MAC)
-  // On macOS < 10.15, a sandboxing limitation causes failures in ftruncate()
-  // syscalls issued from renderers. For this reason, base::File::SetLength()
-  // fails in the renderer. We work around this problem by calling ftruncate()
-  // in the browser process. See https://crbug.com/1084565.
-  if (!base::mac::IsAtLeastOS10_15()) {
-    if (!file_utilities_host_.is_bound()) {
-      context_->GetBrowserInterfaceBroker().GetInterface(
-          file_utilities_host_.BindNewPipeAndPassReceiver(task_runner_));
-    }
-    file_utilities_host_->SetLength(
-        std::move(backing_file_), new_length,
-        WTF::BindOnce(
-            [](base::OnceCallback<void(base::File, base::File::Error)> callback,
-               base::File file, bool success) {
-              // Unfortunately we don't have access to the error code when using
-              // the FileUtilitiesHost, so we can say the operation failed but
-              // not why (ex: out of quota).
-              std::move(callback).Run(
-                  std::move(file), success
-                                       ? base::File::Error::FILE_OK
-                                       : base::File::Error::FILE_ERROR_FAILED);
-            },
-            WTF::BindOnce(&FileSystemAccessRegularFileDelegate::DidSetLength,
-                          WrapPersistent(this), std::move(wrapped_callback),
-                          new_length)));
-    return;
-  }
-#endif  // BUILDFLAG(IS_MAC)
-
-  // Truncate file on a worker thread and reply back to this sequence.
-  worker_pool::PostTask(
-      FROM_HERE, {base::MayBlock()},
-      CrossThreadBindOnce(&FileSystemAccessRegularFileDelegate::DoSetLength,
-                          MakeCrossThreadHandle(this),
-                          std::move(wrapped_callback), std::move(backing_file_),
-                          task_runner_, new_length));
-}
-
-// static
-void FileSystemAccessRegularFileDelegate::DoSetLength(
-    CrossThreadHandle<FileSystemAccessRegularFileDelegate> delegate,
-    CrossThreadOnceFunction<void(base::File::Error)> callback,
-    base::File file,
-    scoped_refptr<base::SequencedTaskRunner> task_runner,
-    int64_t length) {
-  DCHECK(!IsMainThread());
-
-  base::File::Error error = base::File::Error::FILE_OK;
-  bool success = file.SetLength(length);
-  if (!success)
-    error = base::File::GetLastFileError();
-
-  PostCrossThreadTask(
-      *task_runner, FROM_HERE,
-      CrossThreadBindOnce(&FileSystemAccessRegularFileDelegate::DidSetLength,
-                          MakeUnwrappingCrossThreadHandle(std::move(delegate)),
-                          std::move(callback), length, std::move(file), error));
-}
-
-void FileSystemAccessRegularFileDelegate::DidSetLength(
-    CrossThreadOnceFunction<void(base::File::Error)> callback,
-    int64_t new_length,
-    base::File file,
-    base::File::Error error) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  backing_file_ = std::move(file);
-
-  // If the operation failed, no change in file size is recorded. This assumes
-  // that setLength operations either succeed or do not change the file's
-  // length, which is consistent with the way other file operations are
-  // implemented in File System Access.
-  if (error == base::File::FILE_OK)
-    capacity_tracker_->CommitFileSizeChange(new_length);
-
-  std::move(callback).Run(error);
-}
-
 bool FileSystemAccessRegularFileDelegate::Flush() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return backing_file_.Flush();
 }
 
-void FileSystemAccessRegularFileDelegate::FlushAsync(
-    base::OnceCallback<void(bool)> callback) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  auto wrapped_callback =
-      CrossThreadOnceFunction<void(bool)>(std::move(callback));
-
-  // Flush file on a worker thread and reply back to this sequence.
-  worker_pool::PostTask(
-      FROM_HERE, {base::MayBlock()},
-      CrossThreadBindOnce(&FileSystemAccessRegularFileDelegate::DoFlush,
-                          MakeCrossThreadHandle(this),
-                          std::move(wrapped_callback), std::move(backing_file_),
-                          task_runner_));
-}
-
-// static
-void FileSystemAccessRegularFileDelegate::DoFlush(
-    CrossThreadHandle<FileSystemAccessRegularFileDelegate> delegate,
-    CrossThreadOnceFunction<void(bool)> callback,
-    base::File file,
-    scoped_refptr<base::SequencedTaskRunner> task_runner) {
-  DCHECK(!IsMainThread());
-
-  bool success = file.Flush();
-  PostCrossThreadTask(
-      *task_runner, FROM_HERE,
-      CrossThreadBindOnce(&FileSystemAccessRegularFileDelegate::DidFlush,
-                          MakeUnwrappingCrossThreadHandle(std::move(delegate)),
-                          std::move(callback), std::move(file), success));
-}
-
-void FileSystemAccessRegularFileDelegate::DidFlush(
-    CrossThreadOnceFunction<void(bool)> callback,
-    base::File file,
-    bool success) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  backing_file_ = std::move(file);
-
-  std::move(callback).Run(success);
-}
-
 void FileSystemAccessRegularFileDelegate::Close() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   backing_file_.Close();
 }
 
-void FileSystemAccessRegularFileDelegate::CloseAsync(
-    base::OnceClosure callback) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  auto wrapped_callback = CrossThreadOnceClosure(std::move(callback));
-
-  // Close file on a worker thread and reply back to this sequence.
-  worker_pool::PostTask(
-      FROM_HERE, {base::MayBlock()},
-      CrossThreadBindOnce(&FileSystemAccessRegularFileDelegate::DoClose,
-                          MakeCrossThreadHandle(this),
-                          std::move(wrapped_callback), std::move(backing_file_),
-                          task_runner_));
-}
-
-// static
-void FileSystemAccessRegularFileDelegate::DoClose(
-    CrossThreadHandle<FileSystemAccessRegularFileDelegate> delegate,
-    CrossThreadOnceClosure callback,
-    base::File file,
-    scoped_refptr<base::SequencedTaskRunner> task_runner) {
-  DCHECK(!IsMainThread());
-
-  file.Close();
-  PostCrossThreadTask(
-      *task_runner, FROM_HERE,
-      CrossThreadBindOnce(&FileSystemAccessRegularFileDelegate::DidClose,
-                          MakeUnwrappingCrossThreadHandle(std::move(delegate)),
-                          std::move(callback), std::move(file)));
-}
-
-void FileSystemAccessRegularFileDelegate::DidClose(
-    CrossThreadOnceClosure callback,
-    base::File file) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  backing_file_ = std::move(file);
-
-  std::move(callback).Run();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h
index 4cb69ef..bb9ada1 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h
@@ -12,14 +12,10 @@
 #include "build/build_config.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom-blink.h"
-#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/file_system_access/file_system_access_capacity_tracker.h"
 #include "third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h"
-#include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
 
 #if BUILDFLAG(IS_MAC)
 #include "third_party/blink/public/mojom/file/file_utilities.mojom-blink.h"
@@ -30,11 +26,6 @@
 
 // Non-incognito implementation of the FileSystemAccessFileDelegate. This class
 // is a thin wrapper around an OS-level file descriptor.
-//
-// For async file operations, ownership of the file descriptor is passed to
-// `task_runner_` to ensure it stays alive while being used. Ownership is passed
-// back to the delegate once the operation completes. The delegate must not be
-// used while there is an in-progress operation.
 class FileSystemAccessRegularFileDelegate final
     : public FileSystemAccessFileDelegate {
  public:
@@ -67,78 +58,12 @@
   base::FileErrorOr<int> Write(int64_t offset,
                                const base::span<uint8_t> data) override;
   base::FileErrorOr<int64_t> GetLength() override;
-  void GetLengthAsync(
-      base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) override;
   base::FileErrorOr<bool> SetLength(int64_t new_length) override;
-  void SetLengthAsync(
-      int64_t new_length,
-      base::OnceCallback<void(base::File::Error)> callback) override;
   bool Flush() override;
-  void FlushAsync(base::OnceCallback<void(bool)> callback) override;
   void Close() override;
-  void CloseAsync(base::OnceClosure callback) override;
   bool IsValid() const override { return backing_file_.IsValid(); }
 
  private:
-  // All async file operations perform I/O via a worker pool, off the main
-  // thread. To keep `backing_file_` from being garbage-collected, ownership is
-  // passed to the worker thread during the file operation. Concurrent file
-  // operations is NOT supported.
-
-  // Performs the file I/O part of getSize(), off the main thread.
-  static void DoGetLength(
-      CrossThreadHandle<FileSystemAccessRegularFileDelegate> delegate,
-      CrossThreadOnceFunction<void(base::FileErrorOr<int64_t>)> callback,
-      base::File file,
-      scoped_refptr<base::SequencedTaskRunner> file_task_runner);
-  // Performs the post file I/O part of getSize(), on the main thread.
-  void DidGetLength(
-      CrossThreadOnceFunction<void(base::FileErrorOr<int64_t>)> callback,
-      base::File file,
-      base::FileErrorOr<int64_t> error_or_length);
-
-  // Performs the file I/O part of truncate(), off the main thread.
-  static void DoSetLength(
-      CrossThreadHandle<FileSystemAccessRegularFileDelegate> delegate,
-      CrossThreadOnceFunction<void(base::File::Error)> callback,
-      base::File file,
-      scoped_refptr<base::SequencedTaskRunner> task_runner,
-      int64_t length);
-  // Performs the post file I/O part of truncate(), on the main thread.
-  void DidSetLength(CrossThreadOnceFunction<void(base::File::Error)> callback,
-                    int64_t new_length,
-                    base::File file,
-                    base::File::Error error);
-
-  // Performs the file I/O part of flush(), off the main thread.
-  static void DoFlush(
-      CrossThreadHandle<FileSystemAccessRegularFileDelegate> delegate,
-      CrossThreadOnceFunction<void(bool)> callback,
-      base::File file,
-      scoped_refptr<base::SequencedTaskRunner> task_runner);
-  // Performs the post file I/O part of flush(), on the main thread.
-  void DidFlush(CrossThreadOnceFunction<void(bool)> callback,
-                base::File file,
-                bool success);
-
-  // Performs the file I/O part of close(), off the main thread.
-  static void DoClose(
-      CrossThreadHandle<FileSystemAccessRegularFileDelegate> delegate,
-      CrossThreadOnceClosure callback,
-      base::File file,
-      scoped_refptr<base::SequencedTaskRunner> task_runner);
-  // Performs the post file I/O part of close(), on the main thread.
-  void DidClose(CrossThreadOnceClosure callback, base::File file);
-
-  // Called after preconditions for SetLength, including the requesting
-  // additional capacity (if needed), have been performed.
-  // If `request_capacity_result` is false, requesting capacity for the
-  // operation failed.
-  void DidCheckSetLengthCapacity(
-      base::OnceCallback<void(base::File::Error)> callback,
-      int64_t new_length,
-      bool request_capacity_result);
-
 #if BUILDFLAG(IS_MAC)
   // We need the FileUtilitiesHost only on Mac, where we have to execute
   // base::File::SetLength on the browser process, see crbug.com/1084565.
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
index f0a89dc2..024ce9a 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
@@ -7,14 +7,8 @@
 #include "base/files/file_error_or.h"
 #include "base/numerics/checked_math.h"
 #include "build/build_config.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
-#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
-
 namespace blink {
 
 FileSystemSyncAccessHandle::FileSystemSyncAccessHandle(
@@ -22,10 +16,7 @@
     FileSystemAccessFileDelegate* file_delegate,
     mojo::PendingRemote<mojom::blink::FileSystemAccessAccessHandleHost>
         access_handle_remote)
-    : file_delegate_(file_delegate),
-      access_handle_remote_(context),
-      resolver_task_runner_(
-          context->GetTaskRunner(TaskType::kMiscPlatformAPI)) {
+    : file_delegate_(file_delegate), access_handle_remote_(context) {
   access_handle_remote_.Bind(
       std::move(access_handle_remote),
       context->GetTaskRunner(TaskType::kMiscPlatformAPI));
@@ -36,19 +27,9 @@
   ScriptWrappable::Trace(visitor);
   visitor->Trace(file_delegate_);
   visitor->Trace(access_handle_remote_);
-  visitor->Trace(queued_close_resolver_);
 }
 
-ScriptValue FileSystemSyncAccessHandle::close(ScriptState* script_state) {
-  if (is_all_sync_interface_enabled_) {
-    CloseSync(script_state);
-    return ScriptValue::From(script_state, ToV8UndefinedGenerator());
-  } else {
-    return ScriptValue::From(script_state, CloseAsync(script_state));
-  }
-}
-
-void FileSystemSyncAccessHandle::CloseSync(ScriptState* script_state) {
+void FileSystemSyncAccessHandle::close() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (is_closed_ || !access_handle_remote_.is_bound()) {
     // close() is idempotent.
@@ -62,75 +43,7 @@
   access_handle_remote_->Close();
 }
 
-ScriptPromise FileSystemSyncAccessHandle::CloseAsync(
-    ScriptState* script_state) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  auto promise = resolver->Promise();
-
-  if (is_closed_ || !access_handle_remote_.is_bound()) {
-    // close() is idempotent.
-    resolver->Resolve();
-    return promise;
-  }
-
-  is_closed_ = true;
-
-  DCHECK(!queued_close_resolver_) << "Close logic kicked off twice";
-  queued_close_resolver_ = resolver;
-
-  if (!io_pending_) {
-    // Pretend that a close() promise was queued behind an I/O operation, and
-    // the operation just finished. This is less logic than handling the
-    // non-queued case separately.
-    DispatchQueuedClose();
-  }
-
-  return promise;
-}
-
-void FileSystemSyncAccessHandle::DispatchQueuedClose() {
-  DCHECK(!io_pending_)
-      << "Dispatching close() concurrently with other I/O operations is racy";
-
-  if (!queued_close_resolver_)
-    return;
-
-  DCHECK(is_closed_) << "close() resolver queued without setting closed_";
-  ScriptPromiseResolver* resolver = queued_close_resolver_;
-  queued_close_resolver_ = nullptr;
-
-  // Access file delegate directly rather than through accessor method, which
-  // checks `io_pending_`.
-  DCHECK(file_delegate_->IsValid())
-      << "file I/O operation queued after file closed";
-
-  file_delegate_->CloseAsync(WTF::BindOnce(
-      [](ScriptPromiseResolver* resolver,
-         FileSystemSyncAccessHandle* access_handle) {
-        ScriptState* script_state = resolver->GetScriptState();
-        if (!script_state->ContextIsValid())
-          return;
-        ScriptState::Scope scope(script_state);
-
-        access_handle->access_handle_remote_->Close(WTF::BindOnce(
-            [](ScriptPromiseResolver* resolver) { resolver->Resolve(); },
-            WrapPersistent(resolver)));
-      },
-      WrapPersistent(resolver), WrapPersistent(this)));
-}
-
-ScriptValue FileSystemSyncAccessHandle::flush(ScriptState* script_state,
-                                              ExceptionState& exception_state) {
-  if (is_all_sync_interface_enabled_) {
-    FlushSync(script_state, exception_state);
-    return ScriptValue::From(script_state, ToV8UndefinedGenerator());
-  } else {
-    return ScriptValue::From(script_state, FlushAsync(script_state));
-  }
-}
-
-void FileSystemSyncAccessHandle::FlushSync(ScriptState* script_state,
-                                           ExceptionState& exception_state) {
+void FileSystemSyncAccessHandle::flush(ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (is_closed_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
@@ -147,64 +60,7 @@
   }
 }
 
-ScriptPromise FileSystemSyncAccessHandle::FlushAsync(
-    ScriptState* script_state) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = resolver->Promise();
-
-  if (is_closed_) {
-    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
-        "The file was already closed"));
-    return result;
-  }
-
-  if (!EnterOperation()) {
-    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
-        "Another I/O operation is in progress on the same file"));
-    return result;
-  }
-
-  DCHECK(file_delegate()->IsValid())
-      << "file I/O operation queued after file closed";
-
-  file_delegate()->FlushAsync(WTF::BindOnce(WTF::BindOnce(
-      [](ScriptPromiseResolver* resolver,
-         FileSystemSyncAccessHandle* access_handle, bool success) {
-        ScriptState* script_state = resolver->GetScriptState();
-        if (!script_state->ContextIsValid())
-          return;
-        ScriptState::Scope scope(script_state);
-
-        access_handle->ExitOperation();
-        if (!success) {
-          resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-              script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
-              "flush failed"));
-          return;
-        }
-        resolver->Resolve();
-      },
-      WrapPersistent(resolver), WrapPersistent(this))));
-
-  return result;
-}
-
-ScriptValue FileSystemSyncAccessHandle::getSize(
-    ScriptState* script_state,
-    ExceptionState& exception_state) {
-  if (is_all_sync_interface_enabled_) {
-    return ScriptValue::From(script_state,
-                             GetSizeSync(script_state, exception_state));
-  } else {
-    return ScriptValue::From(script_state, GetSizeAsync(script_state));
-  }
-}
-
-uint64_t FileSystemSyncAccessHandle::GetSizeSync(
-    ScriptState* script_state,
-    ExceptionState& exception_state) {
+uint64_t FileSystemSyncAccessHandle::getSize(ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (is_closed_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
@@ -224,66 +80,8 @@
   return base::as_unsigned(error_or_length.value());
 }
 
-ScriptPromise FileSystemSyncAccessHandle::GetSizeAsync(
-    ScriptState* script_state) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = resolver->Promise();
-
-  if (is_closed_) {
-    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
-        "The file was already closed"));
-    return result;
-  }
-
-  if (!EnterOperation()) {
-    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
-        "Another I/O operation is in progress on the same file"));
-    return result;
-  }
-
-  DCHECK(file_delegate()->IsValid())
-      << "file I/O operation queued after file closed";
-
-  file_delegate()->GetLengthAsync(WTF::BindOnce(
-      [](ScriptPromiseResolver* resolver,
-         FileSystemSyncAccessHandle* access_handle,
-         base::FileErrorOr<int64_t> error_or_length) {
-        ScriptState* script_state = resolver->GetScriptState();
-        if (!script_state->ContextIsValid())
-          return;
-        ScriptState::Scope scope(script_state);
-
-        access_handle->ExitOperation();
-        if (!error_or_length.has_value()) {
-          resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-              script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
-              "getSize failed"));
-          return;
-        }
-        resolver->Resolve(error_or_length.value());
-      },
-      WrapPersistent(resolver), WrapPersistent(this)));
-
-  return result;
-}
-
-ScriptValue FileSystemSyncAccessHandle::truncate(
-    ScriptState* script_state,
-    uint64_t size,
-    ExceptionState& exception_state) {
-  if (is_all_sync_interface_enabled_) {
-    TruncateSync(script_state, size, exception_state);
-    return ScriptValue::From(script_state, ToV8UndefinedGenerator());
-  } else {
-    return ScriptValue::From(script_state, TruncateAsync(script_state, size));
-  }
-}
-
-void FileSystemSyncAccessHandle::TruncateSync(ScriptState* script_state,
-                                              uint64_t size,
-                                              ExceptionState& exception_state) {
+void FileSystemSyncAccessHandle::truncate(uint64_t size,
+                                          ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (is_closed_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
@@ -294,8 +92,7 @@
   DCHECK(file_delegate_->IsValid())
       << "file delgate invalidated before truncate";
 
-  if (!base::CheckedNumeric<int64_t>(size).IsValid() ||
-      !base::CheckedNumeric<uint64_t>(size).IsValid()) {
+  if (!base::CheckedNumeric<int64_t>(size).IsValid()) {
     exception_state.ThrowTypeError("Cannot truncate file to given length");
     return;
   }
@@ -317,92 +114,10 @@
   }
 }
 
-ScriptPromise FileSystemSyncAccessHandle::TruncateAsync(
-    ScriptState* script_state,
-    uint64_t size) {
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = resolver->Promise();
-
-  if (is_closed_) {
-    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
-        "The file was already closed"));
-    return result;
-  }
-
-  if (!base::CheckedNumeric<int64_t>(size).IsValid() ||
-      !base::CheckedNumeric<uint64_t>(size).IsValid()) {
-    resolver->Reject(V8ThrowException::CreateTypeError(
-        script_state->GetIsolate(), "Cannot truncate file to given length"));
-    return result;
-  }
-
-  if (!EnterOperation()) {
-    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
-        "Another I/O operation is in progress on the same file"));
-    return result;
-  }
-
-  DCHECK(file_delegate()->IsValid())
-      << "file I/O operation queued after file closed";
-
-  file_delegate()->SetLengthAsync(
-      size,
-      WTF::BindOnce(
-          [](ScriptPromiseResolver* resolver,
-             FileSystemSyncAccessHandle* access_handle,
-             base::File::Error file_error) {
-            ScriptState* script_state = resolver->GetScriptState();
-            if (!script_state->ContextIsValid())
-              return;
-            ScriptState::Scope scope(script_state);
-
-            access_handle->ExitOperation();
-            if (file_error == base::File::FILE_ERROR_NO_SPACE) {
-              resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-                  script_state->GetIsolate(),
-                  DOMExceptionCode::kQuotaExceededError,
-                  "No space available for this operation"));
-              return;
-            }
-            if (file_error != base::File::FILE_OK) {
-              resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
-                  script_state->GetIsolate(),
-                  DOMExceptionCode::kInvalidStateError, "truncate failed"));
-              return;
-            }
-            resolver->Resolve(true);
-          },
-          WrapPersistent(resolver), WrapPersistent(this)));
-
-  return result;
-}
-
 uint64_t FileSystemSyncAccessHandle::read(
     MaybeShared<DOMArrayBufferView> buffer,
     FileSystemReadWriteOptions* options,
     ExceptionState& exception_state) {
-  if (is_all_sync_interface_enabled_) {
-    return DoRead(buffer, options, exception_state);
-  } else {
-    // TODO(crbug.com/1338340): OperationScope is only used for async methods.
-    // Remove once we migrate all methods to be sync.
-    OperationScope scope(this);
-    if (!scope.entered_operation()) {
-      exception_state.ThrowDOMException(
-          DOMExceptionCode::kInvalidStateError,
-          "There is a pending operation on the access handle");
-      return 0;
-    }
-    return DoRead(buffer, options, exception_state);
-  }
-}
-
-uint64_t FileSystemSyncAccessHandle::DoRead(
-    MaybeShared<DOMArrayBufferView> buffer,
-    FileSystemReadWriteOptions* options,
-    ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!file_delegate()->IsValid() || is_closed_) {
@@ -434,26 +149,6 @@
     MaybeShared<DOMArrayBufferView> buffer,
     FileSystemReadWriteOptions* options,
     ExceptionState& exception_state) {
-  if (is_all_sync_interface_enabled_) {
-    return DoWrite(buffer, options, exception_state);
-  } else {
-    // TODO(crbug.com/1338340): OperationScope is only used for async methods.
-    // Remove once we migrate all methods to be sync.
-    OperationScope scope(this);
-    if (!scope.entered_operation()) {
-      exception_state.ThrowDOMException(
-          DOMExceptionCode::kInvalidStateError,
-          "There is a pending operation on the access handle");
-      return 0;
-    }
-    return DoWrite(buffer, options, exception_state);
-  }
-}
-
-uint64_t FileSystemSyncAccessHandle::DoWrite(
-    MaybeShared<DOMArrayBufferView> buffer,
-    FileSystemReadWriteOptions* options,
-    ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!file_delegate()->IsValid() || is_closed_) {
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h
index 140506a..a358741 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h
@@ -5,14 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_SYNC_ACCESS_HANDLE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_SYNC_ACCESS_HANDLE_H_
 
-#include "base/feature_list.h"
 #include "base/sequence_checker.h"
-#include "base/task/sequenced_task_runner.h"
-#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_access_handle_host.mojom-blink.h"
-#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_file_system_read_write_options.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
@@ -20,12 +14,9 @@
 #include "third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
-class ScriptPromiseResolver;
-
 class FileSystemSyncAccessHandle final : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -43,13 +34,13 @@
   // GarbageCollected
   void Trace(Visitor* visitor) const override;
 
-  ScriptValue close(ScriptState*);
+  void close();
 
-  ScriptValue flush(ScriptState*, ExceptionState&);
+  void flush(ExceptionState&);
 
-  ScriptValue getSize(ScriptState*, ExceptionState&);
+  uint64_t getSize(ExceptionState&);
 
-  ScriptValue truncate(ScriptState*, uint64_t size, ExceptionState&);
+  void truncate(uint64_t size, ExceptionState&);
 
   uint64_t read(MaybeShared<DOMArrayBufferView> buffer,
                 FileSystemReadWriteOptions* options,
@@ -60,79 +51,10 @@
                  ExceptionState&);
 
  private:
-  void CloseSync(ScriptState*);
-  ScriptPromise CloseAsync(ScriptState*);
-  void FlushSync(ScriptState*, ExceptionState&);
-  ScriptPromise FlushAsync(ScriptState*);
-  uint64_t GetSizeSync(ScriptState*, ExceptionState&);
-  ScriptPromise GetSizeAsync(ScriptState*);
-  void TruncateSync(ScriptState*, uint64_t size, ExceptionState&);
-  ScriptPromise TruncateAsync(ScriptState*, uint64_t size);
-  uint64_t DoRead(MaybeShared<DOMArrayBufferView> buffer,
-                  FileSystemReadWriteOptions* options,
-                  ExceptionState&);
-  uint64_t DoWrite(MaybeShared<DOMArrayBufferView> buffer,
-                   FileSystemReadWriteOptions* options,
-                   ExceptionState&);
-  void DispatchQueuedClose();
-
-  // Must be called right before calling async methods on file_delegate.
-  bool EnterOperation() {
-    if (is_all_sync_interface_enabled_) {
-      NOTREACHED();
-      return false;
-    }
-    if (io_pending_)
-      return false;
-    io_pending_ = true;
-    return true;
-  }
-
-  void ExitOperation() {
-    if (is_all_sync_interface_enabled_) {
-      NOTREACHED();
-      return;
-    }
-    DCHECK(io_pending_);
-    io_pending_ = false;
-    DispatchQueuedClose();
-  }
-
-  FileSystemAccessFileDelegate* file_delegate() {
-    DCHECK(io_pending_ || is_all_sync_interface_enabled_);
-    return file_delegate_.Get();
-  }
+  FileSystemAccessFileDelegate* file_delegate() { return file_delegate_.Get(); }
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  // TODO(crbug.com/1338340): This method is only used for async methods.
-  // Remove once we migrate all methods to be sync.
-  //
-  // The {OperationScope} is used to call {EnterOperation()} and
-  // {ExitOperation()} in the synchronous functions {read} and {write}.
-  // {OperationScope} calls {ExitOperation()} automatically in its destructor.
-  class OperationScope {
-    STACK_ALLOCATED();
-
-   public:
-    explicit OperationScope(FileSystemSyncAccessHandle* handle)
-        : handle_(handle) {
-      entered_operation_ = handle_->EnterOperation();
-    }
-
-    ~OperationScope() {
-      if (entered_operation_) {
-        handle_->ExitOperation();
-      }
-    }
-
-    bool entered_operation() const { return entered_operation_; }
-
-   private:
-    FileSystemSyncAccessHandle* handle_;
-    bool entered_operation_;
-  };
-
   // Interface that provides file-like access to the backing storage.
   // The file delegate should only be accessed through the {file_delegate()}
   // getter.
@@ -142,43 +64,7 @@
   HeapMojoRemote<mojom::blink::FileSystemAccessAccessHandleHost>
       access_handle_remote_;
 
-  // TODO(crbug.com/1338340): This method is only used for async methods.
-  // Remove once we migrate all methods to be sync.
-  //
-  // True when an I/O operation other than close is underway.
-  //
-  // Set to {true} whenever an async operation is started, and back to {false}
-  // when the operation resolves its promise.
-  // All I/O operations throw an exception if they get called when
-  // {io_pending_} is true, except for close(). This ensures that at most one
-  // I/O operation is underway at any given time. When close() is called when
-  // {io_pending_} is true, then the close() operation gets queued right after
-  // the pending I/O operation.
-  // {io_pending_} should only be set with the {EnterOperation()} and
-  // {ExitOperation()} functions.
-  bool io_pending_ = false;
-
   bool is_closed_ = false;
-
-  // Whether all-sync interface feature is enabled and the async interface is
-  // not force-enabled by enterprise policy, as indicated by the switch.
-  const bool is_all_sync_interface_enabled_ =
-      base::FeatureList::IsEnabled(
-          blink::features::kSyncAccessHandleAllSyncSurface) &&
-      !RuntimeEnabledFeatures::
-          FileSystemSyncAccessHandleAsyncInterfaceOverrideEnabled();
-
-  // crbug.com/1338340: Note that this is only used (and valid) when async
-  // methods are in-use before the migration to the all-sync interface.
-  //
-  // Non-null when a close() I/O is queued behind another I/O operation.
-  //
-  // Set when close() is called while another I/O operation is underway. Cleared
-  // when the queued close() operation is queued.
-  Member<ScriptPromiseResolver> queued_close_resolver_;
-
-  // Schedules resolving Promises with file I/O results.
-  const scoped_refptr<base::SequencedTaskRunner> resolver_task_runner_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.idl b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.idl
index 12e07603..33eb400 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.idl
@@ -14,30 +14,22 @@
   RuntimeEnabled=FileSystemAccessAccessHandle,
   SecureContext
 ] interface FileSystemSyncAccessHandle {
-  [CallWith=ScriptState, Measure] any close();
+  [Measure] void close();
 
-[
-  CallWith=ScriptState, RaisesException, Measure
-] any flush();
+  [RaisesException, Measure] void flush();
 
-[
-  CallWith=ScriptState, RaisesException, Measure
-] any getSize();
+  [RaisesException, Measure] unsigned long long getSize();
 
-// TODO(crbug.com/1338340): Add back [EnforceRange] to `size` after all methods
-// migrated to be sync. This check is currently done in 
-// `file_system_sync_access_handle.cc` due to `any` return type wrapping a
-// ScriptPromise throwing a TypeError instead of rejecting a promise.
-[
-  CallWith = ScriptState, RaisesException, Measure
-] any truncate(unsigned long long size);
+  [
+    RaisesException, Measure
+  ] void truncate([EnforceRange] unsigned long long size);
 
-[
-  RaisesException, Measure
-] unsigned long long read([AllowShared] ArrayBufferView buffer,
-                           optional FileSystemReadWriteOptions options = {});
-[
-  RaisesException, Measure
-] unsigned long long write([AllowShared] ArrayBufferView buffer,
-                           optional FileSystemReadWriteOptions options = {});
+  [
+    RaisesException, Measure
+  ] unsigned long long read([AllowShared] ArrayBufferView buffer,
+                            optional FileSystemReadWriteOptions options = {});
+  [
+    RaisesException, Measure
+  ] unsigned long long write([AllowShared] ArrayBufferView buffer,
+                            optional FileSystemReadWriteOptions options = {});
 };
diff --git a/third_party/blink/renderer/modules/imagecapture/image_capture.cc b/third_party/blink/renderer/modules/imagecapture/image_capture.cc
index 03f7505..cf29bf4 100644
--- a/third_party/blink/renderer/modules/imagecapture/image_capture.cc
+++ b/third_party/blink/renderer/modules/imagecapture/image_capture.cc
@@ -10,6 +10,7 @@
 #include "base/containers/contains.h"
 #include "base/functional/callback_helpers.h"
 #include "base/trace_event/trace_event.h"
+#include "base/types/strong_alias.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom-blink.h"
@@ -56,6 +57,97 @@
 const char kInvalidStateTrackError[] =
     "The associated Track is in an invalid state";
 
+using CopyPanTiltZoom = base::StrongAlias<class CopyPanTiltZoomTag, bool>;
+
+template <typename T>
+void CopyCommonMembers(const T* source,
+                       T* destination,
+                       CopyPanTiltZoom copy_pan_tilt_zoom) {
+  DCHECK(source);
+  DCHECK(destination);
+  // Merge any present |source| common members into |destination|.
+  if (source->hasWhiteBalanceMode()) {
+    destination->setWhiteBalanceMode(source->whiteBalanceMode());
+  }
+  if (source->hasExposureMode()) {
+    destination->setExposureMode(source->exposureMode());
+  }
+  if (source->hasFocusMode()) {
+    destination->setFocusMode(source->focusMode());
+  }
+  if (source->hasExposureCompensation()) {
+    destination->setExposureCompensation(source->exposureCompensation());
+  }
+  if (source->hasExposureTime()) {
+    destination->setExposureTime(source->exposureTime());
+  }
+  if (source->hasColorTemperature()) {
+    destination->setColorTemperature(source->colorTemperature());
+  }
+  if (source->hasIso()) {
+    destination->setIso(source->iso());
+  }
+  if (source->hasBrightness()) {
+    destination->setBrightness(source->brightness());
+  }
+  if (source->hasContrast()) {
+    destination->setContrast(source->contrast());
+  }
+  if (source->hasSaturation()) {
+    destination->setSaturation(source->saturation());
+  }
+  if (source->hasSharpness()) {
+    destination->setSharpness(source->sharpness());
+  }
+  if (source->hasFocusDistance()) {
+    destination->setFocusDistance(source->focusDistance());
+  }
+  if (copy_pan_tilt_zoom) {
+    if (source->hasPan()) {
+      destination->setPan(source->pan());
+    }
+    if (source->hasTilt()) {
+      destination->setTilt(source->tilt());
+    }
+    if (source->hasZoom()) {
+      destination->setZoom(source->zoom());
+    }
+  }
+  if (source->hasTorch()) {
+    destination->setTorch(source->torch());
+  }
+  if (source->hasBackgroundBlur()) {
+    destination->setBackgroundBlur(source->backgroundBlur());
+  }
+}
+
+void CopyCapabilities(const MediaTrackCapabilities* source,
+                      MediaTrackCapabilities* destination,
+                      CopyPanTiltZoom copy_pan_tilt_zoom) {
+  // Merge any present |source| members into |destination|.
+  CopyCommonMembers(source, destination, copy_pan_tilt_zoom);
+}
+
+void CopyConstraintSet(const MediaTrackConstraintSet* source,
+                       MediaTrackConstraintSet* destination,
+                       CopyPanTiltZoom copy_pan_tilt_zoom) {
+  // Merge any present |source| members into |destination|.
+  CopyCommonMembers(source, destination, copy_pan_tilt_zoom);
+  if (source->hasPointsOfInterest()) {
+    destination->setPointsOfInterest(source->pointsOfInterest());
+  }
+}
+
+void CopySettings(const MediaTrackSettings* source,
+                  MediaTrackSettings* destination,
+                  CopyPanTiltZoom copy_pan_tilt_zoom) {
+  // Merge any present |source| members into |destination|.
+  CopyCommonMembers(source, destination, copy_pan_tilt_zoom);
+  if (source->hasPointsOfInterest() && !source->pointsOfInterest().empty()) {
+    destination->setPointsOfInterest(source->pointsOfInterest());
+  }
+}
+
 bool TrackIsInactive(const MediaStreamTrack& track) {
   // Spec instructs to return an exception if the Track's readyState() is not
   // "live". Also reject if the track is disabled or muted.
@@ -378,51 +470,8 @@
 void ImageCapture::GetMediaTrackCapabilities(
     MediaTrackCapabilities* capabilities) const {
   // Merge any present |capabilities_| members into |capabilities|.
-
-  if (capabilities_->hasWhiteBalanceMode())
-    capabilities->setWhiteBalanceMode(capabilities_->whiteBalanceMode());
-  if (capabilities_->hasExposureMode())
-    capabilities->setExposureMode(capabilities_->exposureMode());
-  if (capabilities_->hasFocusMode())
-    capabilities->setFocusMode(capabilities_->focusMode());
-  if (capabilities_->hasExposureCompensation()) {
-    capabilities->setExposureCompensation(
-        capabilities_->exposureCompensation());
-  }
-  if (capabilities_->hasExposureTime())
-    capabilities->setExposureTime(capabilities_->exposureTime());
-
-  if (capabilities_->hasColorTemperature())
-    capabilities->setColorTemperature(capabilities_->colorTemperature());
-  if (capabilities_->hasIso())
-    capabilities->setIso(capabilities_->iso());
-
-  if (capabilities_->hasBrightness())
-    capabilities->setBrightness(capabilities_->brightness());
-  if (capabilities_->hasContrast())
-    capabilities->setContrast(capabilities_->contrast());
-  if (capabilities_->hasSaturation())
-    capabilities->setSaturation(capabilities_->saturation());
-  if (capabilities_->hasSharpness())
-    capabilities->setSharpness(capabilities_->sharpness());
-
-  if (capabilities_->hasFocusDistance())
-    capabilities->setFocusDistance(capabilities_->focusDistance());
-
-  if (HasPanTiltZoomPermissionGranted()) {
-    if (capabilities_->hasPan())
-      capabilities->setPan(capabilities_->pan());
-    if (capabilities_->hasTilt())
-      capabilities->setTilt(capabilities_->tilt());
-    if (capabilities_->hasZoom())
-      capabilities->setZoom(capabilities_->zoom());
-  }
-
-  if (capabilities_->hasTorch())
-    capabilities->setTorch(capabilities_->torch());
-
-  if (capabilities_->hasBackgroundBlur())
-    capabilities->setBackgroundBlur(capabilities_->backgroundBlur());
+  CopyCapabilities(capabilities_, capabilities,
+                   CopyPanTiltZoom(HasPanTiltZoomPermissionGranted()));
 }
 
 // TODO(mcasas): make the implementation fully Spec compliant, see the TODOs
@@ -872,54 +921,8 @@
 
 void ImageCapture::GetMediaTrackSettings(MediaTrackSettings* settings) const {
   // Merge any present |settings_| members into |settings|.
-
-  if (settings_->hasWhiteBalanceMode())
-    settings->setWhiteBalanceMode(settings_->whiteBalanceMode());
-  if (settings_->hasExposureMode())
-    settings->setExposureMode(settings_->exposureMode());
-  if (settings_->hasFocusMode())
-    settings->setFocusMode(settings_->focusMode());
-
-  if (settings_->hasPointsOfInterest() &&
-      !settings_->pointsOfInterest().empty()) {
-    settings->setPointsOfInterest(settings_->pointsOfInterest());
-  }
-
-  if (settings_->hasExposureCompensation())
-    settings->setExposureCompensation(settings_->exposureCompensation());
-  if (settings_->hasExposureTime())
-    settings->setExposureTime(settings_->exposureTime());
-  if (settings_->hasColorTemperature())
-    settings->setColorTemperature(settings_->colorTemperature());
-  if (settings_->hasIso())
-    settings->setIso(settings_->iso());
-
-  if (settings_->hasBrightness())
-    settings->setBrightness(settings_->brightness());
-  if (settings_->hasContrast())
-    settings->setContrast(settings_->contrast());
-  if (settings_->hasSaturation())
-    settings->setSaturation(settings_->saturation());
-  if (settings_->hasSharpness())
-    settings->setSharpness(settings_->sharpness());
-
-  if (settings_->hasFocusDistance())
-    settings->setFocusDistance(settings_->focusDistance());
-
-  if (HasPanTiltZoomPermissionGranted()) {
-    if (settings_->hasPan())
-      settings->setPan(settings_->pan());
-    if (settings_->hasTilt())
-      settings->setTilt(settings_->tilt());
-    if (settings_->hasZoom())
-      settings->setZoom(settings_->zoom());
-  }
-
-  if (settings_->hasTorch())
-    settings->setTorch(settings_->torch());
-
-  if (settings_->hasBackgroundBlur())
-    settings->setBackgroundBlur(settings_->backgroundBlur());
+  CopySettings(settings_, settings,
+               CopyPanTiltZoom(HasPanTiltZoomPermissionGranted()));
 }
 
 ImageCapture::ImageCapture(ExecutionContext* context,
@@ -1256,153 +1259,16 @@
       /*callback=*/base::DoNothing());
 
   // Copy capabilities.
-  if (capabilities_->hasWhiteBalanceMode()) {
-    clone->capabilities_->setWhiteBalanceMode(
-        capabilities_->whiteBalanceMode());
-  }
-  if (capabilities_->hasExposureMode())
-    clone->capabilities_->setExposureMode(capabilities_->exposureMode());
-  if (capabilities_->hasFocusMode())
-    clone->capabilities_->setFocusMode(capabilities_->focusMode());
-  if (capabilities_->hasExposureCompensation()) {
-    clone->capabilities_->setExposureCompensation(
-        capabilities_->exposureCompensation());
-  }
-  if (capabilities_->hasExposureTime())
-    clone->capabilities_->setExposureTime(capabilities_->exposureTime());
-  if (capabilities_->hasColorTemperature()) {
-    clone->capabilities_->setColorTemperature(
-        capabilities_->colorTemperature());
-  }
-  if (capabilities_->hasIso())
-    clone->capabilities_->setIso(capabilities_->iso());
-  if (capabilities_->hasBrightness())
-    clone->capabilities_->setBrightness(capabilities_->brightness());
-  if (capabilities_->hasContrast())
-    clone->capabilities_->setContrast(capabilities_->contrast());
-  if (capabilities_->hasSaturation())
-    clone->capabilities_->setSaturation(capabilities_->saturation());
-  if (capabilities_->hasSharpness())
-    clone->capabilities_->setSharpness(capabilities_->sharpness());
-  if (capabilities_->hasFocusDistance())
-    clone->capabilities_->setFocusDistance(capabilities_->focusDistance());
-  if (capabilities_->hasPan())
-    clone->capabilities_->setPan(capabilities_->pan());
-  if (capabilities_->hasTilt())
-    clone->capabilities_->setTilt(capabilities_->tilt());
-  if (capabilities_->hasZoom())
-    clone->capabilities_->setZoom(capabilities_->zoom());
-  if (capabilities_->hasTorch())
-    clone->capabilities_->setTorch(capabilities_->torch());
-  if (capabilities_->hasBackgroundBlur())
-    clone->capabilities_->setBackgroundBlur(capabilities_->backgroundBlur());
+  CopyCapabilities(capabilities_, clone->capabilities_, CopyPanTiltZoom(true));
 
   // Copy settings.
-  if (settings_->hasWhiteBalanceMode())
-    clone->settings_->setWhiteBalanceMode(settings_->whiteBalanceMode());
-  if (settings_->hasExposureMode())
-    clone->settings_->setExposureMode(settings_->exposureMode());
-  if (settings_->hasFocusMode())
-    clone->settings_->setFocusMode(settings_->focusMode());
-  if (settings_->hasPointsOfInterest() &&
-      !settings_->pointsOfInterest().empty()) {
-    clone->settings_->setPointsOfInterest(settings_->pointsOfInterest());
-  }
-  if (settings_->hasExposureCompensation()) {
-    clone->settings_->setExposureCompensation(
-        settings_->exposureCompensation());
-  }
-  if (settings_->hasExposureTime())
-    clone->settings_->setExposureTime(settings_->exposureTime());
-  if (settings_->hasColorTemperature())
-    clone->settings_->setColorTemperature(settings_->colorTemperature());
-  if (settings_->hasIso())
-    clone->settings_->setIso(settings_->iso());
-  if (settings_->hasBrightness())
-    clone->settings_->setBrightness(settings_->brightness());
-  if (settings_->hasContrast())
-    clone->settings_->setContrast(settings_->contrast());
-  if (settings_->hasSaturation())
-    clone->settings_->setSaturation(settings_->saturation());
-  if (settings_->hasSharpness())
-    clone->settings_->setSharpness(settings_->sharpness());
-  if (settings_->hasFocusDistance())
-    clone->settings_->setFocusDistance(settings_->focusDistance());
-  if (settings_->hasPan())
-    clone->settings_->setPan(settings_->pan());
-  if (settings_->hasTilt())
-    clone->settings_->setTilt(settings_->tilt());
-  if (settings_->hasZoom())
-    clone->settings_->setZoom(settings_->zoom());
-  if (settings_->hasTorch())
-    clone->settings_->setTorch(settings_->torch());
-  if (settings_->hasBackgroundBlur())
-    clone->settings_->setBackgroundBlur(settings_->backgroundBlur());
-
-  if (!current_constraints_)
-    return clone;
+  CopySettings(settings_, clone->settings_, CopyPanTiltZoom(true));
 
   // Copy current constraints.
-  clone->current_constraints_ = MediaTrackConstraintSet::Create();
-  if (current_constraints_->hasWhiteBalanceMode()) {
-    clone->current_constraints_->setWhiteBalanceMode(
-        current_constraints_->whiteBalanceMode());
-  }
-  if (current_constraints_->hasExposureMode()) {
-    clone->current_constraints_->setExposureMode(
-        current_constraints_->exposureMode());
-  }
-  if (current_constraints_->hasFocusMode()) {
-    clone->current_constraints_->setFocusMode(
-        current_constraints_->focusMode());
-  }
-  if (current_constraints_->hasPointsOfInterest()) {
-    clone->current_constraints_->setPointsOfInterest(
-        current_constraints_->pointsOfInterest());
-  }
-  if (current_constraints_->hasExposureCompensation()) {
-    clone->current_constraints_->setExposureCompensation(
-        current_constraints_->exposureCompensation());
-  }
-  if (current_constraints_->hasExposureTime()) {
-    clone->current_constraints_->setExposureTime(
-        current_constraints_->exposureTime());
-  }
-  if (current_constraints_->hasColorTemperature()) {
-    clone->current_constraints_->setColorTemperature(
-        current_constraints_->colorTemperature());
-  }
-  if (current_constraints_->hasIso())
-    clone->current_constraints_->setIso(current_constraints_->iso());
-  if (current_constraints_->hasBrightness()) {
-    clone->current_constraints_->setBrightness(
-        current_constraints_->brightness());
-  }
-  if (current_constraints_->hasContrast())
-    clone->current_constraints_->setContrast(current_constraints_->contrast());
-  if (current_constraints_->hasSaturation()) {
-    clone->current_constraints_->setSaturation(
-        current_constraints_->saturation());
-  }
-  if (current_constraints_->hasSharpness()) {
-    clone->current_constraints_->setSharpness(
-        current_constraints_->sharpness());
-  }
-  if (current_constraints_->hasFocusDistance()) {
-    clone->current_constraints_->setFocusDistance(
-        current_constraints_->focusDistance());
-  }
-  if (current_constraints_->hasPan())
-    clone->current_constraints_->setPan(current_constraints_->pan());
-  if (current_constraints_->hasTilt())
-    clone->current_constraints_->setTilt(current_constraints_->tilt());
-  if (current_constraints_->hasZoom())
-    clone->current_constraints_->setZoom(current_constraints_->zoom());
-  if (current_constraints_->hasTorch())
-    clone->current_constraints_->setTorch(current_constraints_->torch());
-  if (current_constraints_->hasBackgroundBlur()) {
-    clone->current_constraints_->setBackgroundBlur(
-        current_constraints_->backgroundBlur());
+  if (current_constraints_) {
+    clone->current_constraints_ = MediaTrackConstraintSet::Create();
+    CopyConstraintSet(current_constraints_, clone->current_constraints_,
+                      CopyPanTiltZoom(true));
   }
 
   return clone;
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
index 13398a9..cc7010d 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -125,8 +125,9 @@
 void MaybeLogStreamDevice(const int32_t& request_id,
                           const String& label,
                           const absl::optional<MediaStreamDevice>& device) {
-  if (!device.has_value())
+  if (!device.has_value()) {
     return;
+  }
 
   SendLogMessage(base::StringPrintf(
       "OnStreamsGenerated({request_id=%d}, {label=%s}, {device=[id: %s, "
@@ -553,8 +554,9 @@
     const String& result_name) {
   // Check if we're waiting to be notified of this source.  If not, then we'll
   // ignore the notification.
-  if (base::Contains(sources_waiting_for_callback_, source))
+  if (base::Contains(sources_waiting_for_callback_, source)) {
     OnTrackStarted(source, result, result_name);
+  }
 }
 
 UserMediaProcessor::UserMediaProcessor(
@@ -671,8 +673,9 @@
         });
     if (it != local_sources_.end()) {
       WebPlatformMediaStreamSource* const source = (*it)->GetPlatformSource();
-      if (source->device().type == MediaStreamType::DEVICE_AUDIO_CAPTURE)
+      if (source->device().type == MediaStreamType::DEVICE_AUDIO_CAPTURE) {
         audio_source = static_cast<MediaStreamAudioSource*>(source);
+      }
     }
     if (audio_source) {
       capabilities.emplace_back(audio_source);
@@ -692,8 +695,9 @@
   // The frame might reload or |user_media_request| might be cancelled while
   // capabilities are queried. Do nothing if a different request is being
   // processed at this point.
-  if (!IsCurrentRequestInfo(user_media_request))
+  if (!IsCurrentRequestInfo(user_media_request)) {
     return;
+  }
 
   DCHECK(current_request_info_->stream_controls()->audio.requested());
   SendLogMessage(base::StringPrintf("SelectAudioSettings({request_id=%d})",
@@ -854,8 +858,9 @@
   // The frame might reload or |user_media_request| might be cancelled while
   // capabilities are queried. Do nothing if a different request is being
   // processed at this point.
-  if (!IsCurrentRequestInfo(user_media_request))
+  if (!IsCurrentRequestInfo(user_media_request)) {
     return;
+  }
 
   DCHECK(current_request_info_->stream_controls()->video.requested());
   DCHECK(blink::IsDeviceMediaType(
@@ -982,8 +987,9 @@
   if (frame_) {  // Can be null for tests.
     auto* web_frame =
         static_cast<WebLocalFrame*>(WebFrame::FromCoreFrame(frame_));
-    if (!web_frame || !web_frame->Client())
+    if (!web_frame || !web_frame->Client()) {
       return nullptr;
+    }
 
     // TODO(704136): Move ownership of |WebMediaStreamDeviceObserver| out of
     // RenderFrameImpl, back to UserMediaClient.
@@ -1127,8 +1133,9 @@
   // The frame might reload or |user_media_request| might be cancelled while
   // video formats are queried. Do nothing if a different request is being
   // processed at this point.
-  if (!IsCurrentRequestInfo(user_media_request))
+  if (!IsCurrentRequestInfo(user_media_request)) {
     return;
+  }
 
   // TODO(crbug.com/1336564): Remove the assumption that all devices support
   // the same video formats.
@@ -1140,8 +1147,9 @@
                            label.Utf8().c_str(), device_id.Utf8().c_str()));
     current_request_info_->AddNativeVideoFormats(device_id, formats);
   }
-  if (current_request_info_->CanStartTracks())
+  if (current_request_info_->CanStartTracks()) {
     StartTracks(label);
+  }
 }
 
 gfx::Size UserMediaProcessor::GetScreenSize() {
@@ -1205,10 +1213,12 @@
        it != pending_local_sources_.end(); ++it) {
     blink::WebPlatformMediaStreamSource* const source_extra_data =
         (*it)->GetPlatformSource();
-    if (source_extra_data != source)
+    if (source_extra_data != source) {
       continue;
-    if (result == MediaStreamRequestResult::OK)
+    }
+    if (result == MediaStreamRequestResult::OK) {
       local_sources_.push_back((*it));
+    }
     pending_local_sources_.erase(it);
 
     NotifyCurrentRequestInfoOfAudioSourceStarted(source, result, result_name);
@@ -1221,8 +1231,9 @@
     MediaStreamRequestResult result,
     const String& result_name) {
   // The only request possibly being processed is |current_request_info_|.
-  if (current_request_info_)
+  if (current_request_info_) {
     current_request_info_->OnAudioSourceStarted(source, result, result_name);
+  }
 }
 
 void UserMediaProcessor::OnStreamGenerationFailed(
@@ -1409,8 +1420,9 @@
 
   // See if the source is already being initialized.
   auto* pending = FindPendingLocalSource(device);
-  if (pending)
+  if (pending) {
     return pending;
+  }
 
   MediaStreamSource* existing_source = FindLocalSource(device);
   if (existing_source) {
@@ -1488,8 +1500,9 @@
   }
 
   capabilities.device_id = blink::WebString::FromUTF8(device.id);
-  if (device.group_id)
+  if (device.group_id) {
     capabilities.group_id = blink::WebString::FromUTF8(*device.group_id);
+  }
 
   MediaStreamSource* source =
       InitializeSourceObject(device, std::move(audio_source));
@@ -1604,8 +1617,9 @@
     const absl::optional<MediaStreamDevice>& device) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(current_request_info_);
-  if (!device)
+  if (!device) {
     return nullptr;
+  }
   MediaStreamSource* source = InitializeVideoSourceObject(*device);
   MediaStreamComponent* component =
       current_request_info_->CreateAndStartVideoTrack(source);
@@ -1619,8 +1633,9 @@
     const absl::optional<MediaStreamDevice>& device) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(current_request_info_);
-  if (!device)
+  if (!device) {
     return nullptr;
+  }
   MediaStreamDevice overriden_audio_device = *device;
   bool render_to_associated_sink =
       current_request_info_->audio_capture_settings().HasValue() &&
@@ -1681,15 +1696,17 @@
       for (auto web_track : descriptor->AudioComponents()) {
         MediaStreamTrackPlatform* track =
             MediaStreamTrackPlatform::GetTrack(WebMediaStreamTrack(web_track));
-        if (track)
+        if (track) {
           track->Stop();
+        }
       }
 
       for (auto web_track : descriptor->VideoComponents()) {
         MediaStreamTrackPlatform* track =
             MediaStreamTrackPlatform::GetTrack(WebMediaStreamTrack(web_track));
-        if (track)
+        if (track) {
           track->Stop();
+        }
       }
     }
   }
@@ -1790,8 +1807,9 @@
     WebPlatformMediaStreamSource* const source =
         local_source->GetPlatformSource();
     const MediaStreamDevice& active_device = source->device();
-    if (IsSameDevice(active_device, device))
+    if (IsSameDevice(active_device, device)) {
       return local_source;
+    }
   }
   return nullptr;
 }
@@ -1807,8 +1825,9 @@
       String::FromUTF8(device.id), device.display_id, type,
       String::FromUTF8(device.name), false /* remote */,
       std::move(platform_source));
-  if (device.group_id)
+  if (device.group_id) {
     source->SetGroupId(String::FromUTF8(*device.group_id));
+  }
   return source;
 }
 
@@ -1928,8 +1947,9 @@
   // The client can be null if the frame is already detached.
   // If it's already detached, dispatcher_host_ shouldn't be bound again.
   // (ref: crbug.com/1105842)
-  if (!frame_->Client())
+  if (!frame_->Client()) {
     return;
+  }
 
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   blink::WebPlatformMediaStreamSource* source_impl = source.GetPlatformSource();
@@ -1940,8 +1960,9 @@
   const bool some_source_removed = RemoveLocalSource(source);
   CHECK(some_source_removed);
 
-  if (auto* media_stream_device_observer = GetMediaStreamDeviceObserver())
+  if (auto* media_stream_device_observer = GetMediaStreamDeviceObserver()) {
     media_stream_device_observer->RemoveStreamDevice(source_impl->device());
+  }
 
   String device_id(source_impl->device().id.data());
   GetMediaStreamDispatcherHost()->StopStreamDevice(
@@ -1956,8 +1977,9 @@
       source_impl->device().session_id().ToString().c_str()));
 
   if (notify_dispatcher) {
-    if (auto* media_stream_device_observer = GetMediaStreamDeviceObserver())
+    if (auto* media_stream_device_observer = GetMediaStreamDeviceObserver()) {
       media_stream_device_observer->RemoveStreamDevice(source_impl->device());
+    }
 
     String device_id(source_impl->device().id.data());
     GetMediaStreamDispatcherHost()->StopStreamDevice(
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
index 292b38e..5a2c24e 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
@@ -7,7 +7,6 @@
 #include <algorithm>
 
 #include "base/numerics/checked_math.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_clamp_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_conv_2d_options.h"
@@ -133,53 +132,6 @@
   return output;
 }
 
-struct PaddingSizes {
-  uint32_t begin;
-  uint32_t end;
-};
-
-// Calculate the padding given auto pad, input size, filter size, stride and
-// dilation. Return the calculated padding sizes if no error.
-absl::optional<PaddingSizes> CalculatePaddingForAutoPad(
-    V8MLAutoPad::Enum auto_pad,
-    const uint32_t input_size,
-    const uint32_t filter_size,
-    const uint32_t stride,
-    const uint32_t dilation) {
-  auto checked_output_size =
-      (base::MakeCheckedNum<uint32_t>(input_size) + stride - 1) / stride;
-  auto checked_dilated_filter_size =
-      (base::MakeCheckedNum<uint32_t>(filter_size) - 1) * dilation + 1;
-  auto checked_needed_input_size =
-      (checked_output_size - 1) * stride + checked_dilated_filter_size;
-  if (!checked_needed_input_size.IsValid()) {
-    return absl::nullopt;
-  }
-  auto checked_total_padding =
-      checked_needed_input_size.ValueOrDie() > input_size
-          ? checked_needed_input_size - input_size
-          : base::MakeCheckedNum<uint32_t>(0);
-  base::CheckedNumeric<uint32_t> checked_padding_begin, checked_padding_end;
-  switch (auto_pad) {
-    case V8MLAutoPad::Enum::kSameUpper:
-      checked_padding_begin = checked_total_padding / 2;
-      checked_padding_end = (checked_total_padding + 1) / 2;
-      break;
-    case V8MLAutoPad::Enum::kSameLower:
-      checked_padding_begin = (checked_total_padding + 1) / 2;
-      checked_padding_end = checked_total_padding / 2;
-      break;
-    default:
-      NOTREACHED();
-  }
-  uint32_t padding_begin, padding_end;
-  if (!checked_padding_begin.AssignIfValid(&padding_begin) ||
-      !checked_padding_end.AssignIfValid(&padding_end)) {
-    return absl::nullopt;
-  }
-  return PaddingSizes({.begin = padding_begin, .end = padding_end});
-}
-
 // Calculate the output size for conv2d based on WebNN spec:
 // https://www.w3.org/TR/webnn/#api-mlgraphbuilder-conv2d
 // Return the calculated output size if no error.
@@ -315,7 +267,7 @@
   // options.padding array are ignored and the explicit padding values need to
   // be calculated.
   if (auto_pad != V8MLAutoPad::Enum::kExplicit) {
-    auto padding_sizes_height = CalculatePaddingForAutoPad(
+    auto padding_sizes_height = MLGraphBuilder::CalculatePaddingForAutoPad(
         auto_pad.AsEnum(), input_height, filter_height, stride_height,
         dilation_height);
     if (!padding_sizes_height) {
@@ -327,9 +279,9 @@
     }
     padding_beginning_height = padding_sizes_height.value().begin;
     padding_ending_height = padding_sizes_height.value().end;
-    auto padding_sizes_width =
-        CalculatePaddingForAutoPad(auto_pad.AsEnum(), input_width, filter_width,
-                                   stride_width, dilation_width);
+    auto padding_sizes_width = MLGraphBuilder::CalculatePaddingForAutoPad(
+        auto_pad.AsEnum(), input_width, filter_width, stride_width,
+        dilation_width);
     if (!padding_sizes_width) {
       exception_state.ThrowDOMException(
           DOMExceptionCode::kDataError,
@@ -561,6 +513,47 @@
   return ml_context_;
 }
 
+// static
+absl::optional<MLGraphBuilder::PaddingSizes>
+MLGraphBuilder::CalculatePaddingForAutoPad(V8MLAutoPad::Enum auto_pad,
+                                           const uint32_t input_size,
+                                           const uint32_t filter_size,
+                                           const uint32_t stride,
+                                           const uint32_t dilation) {
+  auto checked_output_size =
+      (base::MakeCheckedNum<uint32_t>(input_size) + stride - 1) / stride;
+  auto checked_dilated_filter_size =
+      (base::MakeCheckedNum<uint32_t>(filter_size) - 1) * dilation + 1;
+  auto checked_needed_input_size =
+      (checked_output_size - 1) * stride + checked_dilated_filter_size;
+  if (!checked_needed_input_size.IsValid()) {
+    return absl::nullopt;
+  }
+  auto checked_total_padding =
+      checked_needed_input_size.ValueOrDie() > input_size
+          ? checked_needed_input_size - input_size
+          : base::MakeCheckedNum<uint32_t>(0);
+  base::CheckedNumeric<uint32_t> checked_padding_begin, checked_padding_end;
+  switch (auto_pad) {
+    case V8MLAutoPad::Enum::kSameUpper:
+      checked_padding_begin = checked_total_padding / 2;
+      checked_padding_end = (checked_total_padding + 1) / 2;
+      break;
+    case V8MLAutoPad::Enum::kSameLower:
+      checked_padding_begin = (checked_total_padding + 1) / 2;
+      checked_padding_end = checked_total_padding / 2;
+      break;
+    default:
+      NOTREACHED();
+  }
+  uint32_t padding_begin, padding_end;
+  if (!checked_padding_begin.AssignIfValid(&padding_begin) ||
+      !checked_padding_end.AssignIfValid(&padding_end)) {
+    return absl::nullopt;
+  }
+  return PaddingSizes({.begin = padding_begin, .end = padding_end});
+}
+
 MLOperand* MLGraphBuilder::input(String name,
                                  const MLOperandDescriptor* desc,
                                  ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h
index b283db8..9e4aa6d1 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h
@@ -5,7 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ML_WEBNN_ML_GRAPH_BUILDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ML_WEBNN_ML_GRAPH_BUILDER_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ml_auto_pad.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_operator.h"
@@ -48,6 +50,22 @@
 
   MLContext* GetContext() const;
 
+  struct PaddingSizes {
+    uint32_t begin;
+    uint32_t end;
+  };
+
+  // Calculate the effective padding based on WebNN auto padding rules.
+  //
+  // TODO(crbug.com/1273291): Add the link to WebNN spec's algorithm once it is
+  // defined, tracked by: https://github.com/webmachinelearning/webnn/issues/326
+  static absl::optional<PaddingSizes> CalculatePaddingForAutoPad(
+      V8MLAutoPad::Enum auto_pad,
+      const uint32_t input_size,
+      const uint32_t filter_size,
+      const uint32_t stride,
+      const uint32_t dilation);
+
   // ml_graph_builder.idl
   MLOperand* input(String name,
                    const MLOperandDescriptor* desc,
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.cc
index 6a82705..0f41904 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.cc
@@ -105,17 +105,20 @@
   return buffer_view;
 }
 
-MLOperand* BuildConstant(V8TestingScope& scope,
-                         MLGraphBuilder* builder,
-                         const Vector<uint32_t>& dimensions,
-                         V8MLOperandType::Enum type) {
+MLOperand* BuildConstant(
+    V8TestingScope& scope,
+    MLGraphBuilder* builder,
+    const Vector<uint32_t>& dimensions,
+    V8MLOperandType::Enum type,
+    absl::optional<NotShared<DOMArrayBufferView>> user_buffer_view) {
   auto* desc = MLOperandDescriptor::Create();
   desc->setDimensions(dimensions);
   desc->setType(type);
   size_t size = std::accumulate(dimensions.begin(), dimensions.end(), size_t(1),
                                 std::multiplies<uint32_t>());
   NotShared<DOMArrayBufferView> buffer_view =
-      CreateDOMArrayBufferView(size, type);
+      user_buffer_view ? std::move(user_buffer_view.value())
+                       : CreateDOMArrayBufferView(size, type);
   auto* constant =
       builder->constant(desc, buffer_view, scope.GetExceptionState());
   EXPECT_NE(constant, nullptr);
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.h b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.h
index 357bb72..b19fdc76 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ML_WEBNN_ML_GRAPH_BUILDER_TEST_H_
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_clamp_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_context_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_conv_2d_options.h"
@@ -40,10 +41,12 @@
     size_t size,
     V8MLOperandType::Enum type);
 
-MLOperand* BuildConstant(V8TestingScope& scope,
-                         MLGraphBuilder* builder,
-                         const Vector<uint32_t>& dimensions,
-                         V8MLOperandType::Enum type);
+MLOperand* BuildConstant(
+    V8TestingScope& scope,
+    MLGraphBuilder* builder,
+    const Vector<uint32_t>& dimensions,
+    V8MLOperandType::Enum type,
+    absl::optional<NotShared<DOMArrayBufferView>> buffer_view = absl::nullopt);
 
 NotShared<DOMArrayBufferView> CreateArrayBufferViewForOperand(
     const MLOperand* operand);
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc
index 7ee0482..d8735d08 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc
@@ -15,6 +15,7 @@
 #include "base/trace_event/typed_macros.h"
 #include "build/buildflag.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ml_conv_2d_options.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/modules/ml/ml.h"
 #include "third_party/blink/renderer/modules/ml/ml_context.h"
@@ -342,6 +343,195 @@
   return operand_value_id_map.at(output);
 }
 
+struct XnnOutputRange {
+  float min;
+  float max;
+};
+
+// Helper to get XNNPACK Node output value range for WebNN activation operators.
+XnnOutputRange GetXnnOutputRangeForActivation(const MLOperator* ml_operator) {
+  DCHECK(ml_operator);
+  XnnOutputRange output_range;
+  switch (ml_operator->Kind()) {
+    // TODO(crbug.com/1273291): Support clamp.
+    case MLOperator::OperatorKind::kRelu:
+      // Set the minimum value to 0 according to the rectified linear function,
+      // y = max(0, x).
+      output_range.min = 0.0f;
+      output_range.max = +std::numeric_limits<float>::infinity();
+      break;
+    default:
+      // Only clamp and relu are supported.
+      NOTREACHED();
+  }
+  return output_range;
+}
+
+xnn_status DefineXnnNodeForConv2d(xnn_subgraph_t subgraph,
+                                  const MLOperator* conv2d,
+                                  const OperandValueIdMap& operand_value_id_map,
+                                  String& error_message) {
+  const uint32_t input_id =
+      GetOperatorInputValueId(conv2d, operand_value_id_map, 0);
+  const uint32_t filter_id =
+      GetOperatorInputValueId(conv2d, operand_value_id_map, 1);
+  // If there is no bias operand, set the XNNPACK Value ID of bias tensor to
+  // XNN_INVALID_VALUE_ID.
+  const uint32_t bias_id =
+      conv2d->Inputs().size() == 3
+          ? GetOperatorInputValueId(conv2d, operand_value_id_map, 2)
+          : XNN_INVALID_VALUE_ID;
+  const uint32_t output_id =
+      GetOperatorOutputValueId(conv2d, operand_value_id_map);
+
+  const MLConv2dOptions* options =
+      static_cast<const MLConv2dOptions*>(conv2d->Options());
+
+  // Set strides of XNNPACK conv2d, default to 1.
+  const Vector<int32_t> default_strides({1, 1});
+  const uint32_t stride_height =
+      base::checked_cast<uint32_t>(options->getStridesOr(default_strides)[0]);
+  const uint32_t stride_width =
+      base::checked_cast<uint32_t>(options->getStridesOr(default_strides)[1]);
+
+  // Set dilations of XNNPACK conv2d, default to 1.
+  const Vector<int32_t> default_dilations({1, 1});
+  const uint32_t dilation_height = base::checked_cast<uint32_t>(
+      options->getDilationsOr(default_dilations)[0]);
+  const uint32_t dilation_width = base::checked_cast<uint32_t>(
+      options->getDilationsOr(default_dilations)[1]);
+
+  // Set input and filter sizes of XNNPACK conv2d.
+  uint32_t input_height, input_width;
+  uint32_t filter_height, filter_width;
+  uint32_t input_channels, output_channels;
+  const uint32_t groups = base::checked_cast<uint32_t>(options->groups());
+  bool depthwise = false;
+  if (options->inputLayout().AsEnum() == V8MLInputOperandLayout::Enum::kNhwc) {
+    const auto* input = conv2d->Inputs()[0].Get();
+    DCHECK(input);
+    input_height = input->Dimensions()[1];
+    input_width = input->Dimensions()[2];
+    input_channels = input->Dimensions()[3];
+    const auto* output = conv2d->Outputs()[0].Get();
+    DCHECK(output);
+    output_channels = output->Dimensions()[3];
+
+    // According to WebNN conv2d spec:
+    // https://www.w3.org/TR/webnn/#api-mlgraphbuilder-conv2d, A depthwise
+    // conv2d operation is a variant of grouped convolution where the
+    // options.groups == input_channels == output_channels.
+    depthwise =
+        (groups == input_channels && groups == output_channels && groups != 1);
+    if (!depthwise) {
+      // For regular conv2d, XNNPACK expects weights layout in ohwi that is
+      // [groups * group_output_channels, kernel_height, kernel_width,
+      //  group_input_channels].
+      //
+      // TODO(crbug.com/1273291): support other layouts by transposing the
+      // filter operand.
+      if (options->filterLayout().AsEnum() !=
+          V8MLConv2dFilterOperandLayout::Enum::kOhwi) {
+        error_message = String::Format("The filter layout %s is not supported.",
+                                       options->filterLayout().AsCStr());
+        return xnn_status_unsupported_parameter;
+      }
+    } else {
+      // For depthwise conv2d, XNNPACK expects weights layout in ihwo that is
+      // [1, kernel_height, kernel_width, input_channels * depth_multiplier].
+      //
+      // TODO(crbug.com/1273291): support other layouts by transposing the
+      // filter operand.
+      if (options->filterLayout().AsEnum() !=
+          V8MLConv2dFilterOperandLayout::Enum::kIhwo) {
+        error_message = String::Format("The filter layout %s is not supported.",
+                                       options->filterLayout().AsCStr());
+        return xnn_status_unsupported_parameter;
+      }
+    }
+    const auto* filter = conv2d->Inputs()[1].Get();
+    DCHECK(filter);
+    filter_height = filter->Dimensions()[1];
+    filter_width = filter->Dimensions()[2];
+  } else {
+    // TODO(crbug.com/1273291): support other layouts by transposing the input
+    // operand.
+    error_message = String::Format("The input layout %s is not supported.",
+                                   options->inputLayout().AsCStr());
+    return xnn_status_unsupported_parameter;
+  }
+
+  // Set or calculate padding sizes of XNNPACK conv2d.
+  uint32_t pad_top, pad_bottom, pad_left, pad_right;
+  if (options->autoPad().AsEnum() == V8MLAutoPad::Enum::kExplicit) {
+    // WebNN padding sizes are in [beginning_height, ending_height,
+    // beginning_width, ending_width], default to 0.
+    const Vector<int32_t> default_pads({0, 0, 0, 0});
+    pad_top =
+        base::checked_cast<uint32_t>(options->getPaddingOr(default_pads)[0]);
+    pad_bottom =
+        base::checked_cast<uint32_t>(options->getPaddingOr(default_pads)[1]);
+    pad_left =
+        base::checked_cast<uint32_t>(options->getPaddingOr(default_pads)[2]);
+    pad_right =
+        base::checked_cast<uint32_t>(options->getPaddingOr(default_pads)[3]);
+  } else {
+    auto padding_sizes_height = MLGraphBuilder::CalculatePaddingForAutoPad(
+        options->autoPad().AsEnum(), input_height, filter_height, stride_height,
+        dilation_height);
+    DCHECK(padding_sizes_height);
+    pad_top = padding_sizes_height.value().begin;
+    pad_bottom = padding_sizes_height.value().end;
+    auto padding_sizes_width = MLGraphBuilder::CalculatePaddingForAutoPad(
+        options->autoPad().AsEnum(), input_width, filter_width, stride_width,
+        dilation_width);
+    pad_left = padding_sizes_width.value().begin;
+    pad_right = padding_sizes_width.value().end;
+  }
+
+  // Set the minimum and maximum output values for XNNPACK conv2d based on the
+  // fused activation function. If no fused activation function is set, there
+  // are no limits for output values.
+  XnnOutputRange output_range{.min = -std::numeric_limits<float>::infinity(),
+                              .max = +std::numeric_limits<float>::infinity()};
+  if (options->hasActivation()) {
+    switch (options->activation()->Kind()) {
+      case MLOperator::OperatorKind::kRelu:
+        output_range = GetXnnOutputRangeForActivation(options->activation());
+        break;
+      default:
+        error_message =
+            "The fused operator (" +
+            MLOperator::OperatorKindToString(options->activation()->Kind()) +
+            ") is not supported by conv2d.";
+        return xnn_status_unsupported_parameter;
+    }
+  }
+
+  // Set group input and output channels of XNNPACK conv2d.
+  const size_t group_input_channels = input_channels / groups;
+  const size_t group_output_channels = output_channels / groups;
+
+  // Define XNNPACK conv2d or depthwise conv2d Node for the Subgraph object.
+  const uint32_t flags = 0;
+  if (depthwise) {
+    const uint32_t depth_multiplier = 1;
+    XNN_CHECK_STATUS_AND_SET_ERROR_MESSAGE(xnn_define_depthwise_convolution_2d(
+        subgraph, pad_top, pad_right, pad_bottom, pad_left, filter_height,
+        filter_width, stride_height, stride_width, dilation_height,
+        dilation_width, depth_multiplier, input_channels, output_range.min,
+        output_range.max, input_id, filter_id, bias_id, output_id, flags));
+  } else {
+    XNN_CHECK_STATUS_AND_SET_ERROR_MESSAGE(xnn_define_convolution_2d(
+        subgraph, pad_top, pad_right, pad_bottom, pad_left, filter_height,
+        filter_width, stride_height, stride_width, dilation_height,
+        dilation_width, groups, group_input_channels, group_output_channels,
+        output_range.min, output_range.max, input_id, filter_id, bias_id,
+        output_id, flags));
+  }
+  return xnn_status_success;
+}
+
 xnn_status DefineXnnNodeForElementWiseBinary(
     xnn_subgraph_t subgraph,
     const MLOperator* binary,
@@ -400,11 +590,11 @@
   const uint32_t input_id = GetOperatorInputValueId(relu, operand_value_id_map);
   const uint32_t output_id =
       GetOperatorOutputValueId(relu, operand_value_id_map);
-  const float output_min = 0.0f;
-  const float output_max = std::numeric_limits<float>::infinity();
+  const auto output_range = GetXnnOutputRangeForActivation(relu);
   const uint32_t flags = 0;
-  XNN_CHECK_STATUS_AND_SET_ERROR_MESSAGE(xnn_define_clamp(
-      subgraph, output_min, output_max, input_id, output_id, flags));
+  XNN_CHECK_STATUS_AND_SET_ERROR_MESSAGE(
+      xnn_define_clamp(subgraph, output_range.min, output_range.max, input_id,
+                       output_id, flags));
   return xnn_status_success;
 }
 
@@ -418,6 +608,11 @@
                          const OperandValueIdMap& operand_value_id_map,
                          String& error_message) {
   switch (ml_operator->Kind()) {
+    case MLOperator::OperatorKind::kConv2d:
+      XNN_CHECK_STATUS(DefineXnnNodeForConv2d(
+          subgraph, ml_operator, operand_value_id_map, error_message));
+      break;
+    // Define XNNPACK Node for element-wise binary operators.
     case MLOperator::OperatorKind::kAdd:
     case MLOperator::OperatorKind::kSub:
     case MLOperator::OperatorKind::kMul:
@@ -428,11 +623,10 @@
           subgraph, ml_operator, operand_value_id_map, error_message));
       break;
     }
-    case MLOperator::OperatorKind::kRelu: {
+    case MLOperator::OperatorKind::kRelu:
       XNN_CHECK_STATUS(DefineXnnNodeForRelu(
           subgraph, ml_operator, operand_value_id_map, error_message));
       break;
-    }
     default: {
       error_message = "The operator (" +
                       MLOperator::OperatorKindToString(ml_operator->Kind()) +
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack_test.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack_test.cc
index ea9994d..819b10cea 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack_test.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack_test.cc
@@ -4,12 +4,15 @@
 
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.h"
 
+#include <numeric>
+
 #include "base/system/sys_info.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ml_conv_2d_options.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/ml/ml.h"
@@ -776,6 +779,147 @@
   }
 }
 
+template <typename T>
+MLOperand* BuildConstant(V8TestingScope& scope,
+                         MLGraphBuilder* builder,
+                         const Vector<uint32_t>& dimensions,
+                         V8MLOperandType::Enum type,
+                         const Vector<T>& values) {
+  size_t buffer_size = std::accumulate(dimensions.begin(), dimensions.end(),
+                                       size_t(1), std::multiplies<uint32_t>());
+  auto buffer = CreateDOMArrayBufferView(buffer_size, type);
+  DCHECK_EQ(buffer->byteLength(), values.size() * sizeof(T));
+  memcpy(buffer->BaseAddress(), values.data(), buffer->byteLength());
+  return BuildConstant(scope, builder, dimensions, type, buffer);
+}
+
+template <typename T>
+struct Conv2dTester {
+  MLGraphXnnpackTest* helper;
+  OperandInfo<T> input;
+  OperandInfo<T> filter;
+  absl::optional<OperandInfo<T>> bias = absl::nullopt;
+  Vector<T> expected;
+
+  void Test(V8TestingScope& scope,
+            MLGraphBuilder* builder,
+            MLConv2dOptions* options = MLConv2dOptions::Create()) {
+    // Build the graph.
+    auto* input_operand =
+        BuildInput(scope, builder, "input", input.dimensions, input.type);
+    auto* filter_operand = BuildConstant(scope, builder, filter.dimensions,
+                                         filter.type, filter.values);
+    if (bias) {
+      options->setBias(BuildConstant(scope, builder, bias.value().dimensions,
+                                     bias.value().type, bias.value().values));
+    }
+    auto* output_operand =
+        BuildConv2d(scope, builder, input_operand, filter_operand, options);
+    auto [graph, build_exception] =
+        helper->BuildGraph(scope, builder, {{"output", output_operand}});
+    EXPECT_NE(graph, nullptr);
+
+    // Compute the graph.
+    auto input_buffer =
+        CreateArrayBufferViewForOperand(input_operand, input.values);
+    auto output_buffer = CreateArrayBufferViewForOperand(output_operand);
+    auto* compute_exception = helper->ComputeGraph(
+        scope, graph, {{"input", input_buffer}}, {{"output", output_buffer}});
+    EXPECT_EQ(compute_exception, nullptr);
+    Vector<float> results = GetArrayBufferViewValues<T>(output_buffer);
+    EXPECT_EQ(results, expected);
+  }
+};
+
+TEST_P(MLGraphXnnpackTest, Conv2dTest) {
+  V8TestingScope scope;
+  auto* builder = CreateMLGraphBuilder(scope);
+  {
+    // Test conv2d operator for nhwc input layout and ohwi filter layout.
+    auto* options = MLConv2dOptions::Create();
+    options->setInputLayout(V8MLInputOperandLayout::Enum::kNhwc);
+    options->setFilterLayout(V8MLConv2dFilterOperandLayout::Enum::kOhwi);
+    Conv2dTester<float>{
+        .input = {.type = V8MLOperandType::Enum::kFloat32,
+                  .dimensions = {1, 2, 3, 3},
+                  .values = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
+                             11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0}},
+        .filter = {.type = V8MLOperandType::Enum::kFloat32,
+                   .dimensions = {3, 1, 1, 3},
+                   .values = {1.0, 4.0, 7.0, 2.0, 5.0, 8.0, 3.0, 6.0, 9.0}},
+        .expected = {30.0, 36.0, 42.0, 66.0, 81.0, 96.0, 102.0, 126.0, 150.0,
+                     138.0, 171.0, 204.0, 174.0, 216.0, 258.0, 210.0, 261.0,
+                     312.0}}
+        .Test(scope, builder, options);
+  }
+  {
+    // Test fused conv2d operator for nhwc input layout and ohwi filter layout,
+    // fusing with bias operand and relu activation.
+    auto* options = MLConv2dOptions::Create();
+    options->setInputLayout(V8MLInputOperandLayout::Enum::kNhwc);
+    options->setFilterLayout(V8MLConv2dFilterOperandLayout::Enum::kOhwi);
+    options->setActivation(builder->relu(scope.GetExceptionState()));
+    Conv2dTester<float>{
+        .input = {.type = V8MLOperandType::Enum::kFloat32,
+                  .dimensions = {1, 2, 3, 3},
+                  .values = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
+                             11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0}},
+        .filter = {.type = V8MLOperandType::Enum::kFloat32,
+                   .dimensions = {3, 1, 1, 3},
+                   .values = {1.0, 4.0, 7.0, 2.0, 5.0, 8.0, 3.0, 6.0, 9.0}},
+        .bias = OperandInfo<float>{.type = V8MLOperandType::Enum::kFloat32,
+                                   .dimensions = {3},
+                                   .values = {-6000.0, -7000.0, 8000.0}},
+        .expected = {0.0, 0.0, 8042.0, 0.0, 0.0, 8096.0, 0.0, 0.0, 8150.0, 0.0,
+                     0.0, 8204.0, 0.0, 0.0, 8258.0, 0.0, 0.0, 8312.0}}
+        .Test(scope, builder, options);
+  }
+  {
+    // Test depthwise conv2d operator by setting groups to input channels,
+    // nhwc input layout, ihwo filter layout.
+    auto* options = MLConv2dOptions::Create();
+    options->setInputLayout(V8MLInputOperandLayout::Enum::kNhwc);
+    options->setFilterLayout(V8MLConv2dFilterOperandLayout::Enum::kIhwo);
+    options->setGroups(4);
+    Conv2dTester<float>{
+        .input = {.type = V8MLOperandType::Enum::kFloat32,
+                  .dimensions = {1, 2, 2, 4},
+                  .values = {10.0, 21.0, 10.0, 0.0, 10.0, 22.0, 20.0, 0.0, 10.0,
+                             23.0, 30.0, 0.0, 10.0, 24.0, 40.0, 0.0}},
+        .filter = {.type = V8MLOperandType::Enum::kFloat32,
+                   .dimensions = {1, 2, 2, 4},
+                   .values = {0.25, 0.0, 10.0, 50.0, 0.25, 1.0, 20.0, 50.0,
+                              0.25, 0.0, 30.0, 50.0, 0.25, 1.0, 40.0, 50.0}},
+        .expected = {10.0, 46.0, 3000.0, 0.0}}
+        .Test(scope, builder, options);
+  }
+  {
+    // Test fused depthwise conv2d operator by setting groups to input channels,
+    // nhwc input layout, ihwo filter layout, fusing with bias operand and relu
+    // activation.
+    auto* options = MLConv2dOptions::Create();
+    options->setInputLayout(V8MLInputOperandLayout::Enum::kNhwc);
+    options->setFilterLayout(V8MLConv2dFilterOperandLayout::Enum::kIhwo);
+    options->setGroups(4);
+    options->setActivation(builder->relu(scope.GetExceptionState()));
+    Conv2dTester<float>{
+        .input = {.type = V8MLOperandType::Enum::kFloat32,
+                  .dimensions = {1, 2, 2, 4},
+                  .values = {10.0, 21.0, 10.0, 0.0, 10.0, 22.0, 20.0, 0.0, 10.0,
+                             23.0, 30.0, 0.0, 10.0, 24.0, 40.0, 0.0}},
+        .filter = {.type = V8MLOperandType::Enum::kFloat32,
+                   .dimensions = {1, 2, 2, 4},
+                   .values = {0.25, 0.0, 10.0, 50.0, 0.25, 1.0, 20.0, 50.0,
+                              0.25, 0.0, 30.0, 50.0, 0.25, 1.0, 40.0, 50.0}},
+        .bias =
+            OperandInfo<float>{.type = V8MLOperandType::Enum::kFloat32,
+                               .dimensions = {4},
+                               .values = {-6000.0, -7000.0, 8000.0, 9000.0}},
+        .expected = {0.0, 0.0, 11000.0, 9000.0}}
+        .Test(scope, builder, options);
+  }
+}
+
 // TODO(crbug.com/1273291): Test the async execution mode once the
 // MLGraphXnnpack implements it.
 INSTANTIATE_TEST_SUITE_P(All,
diff --git a/third_party/blink/renderer/modules/peerconnection/identifiability_metrics.cc b/third_party/blink/renderer/modules/peerconnection/identifiability_metrics.cc
index dcebb75..687b413 100644
--- a/third_party/blink/renderer/modules/peerconnection/identifiability_metrics.cc
+++ b/third_party/blink/renderer/modules/peerconnection/identifiability_metrics.cc
@@ -39,13 +39,6 @@
       } else {
         builder.AddToken(IdentifiableToken());
       }
-      if (codec->hasScalabilityModes()) {
-        for (const auto& mode : codec->scalabilityModes()) {
-          builder.AddToken(IdentifiabilitySensitiveStringToken(mode));
-        }
-      } else {
-        builder.AddToken(IdentifiableToken());
-      }
     }
   } else {
     builder.AddToken(IdentifiableToken());
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_codec_capability.idl b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_codec_capability.idl
index 93c6c8a..806d24d 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_codec_capability.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_codec_capability.idl
@@ -8,5 +8,4 @@
     required unsigned long  clockRate;
              unsigned short channels;
              DOMString      sdpFmtpLine;
-    [RuntimeEnabled=RTCSvcScalabilityMode] sequence<DOMString> scalabilityModes;
 };
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
index 65cc6af..30624c8 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -805,12 +805,6 @@
       }
       codec->setSdpFmtpLine(sdp_fmtp_line.c_str());
     }
-    Vector<String> scalability_modes;
-    for (const auto& mode : rtc_codec.scalability_modes) {
-      scalability_modes.push_back(WTF::String::FromUTF8(
-          std::string(webrtc::ScalabilityModeToString(mode))));
-    }
-    codec->setScalabilityModes(scalability_modes);
     codecs.push_back(codec);
   }
   capabilities->setCodecs(codecs);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc
index 474fe0e..df95fe1 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu.cc
@@ -140,8 +140,12 @@
   // This is necessary because we will free the shmem backings, and some
   // short amount of JS can still execute after the ContextDestroyed event
   // is received.
-  for (GPUBuffer* buffer : mappable_buffers_) {
-    buffer->DetachMappedArrayBuffers(ThreadState::Current()->GetIsolate());
+  if (!mappable_buffers_.empty()) {
+    v8::Isolate* isolate = ThreadState::Current()->GetIsolate();
+    v8::HandleScope scope(isolate);
+    for (GPUBuffer* buffer : mappable_buffers_) {
+      buffer->DetachMappedArrayBuffers(isolate);
+    }
   }
   // GPUBuffer::~GPUBuffer and GPUBuffer::destroy will remove WGPUBuffers from
   // |mappable_buffer_handles_|.
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc b/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
index 0f3bae99..ea14013 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
@@ -68,42 +68,22 @@
 // becomes complex to handle destruction when the last reference to
 // the WGPUBuffer may be held either by a GC object, or a non-GC object.
 class GPUMappedDOMArrayBuffer : public DOMArrayBuffer {
+  static constexpr char kWebGPUBufferMappingDetachKey[] = "WebGPUBufferMapping";
+
  public:
-  static GPUMappedDOMArrayBuffer* Create(GPUBuffer* owner,
+  static GPUMappedDOMArrayBuffer* Create(v8::Isolate* isolate,
+                                         GPUBuffer* owner,
                                          ArrayBufferContents contents) {
-    return MakeGarbageCollected<GPUMappedDOMArrayBuffer>(owner,
-                                                         std::move(contents));
+    auto* mapped_array_buffer = MakeGarbageCollected<GPUMappedDOMArrayBuffer>(
+        owner, std::move(contents));
+    mapped_array_buffer->SetDetachKey(isolate, kWebGPUBufferMappingDetachKey);
+    return mapped_array_buffer;
   }
 
   GPUMappedDOMArrayBuffer(GPUBuffer* owner, ArrayBufferContents contents)
       : DOMArrayBuffer(std::move(contents)), owner_(owner) {}
   ~GPUMappedDOMArrayBuffer() override = default;
 
-  // Override Transfer such that a copy of the contents is always made. The
-  // backing store will still be detached for this ArrayBuffer, but the
-  // result will be a copy of the contents, not a reference to
-  // the same backing store. This is required by the WebGPU specification so
-  // that the mapped backing store may not be shared cross-thread.
-  bool Transfer(v8::Isolate* isolate,
-                v8::Local<v8::Value> detach_key,
-                ArrayBufferContents& result,
-                ExceptionState& exception_state) override {
-    // Transfer into |contents| which will detach |this| and all views of
-    // |this|.
-    ArrayBufferContents contents;
-    if (!DOMArrayBuffer::Transfer(isolate, detach_key, contents,
-                                  exception_state)) {
-      return false;
-    }
-
-    // Copy the contents into the result.
-    contents.CopyTo(result);
-    owner_->device()->AddConsoleWarning(
-        "ArrayBuffer backed by mapped GPUBuffer was copied and detached, not "
-        "transferred.");
-    return true;
-  }
-
   void DetachContents(v8::Isolate* isolate) {
     if (IsDetached()) {
       return;
@@ -112,8 +92,9 @@
     // Detach the array buffer by transferring the contents out and dropping
     // them.
     ArrayBufferContents contents;
-    bool result = DOMArrayBuffer::Transfer(isolate, v8::Local<v8::Value>(),
-                                           contents, exception_state);
+    bool result = DOMArrayBuffer::Transfer(
+        isolate, V8AtomicString(isolate, kWebGPUBufferMappingDetachKey),
+        contents, exception_state);
     // TODO(crbug.com/1326210): Temporary CHECK to prevent aliased array
     // buffers.
     CHECK(result && IsDetached());
@@ -207,27 +188,26 @@
   return MapAsyncImpl(script_state, mode, offset, size, exception_state);
 }
 
-DOMArrayBuffer* GPUBuffer::getMappedRange(ExecutionContext* execution_context,
+DOMArrayBuffer* GPUBuffer::getMappedRange(v8::Isolate* isolate,
                                           uint64_t offset,
                                           ExceptionState& exception_state) {
-  return GetMappedRangeImpl(offset, absl::nullopt, execution_context,
-                            exception_state);
+  return GetMappedRangeImpl(isolate, offset, absl::nullopt, exception_state);
 }
 
-DOMArrayBuffer* GPUBuffer::getMappedRange(ExecutionContext* execution_context,
+DOMArrayBuffer* GPUBuffer::getMappedRange(v8::Isolate* isolate,
                                           uint64_t offset,
                                           uint64_t size,
                                           ExceptionState& exception_state) {
-  return GetMappedRangeImpl(offset, size, execution_context, exception_state);
+  return GetMappedRangeImpl(isolate, offset, size, exception_state);
 }
 
-void GPUBuffer::unmap(ScriptState* script_state) {
-  ResetMappingState(script_state->GetIsolate());
+void GPUBuffer::unmap(v8::Isolate* isolate) {
+  ResetMappingState(isolate);
   GetProcs().bufferUnmap(GetHandle());
 }
 
-void GPUBuffer::destroy(ScriptState* script_state) {
-  ResetMappingState(script_state->GetIsolate());
+void GPUBuffer::destroy(v8::Isolate* isolate) {
+  ResetMappingState(isolate);
   GetProcs().bufferDestroy(GetHandle());
   // Destroyed, so it can never be mapped again. Stop tracking.
   device_->adapter()->gpu()->UntrackMappableBuffer(this);
@@ -291,11 +271,10 @@
   return promise;
 }
 
-DOMArrayBuffer* GPUBuffer::GetMappedRangeImpl(
-    uint64_t offset,
-    absl::optional<uint64_t> size,
-    ExecutionContext* execution_context,
-    ExceptionState& exception_state) {
+DOMArrayBuffer* GPUBuffer::GetMappedRangeImpl(v8::Isolate* isolate,
+                                              uint64_t offset,
+                                              absl::optional<uint64_t> size,
+                                              ExceptionState& exception_state) {
   // Compute the defaulted size which is "until the end of the buffer" or 0 if
   // offset is past the end of the buffer.
   uint64_t size_defaulted = 0;
@@ -374,8 +353,7 @@
       const_cast<uint8_t*>(static_cast<const uint8_t*>(map_data_const));
 
   mapped_ranges_.push_back(std::make_pair(range_offset, range_end));
-  return CreateArrayBufferForMappedData(map_data, range_size,
-                                        execution_context);
+  return CreateArrayBufferForMappedData(isolate, map_data, range_size);
 }
 
 void GPUBuffer::OnMapAsyncCallback(ScriptPromiseResolver* resolver,
@@ -411,18 +389,16 @@
   }
 }
 
-DOMArrayBuffer* GPUBuffer::CreateArrayBufferForMappedData(
-    void* data,
-    size_t data_length,
-    ExecutionContext* execution_context) {
+DOMArrayBuffer* GPUBuffer::CreateArrayBufferForMappedData(v8::Isolate* isolate,
+                                                          void* data,
+                                                          size_t data_length) {
   DCHECK(data);
   DCHECK_LE(static_cast<uint64_t>(data_length), v8::TypedArray::kMaxLength);
 
   ArrayBufferContents contents(v8::ArrayBuffer::NewBackingStore(
       data, data_length, v8::BackingStore::EmptyDeleter, nullptr));
   GPUMappedDOMArrayBuffer* array_buffer =
-      GPUMappedDOMArrayBuffer::Create(this, contents);
-
+      GPUMappedDOMArrayBuffer::Create(isolate, this, contents);
   mapped_array_buffers_.push_back(array_buffer);
   return array_buffer;
 }
@@ -436,8 +412,6 @@
   for (Member<GPUMappedDOMArrayBuffer>& mapped_array_buffer :
        mapped_array_buffers_) {
     GPUMappedDOMArrayBuffer* array_buffer = mapped_array_buffer.Release();
-    DCHECK(array_buffer->IsDetachable(isolate));
-
     array_buffer->DetachContents(isolate);
   }
   mapped_array_buffers_.clear();
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_buffer.h b/third_party/blink/renderer/modules/webgpu/gpu_buffer.h
index 9200472..e27f42b 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_buffer.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_buffer.h
@@ -17,9 +17,9 @@
 class DOMArrayBuffer;
 class GPUBufferDescriptor;
 class GPUMappedDOMArrayBuffer;
-class ExecutionContext;
 struct BoxedMappableWGPUBufferHandles;
 class ScriptPromiseResolver;
+class ScriptState;
 
 class GPUBuffer : public DawnObject<WGPUBuffer> {
   DEFINE_WRAPPERTYPEINFO();
@@ -46,15 +46,15 @@
                          uint64_t offset,
                          uint64_t size,
                          ExceptionState& exception_state);
-  DOMArrayBuffer* getMappedRange(ExecutionContext* execution_context,
+  DOMArrayBuffer* getMappedRange(v8::Isolate* isolate,
                                  uint64_t offset,
                                  ExceptionState& exception_state);
-  DOMArrayBuffer* getMappedRange(ExecutionContext* execution_context,
+  DOMArrayBuffer* getMappedRange(v8::Isolate* isolate,
                                  uint64_t offset,
                                  uint64_t size,
                                  ExceptionState& exception_state);
-  void unmap(ScriptState* script_state);
-  void destroy(ScriptState* script_state);
+  void unmap(v8::Isolate* isolate);
+  void destroy(v8::Isolate* isolate);
   uint64_t size() const;
   uint32_t usage() const;
   String mapState() const;
@@ -67,18 +67,17 @@
                              uint64_t offset,
                              absl::optional<uint64_t> size,
                              ExceptionState& exception_state);
-  DOMArrayBuffer* GetMappedRangeImpl(uint64_t offset,
+  DOMArrayBuffer* GetMappedRangeImpl(v8::Isolate* isolate,
+                                     uint64_t offset,
                                      absl::optional<uint64_t> size,
-                                     ExecutionContext* execution_context,
                                      ExceptionState& exception_state);
 
   void OnMapAsyncCallback(ScriptPromiseResolver* resolver,
                           WGPUBufferMapAsyncStatus status);
 
-  DOMArrayBuffer* CreateArrayBufferForMappedData(
-      void* data,
-      size_t data_length,
-      ExecutionContext* execution_context);
+  DOMArrayBuffer* CreateArrayBufferForMappedData(v8::Isolate* isolate,
+                                                 void* data,
+                                                 size_t data_length);
   void ResetMappingState(v8::Isolate* isolate);
 
   void setLabelImpl(const String& value) override {
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_buffer.idl b/third_party/blink/renderer/modules/webgpu/gpu_buffer.idl
index b64467d4..6dc73454 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_buffer.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_buffer.idl
@@ -12,11 +12,11 @@
         GPUMapModeFlags mode,
         optional GPUSize64 offset = 0,
         optional GPUSize64 size);
-    [CallWith=ExecutionContext, RaisesException] ArrayBuffer getMappedRange(
+    [CallWith=Isolate, RaisesException] ArrayBuffer getMappedRange(
         optional GPUSize64 offset = 0,
         optional GPUSize64 size);
-    [CallWith=ScriptState] void unmap();
-    [CallWith=ScriptState] void destroy();
+    [CallWith=Isolate] void unmap();
+    [CallWith=Isolate] void destroy();
 
     readonly attribute GPUSize64 size;
     readonly attribute GPUBufferUsageFlags usage;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.cc b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
index 32b92b7..77cb8b2 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
@@ -389,13 +389,13 @@
   return queue_;
 }
 
-void GPUDevice::destroy(ScriptState* script_state) {
+void GPUDevice::destroy(v8::Isolate* isolate) {
   destroyed_ = true;
   DestroyAllExternalTextures();
   // Dissociate mailboxes before destroying the device. This ensures that
   // mailbox operations which run during dissociation can succeed.
   DissociateMailboxes();
-  UnmapAllMappableBuffers(script_state);
+  UnmapAllMappableBuffers(isolate);
   GetProcs().deviceDestroy(GetHandle());
   FlushNow();
 }
@@ -642,9 +642,9 @@
   textures_with_mailbox_.clear();
 }
 
-void GPUDevice::UnmapAllMappableBuffers(ScriptState* script_state) {
+void GPUDevice::UnmapAllMappableBuffers(v8::Isolate* isolate) {
   for (GPUBuffer* buffer : mappable_buffers_) {
-    buffer->unmap(script_state);
+    buffer->unmap(isolate);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.h b/third_party/blink/renderer/modules/webgpu/gpu_device.h
index 7f6b363..cec846f1 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.h
@@ -83,7 +83,7 @@
 
   GPUQueue* queue();
 
-  void destroy(ScriptState* script_state);
+  void destroy(v8::Isolate* isolate);
 
   GPUBuffer* createBuffer(const GPUBufferDescriptor* descriptor,
                           ExceptionState& exception_state);
@@ -168,7 +168,7 @@
 
   void DestroyAllExternalTextures();
   void DissociateMailboxes();
-  void UnmapAllMappableBuffers(ScriptState* script_state);
+  void UnmapAllMappableBuffers(v8::Isolate* isolate);
 
   void OnUncapturedError(WGPUErrorType errorType, const char* message);
   void OnLogging(WGPULoggingType loggingType, const char* message);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.idl b/third_party/blink/renderer/modules/webgpu/gpu_device.idl
index f7e32f1b..13f4b7c 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.idl
@@ -15,7 +15,7 @@
 
     [SameObject] readonly attribute GPUQueue queue;
 
-    [CallWith=ScriptState] void destroy();
+    [CallWith=Isolate] void destroy();
 
     [RaisesException] GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
     [RaisesException] GPUTexture createTexture(GPUTextureDescriptor descriptor);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a97e8ef..2f26171 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -657,7 +657,11 @@
     },
     {
       name: "CSSColorTypedOM",
-      status:  "experimental",
+      status: "experimental",
+    },
+    {
+      name: "CSSDisplayAnimation",
+      status: "test"
     },
     {
       // Include custom properties in CSSComputedStyleDeclaration::item/length.
@@ -1341,12 +1345,6 @@
       status: "stable",
     },
     {
-      // Whether to re-enable async interface for FileSystemSyncAccessHandle
-      // This is used by enterprise policy during migration.
-      name: "FileSystemSyncAccessHandleAsyncInterfaceOverride",
-      public: true,
-    },
-    {
       name: "FirstPartySets",
       status: "experimental",
     },
@@ -1580,6 +1578,12 @@
       name: "InfiniteCullRect",
     },
     {
+      // If enabled, setting innerHTML will use a special fast-path parser.
+      name: "InnerHTMLParserFastpath",
+      base_feature: "InnerHTMLParserFastpath",
+      status: "experimental",
+    },
+    {
       name: "InputMultipleFieldsUI",
       // No plan to support complex UI for date/time INPUT types on Android.
       status: {"Android": "test", "default": "stable"},
diff --git a/third_party/blink/tools/blinkpy/common/config/builders.json b/third_party/blink/tools/blinkpy/common/config/builders.json
index ff6abe1..1503af5 100644
--- a/third_party/blink/tools/blinkpy/common/config/builders.json
+++ b/third_party/blink/tools/blinkpy/common/config/builders.json
@@ -124,7 +124,22 @@
         "has_webdriver_tests": true,
         "steps": {
             "blink_web_tests (with patch)": {},
-            "blink_wpt_tests (with patch)": {}
+            "blink_wpt_tests (with patch)": {},
+            "high_dpi_blink_web_tests (with patch)": {
+                "flag_specific": "highdpi"
+            },
+            "high_dpi_blink_wpt_tests (with patch)": {
+                "flag_specific": "highdpi"
+            },
+            "vulkan_swiftshader_blink_web_tests (with patch)": {
+                "flag_specific": "skia-vulkan-swiftshader"
+            },
+            "not_site_per_process_blink_web_tests (with patch)": {
+                "flag_specific": "disable-site-isolation-trials"
+            },
+            "not_site_per_process_blink_wpt_tests (with patch)": {
+                "flag_specific": "disable-site-isolation-trials"
+            }
         },
         "is_try_builder": true
     },
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
index e8076fd..a9d86d8 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
@@ -13,7 +13,7 @@
 import logging
 import re
 from collections import defaultdict, namedtuple
-from typing import List
+from typing import List, Optional
 
 from blinkpy.common.memoized import memoized
 from blinkpy.common.net.git_cl import GitCL
@@ -133,6 +133,19 @@
                  'This command line argument can be used to mark tests '
                  'as flaky.')
 
+    def suite_for_builder(self,
+                          builder: str,
+                          flag_specific: Optional[str] = None) -> str:
+        for step in self.host.builders.step_names_for_builder(builder):
+            if self.host.builders.flag_specific_option(builder,
+                                                       step) == flag_specific:
+                suite_match = re.match(r'(?P<suite>[\w_-]*blink_wpt_tests)',
+                                       step)
+                if suite_match:
+                    return suite_match['suite']
+        raise ValueError('"%s" flag-specific suite on "%s" not found' %
+                         (flag_specific, builder))
+
     def update_expectations_for_flag_specific(self, flag_specific):
         """Adds test expectations lines for flag specific builders.
 
@@ -147,13 +160,11 @@
         if issue_number == 'None':
             raise ScriptError('No issue on current branch.')
 
-        if flag_specific == "disable-site-isolation-trials":
-            builder_names = ["linux-rel"]
-            test_suite = "not_site_per_process_blink_wpt_tests"
-        else:
-            builder_names = self.host.builders.all_flag_specific_try_builder_names(
-                flag_specific)
-            test_suite = "blink_wpt_tests"
+        # TODO(crbug.com/1406978): Retrieve builder names from the config
+        # instead of hardcoding.
+        builder = 'linux-blink-rel'
+        builder_names = [builder]
+        test_suite = self.flag_specific_suite(builder, flag_specific)
 
         build_to_status = self.git_cl.latest_try_jobs(
             builder_names=builder_names,
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
index 9b50a52..de3f7a59 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
@@ -49,6 +49,13 @@
                 'main': 'tryserver.blink',
                 'has_webdriver_tests': True,
                 'is_try_builder': True,
+                'steps': {
+                    'blink_web_tests (with patch)': {},
+                    'blink_wpt_tests (with patch)': {},
+                    'flag_specific_blink_wpt_tests (with patch)': {
+                        'flag_specific': 'flag-specific',
+                    },
+                },
             },
             'MOCK Try Precise': {
                 'port_name': 'test-linux-precise',
@@ -105,6 +112,15 @@
 
         return host
 
+    def test_suite_for_builder(self):
+        host = self.mock_host()
+        updater = WPTExpectationsUpdater(host)
+        self.assertEqual(updater.suite_for_builder('MOCK Try Trusty'),
+                         'blink_wpt_tests')
+        self.assertEqual(
+            updater.suite_for_builder('MOCK Try Trusty', 'flag-specific'),
+            'flag_specific_blink_wpt_tests')
+
     def test_run_single_platform_failure(self):
         """Tests the main run method in a case where one test fails on one platform."""
         host = self.mock_host()
diff --git a/third_party/blink/web_tests/FlagExpectations/devtools-tab-target b/third_party/blink/web_tests/FlagExpectations/devtools-tab-target
index 91c9d1b5..456694d 100644
--- a/third_party/blink/web_tests/FlagExpectations/devtools-tab-target
+++ b/third_party/blink/web_tests/FlagExpectations/devtools-tab-target
@@ -2,3 +2,6 @@
 # tags: [ Release Debug ]
 # results: [ Timeout Crash Pass Failure Slow Skip ]
 
+virtual/portals/http/tests/devtools/portals/portals-console.js [ Skip ]
+virtual/portals/http/tests/devtools/portals/portals-elements-activate.js [ Skip ]
+
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index ca59c97..50227557 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -236,12 +236,6 @@
 # Test might be flaky on Mac12-arm64 as the test server causes network errors probably becauuse of lack of resources on the bot.
 crbug.com/1382361 [ Mac ] external/wpt/speculation-rules/prerender/csp-script-src-self.html [ Failure Pass ]
 
-# These tests should be revisited after the eagerness api for speculation rules is implemented.
-crbug.com/1374023 virtual/prefetch/external/wpt/speculation-rules/prefetch/document-rules.https.html* [ Failure ]
-crbug.com/1374023 virtual/prefetch/external/wpt/speculation-rules/prefetch/document-rules.https.html?include=invalidPredicate [ Pass ]
-crbug.com/1374023 virtual/prefetch/external/wpt/speculation-rules/prefetch/referrer-policy-from-rules.https.html?2-2 [ Failure ]
-crbug.com/1374023 virtual/prefetch/external/wpt/speculation-rules/prefetch/referrer-policy.https.html?4-last [ Failure ]
-
 # These tests are flaky with BFCache, which is enabled by default.
 crbug.com/1311546 http/tests/test-runner/back-forward.html [ Failure Pass ]
 
@@ -3579,6 +3573,11 @@
 
 # [css-animations]
 
+# CSSDisplayAnimation makes display animateable changing the expectation that it's not.
+crbug.com/1395394 external/wpt/css/css-display/animations/display-interpolation.html [ Failure ]
+crbug.com/1395394 external/wpt/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html [ Failure ]
+crbug.com/1395394 fast/css-generated-content/pseudo-animation-display.html [ Failure ]
+
 crbug.com/664450 http/tests/devtools/console/console-on-animation-worklet.js [ Failure Pass Timeout ]
 
 # [css-transitions]
@@ -6911,6 +6910,11 @@
 # Sheriff 2023-01-04
 crbug.com/1404951 external/wpt/credential-management/fedcm-network-requests.https.html [ Pass Timeout ]
 
+# TODO(crbug.com/1407201): this test fails because of fast-path parser when dchecks are enabled.
+# It fails because this test expects certain console warnings. Those warnings are duplicated because
+# parsing (which generates the warnings) happens twice, once for fast-path and once for existing path.
+crbug.com/1407201 fast/css/pseudo-in-range-invalid-value.html [ Failure Pass ]
+
 # Sheriff 2023-01-05
 # fast/dom/Element/scrollTop-scrollLeft-body.html previously also linked with crbug.com/1249176
 crbug.com/1361956 [ Mac12 ] fast/dom/Element/scrollTop-scrollLeft-body.html [ Failure Pass Timeout ]
@@ -6942,6 +6946,8 @@
 http/tests/security/cross-frame-access-protocol-explicit-domain.html [ Failure Pass ]
 http/tests/security/canvas-remote-read-remote-image-document-domain.html [ Failure Pass ]
 
-# Sheriff 2023-01-16
-crbug.com/1407922 http/tests/devtools/background-services/background-service-grid.js [ Failure Pass ]
-crbug.com/1407922 http/tests/devtools/background-services/background-services-panel.js [ Failure Pass ]
+crbug.com/1407166 http/tests/devtools/application-panel/resources-panel-iframe-idb.js [ Failure Pass ]
+crbug.com/1407166 http/tests/devtools/application-panel/resources-panel-on-navigation.js [ Failure Pass ]
+crbug.com/1407166 http/tests/devtools/application-panel/resources-panel-resource-preview.js [ Failure Pass ]
+crbug.com/1407166 http/tests/devtools/application-panel/resources-panel-selection-on-reload.js [ Failure Pass ]
+crbug.com/1407166 http/tests/devtools/application-panel/resources-panel-websql.js [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 39e782d..8dddc6b 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1541,7 +1541,7 @@
     ],
     "exclusive_tests": "ALL",
     "args": [
-      "--enable-blink-features=SpeculationRulesPrefetchProxy,SpeculationRulesFetchFromHeader,SpeculationRulesDocumentRules,NoVarySearchPrefetch",
+      "--enable-blink-features=SpeculationRulesPrefetchProxy,SpeculationRulesFetchFromHeader,SpeculationRulesDocumentRules,NoVarySearchPrefetch,SpeculationRulesEagerness",
       "--enable-features=SpeculationRulesPrefetchProxy,PrefetchUseContentRefactor",
       "--bypass-prefetch-proxy-for-host=not-web-platform.test"
     ],
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-071.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-071.html
index 371f41a2..629ebda4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-071.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-071.html
@@ -7,7 +7,6 @@
 <style>
   .multicol {
     column-count: 2;
-    column-fill: auto;
     column-gap: 0px;
   }
   #outer {
@@ -15,6 +14,7 @@
   }
   #inner {
     width: 50px;
+    column-fill: auto;
   }
   .abs {
     position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-display/animations/display-interpolation.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-display/animations/display-interpolation.tentative.html
new file mode 100644
index 0000000..b6b58ee4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-display/animations/display-interpolation.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>display interpolation</title>
+<link rel="help" href="https://www.w3.org/TR/CSS2/visuren.html#display-prop">
+<meta name="assert" content="display supports animation">
+
+<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: 'display',
+      from: 'grid',
+      to: 'flex',
+      underlying: 'block',
+      target_names: ['CSS Animations', 'Web Animations']
+    });
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/abspos-after-spanner-static-pos.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/abspos-after-spanner-static-pos.html
index d257016..0cc6b08 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-multicol/abspos-after-spanner-static-pos.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/abspos-after-spanner-static-pos.html
@@ -5,7 +5,6 @@
 <style>
   .multicol {
     columns: 2;
-    column-fill: auto;
     column-gap: 0px;
     width: 400px;
     margin-top: -60px;
diff --git a/third_party/blink/web_tests/external/wpt/event-timing/event-click-visibilitychange.html b/third_party/blink/web_tests/external/wpt/event-timing/event-click-visibilitychange.html
new file mode 100644
index 0000000..beb3ba3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/event-timing/event-click-visibilitychange.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html>
+<meta charset=utf-8 />
+<meta name="timeout" content="long">
+<title>Event Timing: eventCounts.</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src=/resources/testdriver-actions.js></script>
+<script src=/page-visibility/resources/window_state_context.js></script>
+<script src=resources/event-timing-test-utils.js></script>
+
+<body>
+  <button id='target'>Click me</button>
+
+  <script>
+    let observedEntries = [];
+    const map = new Map();
+    const events = ['pointerdown'];
+
+    promise_test(async t => {
+      assert_implements(window.PerformanceEventTiming, 'Event Timing is not supported.');
+
+      const { minimize, restore } = window_state_context(t);
+      const button = document.getElementById('target');
+
+      const callback = (entryList) => { observedEntries = observedEntries.concat(entryList.getEntries().filter(filterAndAddToMap(events, map))); };
+      const readyToResolve = () => { return observedEntries.length >= 1; };
+      const observerPromise = createPerformanceObserverPromise(['event'], callback, readyToResolve);
+
+      const tapEventPromise = new Promise(resolve => {
+        button.addEventListener('pointerdown', async (event) => {
+          document.body.innerText += "Adding content to force rendering";
+
+          // await here will yield to event loop, and end event processing time,
+          // which will allow rendering to continue.
+          // The visibility change may happen before rendering has a chance
+          // but it is not guarenteed which will happen first.
+          await minimize();
+          const timeAfterVisibilityFalse = performance.now();
+
+          await restore();
+          const timeAfterVisibilityTrue = performance.now();
+
+          resolve({ timeAfterVisibilityFalse, timeAfterVisibilityTrue });
+        });
+      });
+
+      // A buffered visibility-state PerformanceEntry would have made this test
+      // cleaner, due to the variability of ordering of events, but it is not
+      // yet available.
+      const visibilityEventPromise = new Promise(resolve => {
+        document.addEventListener('visibilitychange', (event) => {
+          if (document.visibilityState !== 'visible') {
+            resolve(performance.now());
+          }
+        });
+      });
+
+      const timeBeforeTap = performance.now();
+      await interactAndObserve('tap', button, observerPromise);
+
+      // The order that these events fire is non-deterministic, but we can await
+      // the result of the promise in any order.
+      const { timeAfterVisibilityFalse, timeAfterVisibilityTrue } = await tapEventPromise;
+      const timeOfVisibilityFalse = await visibilityEventPromise;
+
+      assert_equals(observedEntries.length, 1, "Pointerdown was measured");
+      const entry = observedEntries[0];
+
+      assert_not_equals(timeBeforeTap, undefined);
+      assert_not_equals(timeAfterVisibilityFalse, undefined);
+      assert_not_equals(timeAfterVisibilityTrue, undefined);
+      assert_not_equals(timeOfVisibilityFalse, undefined);
+
+      assert_less_than(
+        entry.processingEnd,
+        timeOfVisibilityFalse,
+        "event handler ends before visibility event fires"
+      );
+      assert_less_than(
+        timeOfVisibilityFalse,
+        timeAfterVisibilityFalse,
+        "visibility event fires before event handler continues"
+      );
+      assert_less_than_equal(
+        entry.startTime + entry.duration,
+        timeAfterVisibilityFalse,
+        "event duration ends before visibility is changed"
+      );
+
+    }, "Event handlers which change visibility should not measure next paint.");
+
+  </script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/selection/crashtests/selection-clip-crash.html b/third_party/blink/web_tests/external/wpt/selection/crashtests/selection-clip-crash.html
new file mode 100644
index 0000000..66d10b4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/selection/crashtests/selection-clip-crash.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Philip Rogers" href="mailto:pdr@chromium.org">
+<meta name="assert" content="Moving a selection to a different clipper should not crash">
+<html class=reftest-wait>
+  <script>
+    function test() {
+      document.designMode = "on";
+
+      const a = document.getElementById('a');
+      const range = new Range();
+      range.setStart(a, 1);
+      range.setEnd(a, 1);
+      document.getSelection().addRange(range);
+      requestAnimationFrame(function() {
+        requestAnimationFrame(function() {
+          const b = document.getElementById('b');
+          const updated_range = window.getSelection().getRangeAt(0);
+          updated_range.setStart(b, 1);
+          updated_range.setEnd(b, 1);
+
+          document.documentElement.classList.remove("reftest-wait");
+        });
+      });
+    }
+  </script>
+  <body onload=test()>
+    <div id=a style="width: 100px; height: 100px;"> </div>
+    <div id=b style="width: 100px; height: 100px; overflow: clip; margin-top: -100px;"> </div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/utils.sub.js b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/utils.sub.js
index ea70939..aaa73c21 100644
--- a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/utils.sub.js
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/utils.sub.js
@@ -157,6 +157,7 @@
   insertSpeculationRules({
     prefetch: [{
       source: 'document',
+      eagerness: 'eager',
       where: predicate,
       ...extra_options
     }]
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/display.tentative.html b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/display.tentative.html
new file mode 100644
index 0000000..6b28dcd9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/display.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Animation type for the 'display' property</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+
+<body>
+  <div id="log"></div>
+  <div id="target"></div>
+  <script>
+    'use strict';
+
+    test(t => {
+      const div = createDiv(t);
+      div.style.display = 'none';
+      const anim = div.animate({ display: ['block', 'block'] },
+        { duration: 100 * MS_PER_SEC });
+
+      anim.currentTime = 0;
+      assert_equals(getComputedStyle(div).display, 'block',
+        'Display when progress = 0');
+
+      anim.currentTime = 10 * MS_PER_SEC + 1;
+      assert_equals(getComputedStyle(div).display, 'block',
+        'Display when progress > 0');
+
+      anim.finish();
+      assert_equals(getComputedStyle(div).display, 'none',
+        'Display when progress = 1');
+
+    }, 'Display can be held by animation');
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/fast/multicol/min-height-greater-than-content.html b/third_party/blink/web_tests/fast/multicol/min-height-greater-than-content.html
index 66f4aab..c39dc9a 100644
--- a/third_party/blink/web_tests/fast/multicol/min-height-greater-than-content.html
+++ b/third_party/blink/web_tests/fast/multicol/min-height-greater-than-content.html
@@ -10,7 +10,7 @@
     content (i.e. the flowthread), on the other hand, is actually greater than min-height (but that
     shouldn't make any difference).</p>
 <p>Below there should be a blue square above a yellow square.</p>
-<div id="test" style="position:relative; -webkit-columns:4; -webkit-column-gap:0; column-fill:auto; width:80px; min-height:160px; background:yellow;">
+<div id="test" style="position:relative; columns:4; gap:0; width:80px; min-height:160px; background:yellow;">
     <div class="box" data-offset-y="0"></div>
     <div class="box" data-offset-y="0"></div>
     <div class="box" data-offset-y="0"></div>
diff --git a/third_party/blink/web_tests/fast/multicol/min-height-less-than-content.html b/third_party/blink/web_tests/fast/multicol/min-height-less-than-content.html
index 2e9b201..30a8794d 100644
--- a/third_party/blink/web_tests/fast/multicol/min-height-less-than-content.html
+++ b/third_party/blink/web_tests/fast/multicol/min-height-less-than-content.html
@@ -2,7 +2,7 @@
 <script src="../../resources/check-layout.js"></script>
 <p>Min-height has no effect if it is less than the height of the content, and height is auto.</p>
 <p>The word "OKAY" should be seen below.</p>
-<div id="test" style="position:relative; -webkit-columns:4; width:4em; -webkit-column-gap:0; column-fill:auto; overflow:hidden; min-height:10px; line-height:20px;">
+<div id="test" style="position:relative; columns:4; width:4em; gap:0; overflow:hidden; min-height:10px; line-height:20px;">
     <br>
     <br>
     <div data-offset-y="40">O</div>
diff --git a/third_party/blink/web_tests/fast/multicol/min-height-much-greater-than-content.html b/third_party/blink/web_tests/fast/multicol/min-height-much-greater-than-content.html
index bccf843..f2cf2b8 100644
--- a/third_party/blink/web_tests/fast/multicol/min-height-much-greater-than-content.html
+++ b/third_party/blink/web_tests/fast/multicol/min-height-much-greater-than-content.html
@@ -4,7 +4,7 @@
     height, but it should not affect column balancing. We specify column-fill:auto here, but the
     columns should still be balanced, because height is auto.</p>
 <p>Below there should be a yellow square with the word "OKAY" inside - from left to right, with large letter spacing.</p>
-<div id="test" style="position:relative; -webkit-columns:8; -webkit-column-gap:0; column-fill:auto; width:160px; min-height:160px; line-height:20px; background:yellow;">
+<div id="test" style="position:relative; columns:8; gap:0; width:160px; min-height:160px; line-height:20px; background:yellow;">
     <div data-offset-y="0">O</div>
     <div data-offset-y="0">K</div>
     <div data-offset-y="0">A</div>
diff --git a/third_party/blink/web_tests/fast/multicol/nested-balanced-inner-column-count-1-with-forced-break.html b/third_party/blink/web_tests/fast/multicol/nested-balanced-inner-column-count-1-with-forced-break.html
index f6fce3d1..ad73661 100644
--- a/third_party/blink/web_tests/fast/multicol/nested-balanced-inner-column-count-1-with-forced-break.html
+++ b/third_party/blink/web_tests/fast/multicol/nested-balanced-inner-column-count-1-with-forced-break.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <p>There should be a papayawhip square below. It should be aligned in
     the bottom right corner of a hotpink rectangle with a black border.</p>
-<div style="columns:2; column-fill:auto; column-gap:0; width:80px; line-height:20px; border:1px solid black; background:hotpink;">
+<div style="columns:2; gap:0; width:80px; line-height:20px; border:1px solid black; background:hotpink;">
     <div style="columns:1;">
         <div style="height:1px;"></div>
         <div style="height:6px; break-before:column;"></div>
diff --git a/third_party/blink/web_tests/fast/multicol/span/balance-before-and-after-spanner-expected.html b/third_party/blink/web_tests/fast/multicol/span/balance-before-and-after-spanner-expected.html
index e1066170..e0f8113 100644
--- a/third_party/blink/web_tests/fast/multicol/span/balance-before-and-after-spanner-expected.html
+++ b/third_party/blink/web_tests/fast/multicol/span/balance-before-and-after-spanner-expected.html
@@ -2,11 +2,11 @@
 <p>Inside the dashed box below you should see the word "PASS", then a small blue box, then
     the word "PASS" again. Large letter spacing is fine.</p>
 <div style="width:4em; margin:20px; border:2px dashed salmon; padding:10px; orphans:1; widows:1;">
-    <div style="-webkit-columns:4; columns:4; column-fill:auto;">
+    <div style="columns:4;">
         P<br>A<br>S<br>S
     </div>
     <div style="width:0.5em; height:1em; margin:auto; background:blue;"></div>
-    <div style="-webkit-columns:4; columns:4; column-fill:auto;">
+    <div style="columns:4;">
         P<br>A<br>S<br>S
     </div>
 </div>
diff --git a/third_party/blink/web_tests/fast/multicol/span/balance-before-and-after-spanner.html b/third_party/blink/web_tests/fast/multicol/span/balance-before-and-after-spanner.html
index 737feb4e..dbcaaad8 100644
--- a/third_party/blink/web_tests/fast/multicol/span/balance-before-and-after-spanner.html
+++ b/third_party/blink/web_tests/fast/multicol/span/balance-before-and-after-spanner.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <p>Inside the dashed box below you should see the word "PASS", then a small blue box, then
     the word "PASS" again. Large letter spacing is fine.</p>
-<div style="width:4em; margin:20px; border:2px dashed salmon; padding:10px; -webkit-columns:4; column-fill:auto; orphans:1; widows:1;">
+<div style="width:4em; margin:20px; border:2px dashed salmon; padding:10px; columns:4; orphans:1; widows:1;">
     P<br>A<br>S<br>S
-    <div style="-webkit-column-span:all; width:0.5em; height:1em; margin:auto; background:blue;"></div>
+    <div style="column-span:all; width:0.5em; height:1em; margin:auto; background:blue;"></div>
     P<br>A<br>S<br>S
 </div>
diff --git a/third_party/blink/web_tests/fast/multicol/span/fill-after-spanner-extra-height-expected.html b/third_party/blink/web_tests/fast/multicol/span/fill-after-spanner-extra-height-expected.html
index cb6ab47..35246be 100644
--- a/third_party/blink/web_tests/fast/multicol/span/fill-after-spanner-extra-height-expected.html
+++ b/third_party/blink/web_tests/fast/multicol/span/fill-after-spanner-extra-height-expected.html
@@ -2,7 +2,7 @@
 <p>Inside the dashed box below you should see the word "PASS", then a small blue box, then
     the the numbers 1, 2 and 3 on separate lines. Large letter spacing is expected.</p>
 <div style="width:4em; margin:20px; border:2px dashed salmon; padding:10px; height:10em; orphans:1; widows:1;">
-    <div style=" -webkit-columns:4; column-fill:auto;">
+    <div style="columns:4;">
         P<br>A<br>S<br>S
     </div>
     <div style="width:0.5em; height:1em; margin:auto; background:blue;"></div>
diff --git a/third_party/blink/web_tests/fast/multicol/span/offset-properties-empty-content.html b/third_party/blink/web_tests/fast/multicol/span/offset-properties-empty-content.html
index c1fac10..511a2d6 100644
--- a/third_party/blink/web_tests/fast/multicol/span/offset-properties-empty-content.html
+++ b/third_party/blink/web_tests/fast/multicol/span/offset-properties-empty-content.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
-<div style="position:relative; border:10px solid; columns:3; column-gap:0; column-fill:auto; width:300px;">
+<div style="position:relative; border:10px solid; columns:3; gap:0; width:300px;">
     <div></div>
     <!-- In front of the spanner there's only a zero-height DIV. This does still count as column
          content, and we'll therefore establish a zero-height column set in front of the spanner, as
diff --git a/third_party/blink/web_tests/fast/sub-pixel/column-clipping.html b/third_party/blink/web_tests/fast/sub-pixel/column-clipping.html
index 828c9abd..bf8d4f6 100644
--- a/third_party/blink/web_tests/fast/sub-pixel/column-clipping.html
+++ b/third_party/blink/web_tests/fast/sub-pixel/column-clipping.html
@@ -2,9 +2,7 @@
 <html>
 <style>
 body > div {
-	-webkit-column-count: 2;
 	column-count: 2;
-	column-fill: auto;
 	width: 51px;
 	position: absolute;
 	left: 0px;
diff --git a/third_party/blink/web_tests/fragmentation/overflow-crossing-boundary.html b/third_party/blink/web_tests/fragmentation/overflow-crossing-boundary.html
index 1795f9c..e058842 100644
--- a/third_party/blink/web_tests/fragmentation/overflow-crossing-boundary.html
+++ b/third_party/blink/web_tests/fragmentation/overflow-crossing-boundary.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<div id="multicol" style="position:relative; columns:2; column-fill:auto; line-height:20px; orphans:1; widows:1; background:yellow;">
+<div id="multicol" style="position:relative; columns:2; line-height:20px; orphans:1; widows:1; background:yellow;">
     <div style="width:4em;">
         <div style="height:20px; background:hotpink;"></div>
         <div style="height:15px;">
@@ -28,6 +28,7 @@
     assert_equals(child4.offsetTop, 20);
     assert_equals(child5.offsetTop, 40);
 
+    multicol.style.columnFill = "auto";
     multicol.style.height = "90px";
     assert_equals(child3.offsetTop, 60);
     assert_equals(child4.offsetTop, 0);
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt
index 71205bd9..4744b6b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-iframe-idb-expected.txt
@@ -30,10 +30,9 @@
  Shared Storage
   http://127.0.0.1:8000
   http://devtools.oopif.test:8000
-Cache
  Cache Storage
- Back/forward cache
 Background Services
+ Back/forward cache
  Background Fetch
  Background Sync
  Notifications
@@ -72,10 +71,9 @@
  Interest Groups
  Shared Storage
   http://127.0.0.1:8000
-Cache
  Cache Storage
- Back/forward cache
 Background Services
+ Back/forward cache
  Background Fetch
  Background Sync
  Notifications
@@ -100,10 +98,10 @@
 Storage
  Local Storage
   http://127.0.0.1:8000/
-  http://devtools.oopif.test:8000/^0http://127.0.0.1^31
+  http://devtools.oopif.test:8000
  Session Storage
   http://127.0.0.1:8000/
-  http://devtools.oopif.test:8000/^0http://127.0.0.1^31
+  http://devtools.oopif.test:8000
  IndexedDB
   Database-main-frame - http://127.0.0.1:8000/
   Database-iframe - http://devtools.oopif.test:8000/^0http://127.0.0.1^31
@@ -117,10 +115,9 @@
  Shared Storage
   http://127.0.0.1:8000
   http://devtools.oopif.test:8000
-Cache
  Cache Storage
- Back/forward cache
 Background Services
+ Back/forward cache
  Background Fetch
  Background Sync
  Notifications
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-on-navigation-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-on-navigation-expected.txt
index c014896e..35fee3f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-on-navigation-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-on-navigation-expected.txt
@@ -24,10 +24,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
@@ -63,10 +62,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-resource-preview-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-resource-preview-expected.txt
index 70eb178..68dc66f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-resource-preview-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-resource-preview-expected.txt
@@ -22,10 +22,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
@@ -63,10 +62,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
@@ -104,10 +102,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
@@ -145,10 +142,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload-expected.txt
index adf47fa..7d010f7 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-selection-on-reload-expected.txt
@@ -24,10 +24,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
@@ -64,10 +63,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
diff --git a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-websql-expected.txt b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-websql-expected.txt
index d5005244..18799d9 100644
--- a/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-websql-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/application-panel/resources-panel-websql-expected.txt
@@ -22,10 +22,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
@@ -62,10 +61,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
@@ -102,10 +100,9 @@
   Interest Groups
   Shared Storage
     http://127.0.0.1:8000
-Cache
   Cache Storage
-  Back/forward cache
 Background Services
+  Back/forward cache
   Background Fetch
   Background Sync
   Notifications
diff --git a/third_party/blink/web_tests/http/tests/devtools/background-services/background-service-grid.js b/third_party/blink/web_tests/http/tests/devtools/background-services/background-service-grid.js
index 7fa586b..5d80a89 100644
--- a/third_party/blink/web_tests/http/tests/devtools/background-services/background-service-grid.js
+++ b/third_party/blink/web_tests/http/tests/devtools/background-services/background-service-grid.js
@@ -37,8 +37,6 @@
 }
 
 (async function() {
-  Root.Runtime.experiments.setEnabled('backgroundServices', true);
-
   TestRunner.addResult(`Tests that the grid shows information as expected.\n`);
   await TestRunner.loadLegacyModule('resources');
   await TestRunner.showPanel('resources');
diff --git a/third_party/blink/web_tests/http/tests/devtools/background-services/background-services-panel.js b/third_party/blink/web_tests/http/tests/devtools/background-services/background-services-panel.js
index 3d8965e..9e1da96 100644
--- a/third_party/blink/web_tests/http/tests/devtools/background-services/background-services-panel.js
+++ b/third_party/blink/web_tests/http/tests/devtools/background-services/background-services-panel.js
@@ -40,8 +40,6 @@
 }
 
 (async function() {
-  Root.Runtime.experiments.setEnabled('backgroundServices', true);
-
   TestRunner.addResult(`Tests the bottom panel shows information as expected.\n`);
   await TestRunner.loadLegacyModule('resources');
   await TestRunner.showPanel('resources');
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/README.md b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/README.md
index 6984bc9..300d727 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/README.md
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/README.md
@@ -1,4 +1,3 @@
 # virtual/third-party-storage-partitioning
 
-This directory is for tests that need the Third-Party Storage Partitioning feature enabled.
-Tests under `virtual/third-party-storage-partitioning` are run with `--enable-features=ThirdPartyStoragePartitioning`
+This suite verifies tests when ThirdPartyStoragePartitioning is disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/FileAPI/BlobURL/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/FileAPI/BlobURL/README.txt
new file mode 100644
index 0000000..62d1233
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/FileAPI/BlobURL/README.txt
@@ -0,0 +1 @@
+This suite runs BlobURL tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/IndexedDB/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/IndexedDB/README.txt
index 3665aad0..06cc2613 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/IndexedDB/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/IndexedDB/README.txt
@@ -1 +1 @@
-This suite runs IndexedDB tests with ThirdPartyStoragePartitioning enabled.
+This suite runs IndexedDB tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/html/browsers/windows/post-message/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/html/browsers/windows/post-message/README.txt
new file mode 100644
index 0000000..860fadf
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/html/browsers/windows/post-message/README.txt
@@ -0,0 +1 @@
+This suite runs postMessage tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/secure-contexts/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/secure-contexts/README.txt
new file mode 100644
index 0000000..dfc5a2f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/secure-contexts/README.txt
@@ -0,0 +1 @@
+This suite runs secure context tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/cache-storage/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/cache-storage/README.txt
index 34e5c7fe..d065ba15 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/cache-storage/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/cache-storage/README.txt
@@ -1 +1 @@
-This suite runs CacheStorage tests with ThirdPartyStoragePartitioning enabled.
+This suite runs CacheStorage tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/README.txt
index 4841463..9067191c 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/README.txt
@@ -1 +1 @@
-This suite runs service-worker tests with ThirdPartyStoragePartitioning enabled.
+This suite runs service-worker tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/storage/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/storage/README.txt
index befcbc77..9c338a6 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/storage/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/storage/README.txt
@@ -1,2 +1,2 @@
-This suite runs Storage Manager tests with ThirdPartyStoragePartitioning enabled.
+This suite runs Storage Manager tests with ThirdPartyStoragePartitioning disabled.
 
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/web-locks/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/web-locks/README.txt
index 2289aa4..e8a3fc5 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/web-locks/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/web-locks/README.txt
@@ -1 +1 @@
-This suite runs web-locks tests with ThirdPartyStoragePartitioning enabled.
+This suite runs web-locks tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/README.txt
index 7daa7eab..9282d03 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webmessaging/broadcastchannel/README.txt
@@ -1,2 +1 @@
-This suite runs BroadcastChannel tests with ThirdPartyStoragePartitioning
-enabled.
+This suite runs BroadcastChannel tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webstorage/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webstorage/README.txt
index 2d846730..866f4dbf 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webstorage/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/webstorage/README.txt
@@ -1,2 +1,2 @@
-This suite runs webstorage tests with ThirdPartyStoragePartitioning enabled.
+This suite runs webstorage tests with ThirdPartyStoragePartitioning disabled.
 
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/workers/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/workers/README.txt
index 60a62a0..29da012 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/workers/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/workers/README.txt
@@ -1 +1 @@
-This suite runs worker tests with ThirdPartyStoragePartitioning enabled.
\ No newline at end of file
+This suite runs worker tests with ThirdPartyStoragePartitioning disabled.
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/fast/filesystem/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/fast/filesystem/README.txt
index 8c03835..711af0c 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/fast/filesystem/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/fast/filesystem/README.txt
@@ -1 +1 @@
-This suite runs File System tests with ThirdPartyStoragePartitioning enabled.
+This suite runs File System tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/http/tests/inspector-protocol/storage/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/http/tests/inspector-protocol/storage/README.txt
new file mode 100644
index 0000000..d479225
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/http/tests/inspector-protocol/storage/README.txt
@@ -0,0 +1 @@
+This suite runs inspector protocol storage tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/http/tests/storage/partitioned-storage/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/http/tests/storage/partitioned-storage/README.txt
index dd975bd..7a3a1405 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/http/tests/storage/partitioned-storage/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/http/tests/storage/partitioned-storage/README.txt
@@ -1 +1 @@
-This suite runs Partitioned Storage tests with ThirdPartyStoragePartitioning enabled.
+This suite runs Partitioned Storage tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/wpt_internal/file-system-api/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/wpt_internal/file-system-api/README.txt
index 8c03835..711af0c 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/wpt_internal/file-system-api/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/wpt_internal/file-system-api/README.txt
@@ -1 +1 @@
-This suite runs File System tests with ThirdPartyStoragePartitioning enabled.
+This suite runs File System tests with ThirdPartyStoragePartitioning disabled.
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/wpt_internal/storage/quota/README.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/wpt_internal/storage/quota/README.txt
index 661fc03..6c9f1be 100644
--- a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/wpt_internal/storage/quota/README.txt
+++ b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/wpt_internal/storage/quota/README.txt
@@ -1,4 +1,4 @@
-This suite runs the following tests with ThirdPartyStoragePartitioning enabled:
+This suite runs the following tests with ThirdPartyStoragePartitioning disabled:
 
 - partitioned-webkitTemporaryStorage-usage-details.sub.html
 - partitioned-webkitPersistentStorage-quota-usage-details.sub.html
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/source-matching.sub.https.html b/third_party/blink/web_tests/wpt_internal/attribution-reporting/source-matching.sub.https.html
new file mode 100644
index 0000000..dc9e9ff
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/source-matching.sub.https.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script>
+
+const source_matching_promise_test = (testCase) => {
+  attribution_reporting_promise_test(async t => {
+    await registerAttributionSrc(t, {
+      source: {
+        destination: testCase.sourceDestination,
+      },
+      reportingOrigin: testCase.sourceReportingOrigin,
+    });
+
+    await registerAttributionSrc(t, {
+      trigger: {
+        event_trigger_data: [{}],
+        debug_reporting: true,
+      },
+      cookie: attributionDebugCookie,
+    });
+
+    if (testCase.expectMatched) {
+      const payload = await pollEventLevelReports();
+      assert_equals(payload.reports.length, 1);
+      return;
+    }
+
+    const payload = await pollVerboseDebugReports();
+    assert_equals(payload.reports.length, 1);
+    const debugReport = JSON.parse(payload.reports[0].body);
+    assert_equals(debugReport.length, 1);
+    assert_equals(debugReport[0].type, 'trigger-no-matching-source');
+  }, testCase.testName);
+};
+
+assert_not_equals(location.origin, get_host_info().HTTPS_REMOTE_ORIGIN);
+
+[
+  {
+    testName: 'Same destination origin and reporting origin',
+    sourceDestination: location.origin,
+    expectMatched: true,
+  },
+  {
+    testName: 'Different destination origin but same site',
+    sourceDestination: get_host_info().HTTPS_REMOTE_ORIGIN,
+    expectMatched: true,
+  },
+  {
+    testName: 'Different destination site',
+    sourceDestination: 'https://example.test',
+    expectMatched: false,
+  },
+  {
+    testName: 'Different reporting origin',
+    sourceDestination: location.origin,
+    sourceReportingOrigin: get_host_info().HTTPS_REMOTE_ORIGIN,
+    expectMatched: false,
+  },
+].forEach(source_matching_promise_test);
+
+</script>
diff --git a/third_party/boringssl/BUILD.generated_tests.gni b/third_party/boringssl/BUILD.generated_tests.gni
index be58ede..f0728fb9 100644
--- a/third_party/boringssl/BUILD.generated_tests.gni
+++ b/third_party/boringssl/BUILD.generated_tests.gni
@@ -200,6 +200,25 @@
   "src/crypto/x509/test/many_names1.pem",
   "src/crypto/x509/test/many_names2.pem",
   "src/crypto/x509/test/many_names3.pem",
+  "src/crypto/x509/test/policy_intermediate.pem",
+  "src/crypto/x509/test/policy_intermediate_any.pem",
+  "src/crypto/x509/test/policy_intermediate_duplicate.pem",
+  "src/crypto/x509/test/policy_intermediate_invalid.pem",
+  "src/crypto/x509/test/policy_intermediate_mapped.pem",
+  "src/crypto/x509/test/policy_intermediate_mapped_any.pem",
+  "src/crypto/x509/test/policy_intermediate_require.pem",
+  "src/crypto/x509/test/policy_intermediate_require_duplicate.pem",
+  "src/crypto/x509/test/policy_intermediate_require_no_policies.pem",
+  "src/crypto/x509/test/policy_leaf.pem",
+  "src/crypto/x509/test/policy_leaf_any.pem",
+  "src/crypto/x509/test/policy_leaf_duplicate.pem",
+  "src/crypto/x509/test/policy_leaf_invalid.pem",
+  "src/crypto/x509/test/policy_leaf_oid1.pem",
+  "src/crypto/x509/test/policy_leaf_oid2.pem",
+  "src/crypto/x509/test/policy_leaf_oid3.pem",
+  "src/crypto/x509/test/policy_leaf_oid4.pem",
+  "src/crypto/x509/test/policy_leaf_oid5.pem",
+  "src/crypto/x509/test/policy_root.pem",
   "src/crypto/x509/test/pss_sha1.pem",
   "src/crypto/x509/test/pss_sha1_explicit.pem",
   "src/crypto/x509/test/pss_sha1_mgf1_syntax_error.pem",
diff --git a/third_party/boringssl/apple-aarch64/crypto/chacha/chacha-armv8.S b/third_party/boringssl/apple-aarch64/crypto/chacha/chacha-armv8.S
index dd992a2..61974285 100644
--- a/third_party/boringssl/apple-aarch64/crypto/chacha/chacha-armv8.S
+++ b/third_party/boringssl/apple-aarch64/crypto/chacha/chacha-armv8.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1989,4 +1989,8 @@
 	AARCH64_VALIDATE_LINK_REGISTER
 	ret
 
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S b/third_party/boringssl/apple-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S
index eea0722..7fce175 100644
--- a/third_party/boringssl/apple-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S
+++ b/third_party/boringssl/apple-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -3014,4 +3014,8 @@
 	b	Lopen_128_hash_64
 .cfi_endproc
 
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/aesv8-armx64.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/aesv8-armx64.S
index 50d7dea..7e9cf40 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/aesv8-armx64.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/aesv8-armx64.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -796,4 +796,8 @@
 	ret
 
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/armv8-mont.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/armv8-mont.S
index 2493ae0..9f1b810 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/armv8-mont.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/armv8-mont.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1430,4 +1430,8 @@
 .byte	77,111,110,116,103,111,109,101,114,121,32,77,117,108,116,105,112,108,105,99,97,116,105,111,110,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	4
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/ghash-neon-armv8.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/ghash-neon-armv8.S
index 5441afc..d4cc16f 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/ghash-neon-armv8.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/ghash-neon-armv8.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -340,4 +340,8 @@
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,100,101,114,105,118,101,100,32,102,114,111,109,32,65,82,77,118,52,32,118,101,114,115,105,111,110,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index 0ba0cdd..829c1e1 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -570,4 +570,8 @@
 .align	2
 .align	2
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S
index 75d2b93..cd24809 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1707,4 +1707,8 @@
 
 	ret
 
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S
index 317b8138..8214e116 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -314,4 +314,8 @@
 	AARCH64_VALIDATE_LINK_REGISTER
 	ret
 
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha1-armv8.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha1-armv8.S
index 62ba800..94c4fc5 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha1-armv8.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha1-armv8.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1232,4 +1232,8 @@
 .byte	83,72,65,49,32,98,108,111,99,107,32,116,114,97,110,115,102,111,114,109,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha256-armv8.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha256-armv8.S
index b40b260..f4a5283 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha256-armv8.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha256-armv8.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1209,4 +1209,8 @@
 	ret
 
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha512-armv8.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha512-armv8.S
index b2d366d..d005b0d0 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha512-armv8.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/sha512-armv8.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1611,4 +1611,8 @@
 	ret
 
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/vpaes-armv8.S b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/vpaes-armv8.S
index 6dfc25d..3beb563 100644
--- a/third_party/boringssl/apple-aarch64/crypto/fipsmodule/vpaes-armv8.S
+++ b/third_party/boringssl/apple-aarch64/crypto/fipsmodule/vpaes-armv8.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1229,4 +1229,8 @@
 	AARCH64_VALIDATE_LINK_REGISTER
 	ret
 
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-aarch64/crypto/test/trampoline-armv8.S b/third_party/boringssl/apple-aarch64/crypto/test/trampoline-armv8.S
index 325da9b..5e1b1ee 100644
--- a/third_party/boringssl/apple-aarch64/crypto/test/trampoline-armv8.S
+++ b/third_party/boringssl/apple-aarch64/crypto/test/trampoline-armv8.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -755,4 +755,8 @@
 	fmov	v15.d[1], xzr
 	ret
 
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/chacha/chacha-armv4.S b/third_party/boringssl/apple-arm/crypto/chacha/chacha-armv4.S
index cadf2b6..c42b0d2 100644
--- a/third_party/boringssl/apple-arm/crypto/chacha/chacha-armv4.S
+++ b/third_party/boringssl/apple-arm/crypto/chacha/chacha-armv4.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1495,4 +1495,8 @@
 .indirect_symbol	_OPENSSL_armcap_P
 .long	0
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/fipsmodule/aesv8-armx32.S b/third_party/boringssl/apple-arm/crypto/fipsmodule/aesv8-armx32.S
index 87b4b0a..3695e57 100644
--- a/third_party/boringssl/apple-arm/crypto/fipsmodule/aesv8-armx32.S
+++ b/third_party/boringssl/apple-arm/crypto/fipsmodule/aesv8-armx32.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -806,4 +806,8 @@
 	ldmia	sp!,{r4,r5,r6,r7,r8,r9,r10,pc}
 
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/fipsmodule/armv4-mont.S b/third_party/boringssl/apple-arm/crypto/fipsmodule/armv4-mont.S
index e549d1f1..a552fb5 100644
--- a/third_party/boringssl/apple-arm/crypto/fipsmodule/armv4-mont.S
+++ b/third_party/boringssl/apple-arm/crypto/fipsmodule/armv4-mont.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -979,4 +979,8 @@
 .long	0
 .private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/fipsmodule/bsaes-armv7.S b/third_party/boringssl/apple-arm/crypto/fipsmodule/bsaes-armv7.S
index 8329a8c2..a72b437 100644
--- a/third_party/boringssl/apple-arm/crypto/fipsmodule/bsaes-armv7.S
+++ b/third_party/boringssl/apple-arm/crypto/fipsmodule/bsaes-armv7.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1533,4 +1533,8 @@
 	@ out to retain a constant-time implementation.
 
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/fipsmodule/ghash-armv4.S b/third_party/boringssl/apple-arm/crypto/fipsmodule/ghash-armv4.S
index 36f4cceb..762ada8f 100644
--- a/third_party/boringssl/apple-arm/crypto/fipsmodule/ghash-armv4.S
+++ b/third_party/boringssl/apple-arm/crypto/fipsmodule/ghash-armv4.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -255,4 +255,8 @@
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,52,47,78,69,79,78,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/fipsmodule/ghashv8-armx32.S b/third_party/boringssl/apple-arm/crypto/fipsmodule/ghashv8-armx32.S
index dcac580..36d8937 100644
--- a/third_party/boringssl/apple-arm/crypto/fipsmodule/ghashv8-armx32.S
+++ b/third_party/boringssl/apple-arm/crypto/fipsmodule/ghashv8-armx32.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -257,4 +257,8 @@
 .align	2
 .align	2
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/fipsmodule/sha1-armv4-large.S b/third_party/boringssl/apple-arm/crypto/fipsmodule/sha1-armv4-large.S
index 82ac8df..fc0a262 100644
--- a/third_party/boringssl/apple-arm/crypto/fipsmodule/sha1-armv4-large.S
+++ b/third_party/boringssl/apple-arm/crypto/fipsmodule/sha1-armv4-large.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1515,4 +1515,8 @@
 .long	0
 .private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/fipsmodule/sha256-armv4.S b/third_party/boringssl/apple-arm/crypto/fipsmodule/sha256-armv4.S
index 0cf3648..26e58cf7 100644
--- a/third_party/boringssl/apple-arm/crypto/fipsmodule/sha256-armv4.S
+++ b/third_party/boringssl/apple-arm/crypto/fipsmodule/sha256-armv4.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2843,4 +2843,8 @@
 .long	0
 .private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/fipsmodule/sha512-armv4.S b/third_party/boringssl/apple-arm/crypto/fipsmodule/sha512-armv4.S
index 21913cb..95ff774 100644
--- a/third_party/boringssl/apple-arm/crypto/fipsmodule/sha512-armv4.S
+++ b/third_party/boringssl/apple-arm/crypto/fipsmodule/sha512-armv4.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1896,4 +1896,8 @@
 .long	0
 .private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/fipsmodule/vpaes-armv7.S b/third_party/boringssl/apple-arm/crypto/fipsmodule/vpaes-armv7.S
index 6aead7c..31690229a 100644
--- a/third_party/boringssl/apple-arm/crypto/fipsmodule/vpaes-armv7.S
+++ b/third_party/boringssl/apple-arm/crypto/fipsmodule/vpaes-armv7.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1262,4 +1262,8 @@
 	vldmia	sp!, {d8,d9,d10,d11,d12,d13,d14,d15}
 	ldmia	sp!, {r7,r8,r9,r10,r11, pc}	@ return
 
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-arm/crypto/test/trampoline-armv4.S b/third_party/boringssl/apple-arm/crypto/test/trampoline-armv4.S
index 9d74f55..2516f66a 100644
--- a/third_party/boringssl/apple-arm/crypto/test/trampoline-armv4.S
+++ b/third_party/boringssl/apple-arm/crypto/test/trampoline-armv4.S
@@ -8,7 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -373,4 +373,8 @@
 	vmov	s31, r0
 	bx	lr
 
-#endif  // !OPENSSL_NO_ASM
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86/crypto/chacha/chacha-x86.S b/third_party/boringssl/apple-x86/crypto/chacha/chacha-x86.S
index ef535b21..8d0de1db 100644
--- a/third_party/boringssl/apple-x86/crypto/chacha/chacha-x86.S
+++ b/third_party/boringssl/apple-x86/crypto/chacha/chacha-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -971,4 +977,8 @@
 L_OPENSSL_ia32cap_P$non_lazy_ptr:
 .indirect_symbol	_OPENSSL_ia32cap_P
 .long	0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/aesni-x86.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/aesni-x86.S
index 00f6003..8a55e3f 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/aesni-x86.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/aesni-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2473,4 +2479,8 @@
 L_OPENSSL_ia32cap_P$non_lazy_ptr:
 .indirect_symbol	_OPENSSL_ia32cap_P
 .long	0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/bn-586.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/bn-586.S
index ede2e76..a169595 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/bn-586.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/bn-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -985,4 +991,8 @@
 L_OPENSSL_ia32cap_P$non_lazy_ptr:
 .indirect_symbol	_OPENSSL_ia32cap_P
 .long	0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/co-586.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/co-586.S
index 015dffa..0d4554c 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/co-586.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/co-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1254,4 +1260,8 @@
 	popl	%edi
 	popl	%esi
 	ret
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/ghash-ssse3-x86.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/ghash-ssse3-x86.S
index 8656679..9d3bac5 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/ghash-ssse3-x86.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/ghash-ssse3-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -286,4 +292,8 @@
 .align	4,0x90
 Llow4_mask:
 .long	252645135,252645135,252645135,252645135
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/ghash-x86.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/ghash-x86.S
index c1e0d53..e39452fa 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/ghash-x86.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/ghash-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -320,4 +326,8 @@
 .byte	82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112
 .byte	112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62
 .byte	0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/md5-586.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/md5-586.S
index f4c4b50..7ff200e0 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/md5-586.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/md5-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -682,4 +688,8 @@
 	popl	%edi
 	popl	%esi
 	ret
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/sha1-586.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/sha1-586.S
index 3213a62..8951d0c 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/sha1-586.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/sha1-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -3802,4 +3808,8 @@
 L_OPENSSL_ia32cap_P$non_lazy_ptr:
 .indirect_symbol	_OPENSSL_ia32cap_P
 .long	0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/sha256-586.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/sha256-586.S
index c81cb9a..b9afaf0 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/sha256-586.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/sha256-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -5565,4 +5571,8 @@
 L_OPENSSL_ia32cap_P$non_lazy_ptr:
 .indirect_symbol	_OPENSSL_ia32cap_P
 .long	0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/sha512-586.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/sha512-586.S
index 8c33cf5..b3a53f3 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/sha512-586.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/sha512-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2835,4 +2841,8 @@
 L_OPENSSL_ia32cap_P$non_lazy_ptr:
 .indirect_symbol	_OPENSSL_ia32cap_P
 .long	0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/vpaes-x86.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/vpaes-x86.S
index 00c0190..476529a 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/vpaes-x86.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/vpaes-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -678,4 +684,8 @@
 	popl	%ebx
 	popl	%ebp
 	ret
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/fipsmodule/x86-mont.S b/third_party/boringssl/apple-x86/crypto/fipsmodule/x86-mont.S
index 7850a37a..a977cec 100644
--- a/third_party/boringssl/apple-x86/crypto/fipsmodule/x86-mont.S
+++ b/third_party/boringssl/apple-x86/crypto/fipsmodule/x86-mont.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -482,4 +488,8 @@
 L_OPENSSL_ia32cap_P$non_lazy_ptr:
 .indirect_symbol	_OPENSSL_ia32cap_P
 .long	0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86/crypto/test/trampoline-x86.S b/third_party/boringssl/apple-x86/crypto/test/trampoline-x86.S
index fd40b95..17d84feb 100644
--- a/third_party/boringssl/apple-x86/crypto/test/trampoline-x86.S
+++ b/third_party/boringssl/apple-x86/crypto/test/trampoline-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -166,4 +172,8 @@
 L_abi_test_clobber_xmm7_begin:
 	pxor	%xmm7,%xmm7
 	ret
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__APPLE__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/chacha/chacha-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/chacha/chacha-x86_64.S
index 782ddf4..226d40c 100644
--- a/third_party/boringssl/apple-x86_64/crypto/chacha/chacha-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/chacha/chacha-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1623,3 +1623,7 @@
 
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
index f988089d..db8bf50 100644
--- a/third_party/boringssl/apple-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -3066,3 +3066,7 @@
 
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S b/third_party/boringssl/apple-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
index 6813510..a1310b2a 100644
--- a/third_party/boringssl/apple-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -8876,3 +8876,7 @@
 
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
index e497c35..fc0f014 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -305,6 +305,9 @@
 	vpaddb	%xmm2,%xmm1,%xmm0
 	movq	%r13,112+8(%rsp)
 	leaq	96(%rdi),%rdi
+
+	prefetcht0	512(%rdi)
+	prefetcht0	576(%rdi)
 	vaesenclast	%xmm5,%xmm11,%xmm11
 	vpaddb	%xmm2,%xmm0,%xmm5
 	movq	%r12,120+8(%rsp)
@@ -848,3 +851,7 @@
 .byte	65,69,83,45,78,73,32,71,67,77,32,109,111,100,117,108,101,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .p2align	6
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/aesni-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/aesni-x86_64.S
index 7633880..c08f7d9 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/aesni-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/aesni-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2501,3 +2501,7 @@
 .byte	65,69,83,32,102,111,114,32,73,110,116,101,108,32,65,69,83,45,78,73,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .p2align	6
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S
index 7f92fc5..1f94506 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -424,3 +424,7 @@
 L$low4_mask:
 .quad	0x0f0f0f0f0f0f0f0f, 0x0f0f0f0f0f0f0f0f
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/ghash-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/ghash-x86_64.S
index fd767a0..9f4cb04 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/ghash-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/ghash-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1123,3 +1123,7 @@
 .byte	71,72,65,83,72,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .p2align	6
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/md5-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/md5-x86_64.S
index 06e3ba0..60afa5a 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/md5-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/md5-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -694,3 +694,7 @@
 
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/p256-x86_64-asm.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
index 36057aa1..5967d83 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -4465,3 +4465,7 @@
 
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S
index ae7293ac..3a71b379 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -326,3 +326,7 @@
 
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/rdrand-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/rdrand-x86_64.S
index 664c0674..08cd93f 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/rdrand-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/rdrand-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -60,3 +60,7 @@
 
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/rsaz-avx2.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/rsaz-avx2.S
index bebc699..3cdd967 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/rsaz-avx2.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/rsaz-avx2.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1746,3 +1746,7 @@
 .long	4,4,4,4, 4,4,4,4
 .p2align	6
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha1-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha1-x86_64.S
index d50851ed..c1510d6 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha1-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha1-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -5464,3 +5464,7 @@
 .byte	83,72,65,49,32,98,108,111,99,107,32,116,114,97,110,115,102,111,114,109,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .p2align	6
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha256-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha256-x86_64.S
index 00dc01c..635b165 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha256-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha256-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -4180,3 +4180,7 @@
 
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha512-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha512-x86_64.S
index 5732f43..d21f0f4 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha512-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/sha512-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2988,3 +2988,7 @@
 
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/vpaes-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/vpaes-x86_64.S
index 31cf32904..b2c3c94 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/vpaes-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/vpaes-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1128,3 +1128,7 @@
 .p2align	6
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/x86_64-mont.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/x86_64-mont.S
index d354b2d..c0c8a05 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/x86_64-mont.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/x86_64-mont.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1254,3 +1254,7 @@
 .byte	77,111,110,116,103,111,109,101,114,121,32,77,117,108,116,105,112,108,105,99,97,116,105,111,110,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .p2align	4
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/x86_64-mont5.S b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/x86_64-mont5.S
index a071e84..3b99f5b 100644
--- a/third_party/boringssl/apple-x86_64/crypto/fipsmodule/x86_64-mont5.S
+++ b/third_party/boringssl/apple-x86_64/crypto/fipsmodule/x86_64-mont5.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -3621,3 +3621,7 @@
 .long	2,2, 2,2
 .byte	77,111,110,116,103,111,109,101,114,121,32,77,117,108,116,105,112,108,105,99,97,116,105,111,110,32,119,105,116,104,32,115,99,97,116,116,101,114,47,103,97,116,104,101,114,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/apple-x86_64/crypto/test/trampoline-x86_64.S b/third_party/boringssl/apple-x86_64/crypto/test/trampoline-x86_64.S
index 5f20aa7..eebbf805 100644
--- a/third_party/boringssl/apple-x86_64/crypto/test/trampoline-x86_64.S
+++ b/third_party/boringssl/apple-x86_64/crypto/test/trampoline-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__APPLE__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -511,3 +511,7 @@
 	.byte	0xf3,0xc3
 
 #endif
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-aarch64/crypto/chacha/chacha-armv8.S b/third_party/boringssl/linux-aarch64/crypto/chacha/chacha-armv8.S
index 4392111..598cb2a 100644
--- a/third_party/boringssl/linux-aarch64/crypto/chacha/chacha-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/chacha/chacha-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1990,6 +1989,8 @@
 	AARCH64_VALIDATE_LINK_REGISTER
 	ret
 .size	ChaCha20_512_neon,.-ChaCha20_512_neon
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S b/third_party/boringssl/linux-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S
index 69e1296..73a3d64 100644
--- a/third_party/boringssl/linux-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -3015,6 +3014,8 @@
 	b	.Lopen_128_hash_64
 .cfi_endproc
 .size	chacha20_poly1305_open,.-chacha20_poly1305_open
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S
index 4949ba3..fe13c64d 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -797,6 +796,8 @@
 	ret
 .size	aes_hw_ctr32_encrypt_blocks,.-aes_hw_ctr32_encrypt_blocks
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/armv8-mont.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/armv8-mont.S
index db89859..8ba7819 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/armv8-mont.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/armv8-mont.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1431,6 +1430,8 @@
 .byte	77,111,110,116,103,111,109,101,114,121,32,77,117,108,116,105,112,108,105,99,97,116,105,111,110,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	4
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghash-neon-armv8.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghash-neon-armv8.S
index 098967b..1f814980 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghash-neon-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghash-neon-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -341,6 +340,8 @@
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,100,101,114,105,118,101,100,32,102,114,111,109,32,65,82,77,118,52,32,118,101,114,115,105,111,110,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index 4544cee0..bfe17cb 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -571,6 +570,8 @@
 .align	2
 .align	2
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S
index d255da2..780bfe5 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1708,6 +1707,8 @@
 
 	ret
 .size	ecp_nistz256_select_w7,.-ecp_nistz256_select_w7
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S
index 9243b8b..0f8645f7 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -315,6 +314,8 @@
 	AARCH64_VALIDATE_LINK_REGISTER
 	ret
 .size	beeu_mod_inverse_vartime,.-beeu_mod_inverse_vartime
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
index d7a87958..079f272 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1233,6 +1232,8 @@
 .byte	83,72,65,49,32,98,108,111,99,107,32,116,114,97,110,115,102,111,114,109,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
index c777ec8..98a685f1 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1210,6 +1209,8 @@
 	ret
 .size	sha256_block_armv8,.-sha256_block_armv8
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
index a3b458a..5e2f885 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1612,6 +1611,8 @@
 	ret
 .size	sha512_block_armv8,.-sha512_block_armv8
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S
index 59b1d31d..a4c2f83 100644
--- a/third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1230,6 +1229,8 @@
 	AARCH64_VALIDATE_LINK_REGISTER
 	ret
 .size	vpaes_ctr32_encrypt_blocks,.-vpaes_ctr32_encrypt_blocks
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-aarch64/crypto/test/trampoline-armv8.S b/third_party/boringssl/linux-aarch64/crypto/test/trampoline-armv8.S
index 8928d7f..08e0bc35 100644
--- a/third_party/boringssl/linux-aarch64/crypto/test/trampoline-armv8.S
+++ b/third_party/boringssl/linux-aarch64/crypto/test/trampoline-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -756,6 +755,8 @@
 	fmov	v15.d[1], xzr
 	ret
 .size	abi_test_clobber_v15_upper,.-abi_test_clobber_v15_upper
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/chacha/chacha-armv4.S b/third_party/boringssl/linux-arm/crypto/chacha/chacha-armv4.S
index 363aeee5..8de81965 100644
--- a/third_party/boringssl/linux-arm/crypto/chacha/chacha-armv4.S
+++ b/third_party/boringssl/linux-arm/crypto/chacha/chacha-armv4.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1488,6 +1487,8 @@
 .size	ChaCha20_neon,.-ChaCha20_neon
 .comm	OPENSSL_armcap_P,4,4
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/fipsmodule/aesv8-armx32.S b/third_party/boringssl/linux-arm/crypto/fipsmodule/aesv8-armx32.S
index 30c6525..e44cb77 100644
--- a/third_party/boringssl/linux-arm/crypto/fipsmodule/aesv8-armx32.S
+++ b/third_party/boringssl/linux-arm/crypto/fipsmodule/aesv8-armx32.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -795,6 +794,8 @@
 	ldmia	sp!,{r4,r5,r6,r7,r8,r9,r10,pc}
 .size	aes_hw_ctr32_encrypt_blocks,.-aes_hw_ctr32_encrypt_blocks
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/fipsmodule/armv4-mont.S b/third_party/boringssl/linux-arm/crypto/fipsmodule/armv4-mont.S
index 02968947..4cbd5fac 100644
--- a/third_party/boringssl/linux-arm/crypto/fipsmodule/armv4-mont.S
+++ b/third_party/boringssl/linux-arm/crypto/fipsmodule/armv4-mont.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -972,6 +971,8 @@
 .comm	OPENSSL_armcap_P,4,4
 .hidden	OPENSSL_armcap_P
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/fipsmodule/bsaes-armv7.S b/third_party/boringssl/linux-arm/crypto/fipsmodule/bsaes-armv7.S
index 69a8fca..45ae4b0 100644
--- a/third_party/boringssl/linux-arm/crypto/fipsmodule/bsaes-armv7.S
+++ b/third_party/boringssl/linux-arm/crypto/fipsmodule/bsaes-armv7.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1524,6 +1523,8 @@
 	@ out to retain a constant-time implementation.
 .size	bsaes_ctr32_encrypt_blocks,.-bsaes_ctr32_encrypt_blocks
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/fipsmodule/ghash-armv4.S b/third_party/boringssl/linux-arm/crypto/fipsmodule/ghash-armv4.S
index 0532695..57ca098 100644
--- a/third_party/boringssl/linux-arm/crypto/fipsmodule/ghash-armv4.S
+++ b/third_party/boringssl/linux-arm/crypto/fipsmodule/ghash-armv4.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -250,6 +249,8 @@
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,52,47,78,69,79,78,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S b/third_party/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
index 096dfb7..46fa9181 100644
--- a/third_party/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
+++ b/third_party/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -252,6 +251,8 @@
 .align	2
 .align	2
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/fipsmodule/sha1-armv4-large.S b/third_party/boringssl/linux-arm/crypto/fipsmodule/sha1-armv4-large.S
index 61deddf..4e7aa29 100644
--- a/third_party/boringssl/linux-arm/crypto/fipsmodule/sha1-armv4-large.S
+++ b/third_party/boringssl/linux-arm/crypto/fipsmodule/sha1-armv4-large.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1506,6 +1505,8 @@
 .comm	OPENSSL_armcap_P,4,4
 .hidden	OPENSSL_armcap_P
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/fipsmodule/sha256-armv4.S b/third_party/boringssl/linux-arm/crypto/fipsmodule/sha256-armv4.S
index aee04785..eaa5701e 100644
--- a/third_party/boringssl/linux-arm/crypto/fipsmodule/sha256-armv4.S
+++ b/third_party/boringssl/linux-arm/crypto/fipsmodule/sha256-armv4.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2834,6 +2833,8 @@
 .comm	OPENSSL_armcap_P,4,4
 .hidden	OPENSSL_armcap_P
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/fipsmodule/sha512-armv4.S b/third_party/boringssl/linux-arm/crypto/fipsmodule/sha512-armv4.S
index a06d41f..02489b8 100644
--- a/third_party/boringssl/linux-arm/crypto/fipsmodule/sha512-armv4.S
+++ b/third_party/boringssl/linux-arm/crypto/fipsmodule/sha512-armv4.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1889,6 +1888,8 @@
 .comm	OPENSSL_armcap_P,4,4
 .hidden	OPENSSL_armcap_P
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/fipsmodule/vpaes-armv7.S b/third_party/boringssl/linux-arm/crypto/fipsmodule/vpaes-armv7.S
index e5ad6ed..dd354ef4 100644
--- a/third_party/boringssl/linux-arm/crypto/fipsmodule/vpaes-armv7.S
+++ b/third_party/boringssl/linux-arm/crypto/fipsmodule/vpaes-armv7.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1231,6 +1230,8 @@
 	vldmia	sp!, {d8,d9,d10,d11,d12,d13,d14,d15}
 	ldmia	sp!, {r7,r8,r9,r10,r11, pc}	@ return
 .size	vpaes_ctr32_encrypt_blocks,.-vpaes_ctr32_encrypt_blocks
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-arm/crypto/test/trampoline-armv4.S b/third_party/boringssl/linux-arm/crypto/test/trampoline-armv4.S
index 9a73ba8..4649b043 100644
--- a/third_party/boringssl/linux-arm/crypto/test/trampoline-armv4.S
+++ b/third_party/boringssl/linux-arm/crypto/test/trampoline-armv4.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__arm__)
+#if !defined(OPENSSL_NO_ASM) && defined(__ARMEL__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -374,6 +373,8 @@
 	vmov	s31, r0
 	bx	lr
 .size	abi_test_clobber_d15,.-abi_test_clobber_d15
+#endif  // !OPENSSL_NO_ASM && defined(__ARMEL__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
-.section	.note.GNU-stack,"",%progbits
diff --git a/third_party/boringssl/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S b/third_party/boringssl/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S
index ea2a7f68..aa39b423 100644
--- a/third_party/boringssl/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S
+++ b/third_party/boringssl/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if !defined(OPENSSL_NO_ASM) && defined(__powerpc64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__powerpc64__) && defined(__ELF__)
 .machine	"any"
 
 .abiversion	2
@@ -3666,5 +3666,8 @@
 	blr	
 .long	0
 .byte	0,12,0x14,0,0,0,0,0
-#endif  // !OPENSSL_NO_ASM && __powerpc64__
-.section	.note.GNU-stack,"",@progbits
+#endif  // !OPENSSL_NO_ASM && __powerpc64__ && __ELF__
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S b/third_party/boringssl/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S
index 76b4e739..1af118d 100644
--- a/third_party/boringssl/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S
+++ b/third_party/boringssl/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if !defined(OPENSSL_NO_ASM) && defined(__powerpc64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__powerpc64__) && defined(__ELF__)
 .machine	"any"
 
 .abiversion	2
@@ -583,5 +583,8 @@
 .byte	71,72,65,83,72,32,102,111,114,32,80,111,119,101,114,73,83,65,32,50,46,48,55,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
-#endif  // !OPENSSL_NO_ASM && __powerpc64__
-.section	.note.GNU-stack,"",@progbits
+#endif  // !OPENSSL_NO_ASM && __powerpc64__ && __ELF__
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-ppc64le/crypto/test/trampoline-ppc.S b/third_party/boringssl/linux-ppc64le/crypto/test/trampoline-ppc.S
index 8166d31..dc58d55 100644
--- a/third_party/boringssl/linux-ppc64le/crypto/test/trampoline-ppc.S
+++ b/third_party/boringssl/linux-ppc64le/crypto/test/trampoline-ppc.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if !defined(OPENSSL_NO_ASM) && defined(__powerpc64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__powerpc64__) && defined(__ELF__)
 .machine	"any"
 .abiversion	2
 .text
@@ -1406,5 +1406,8 @@
 	bctr	
 .size	abi_test_clobber_lr,.-abi_test_clobber_lr
 
-#endif  // !OPENSSL_NO_ASM && __powerpc64__
-.section	.note.GNU-stack,"",@progbits
+#endif  // !OPENSSL_NO_ASM && __powerpc64__ && __ELF__
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86/crypto/chacha/chacha-x86.S b/third_party/boringssl/linux-x86/crypto/chacha/chacha-x86.S
index 0ae7a4bb..17d280d 100644
--- a/third_party/boringssl/linux-x86/crypto/chacha/chacha-x86.S
+++ b/third_party/boringssl/linux-x86/crypto/chacha/chacha-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -971,5 +977,8 @@
 .byte	44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32
 .byte	60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111
 .byte	114,103,62,0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/aesni-x86.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/aesni-x86.S
index 00a6ec2..34e1e43 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/aesni-x86.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/aesni-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2509,5 +2515,8 @@
 .byte	83,45,78,73,44,32,67,82,89,80,84,79,71,65,77,83
 .byte	32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115
 .byte	115,108,46,111,114,103,62,0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/bn-586.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/bn-586.S
index 638c036..d3b83f89 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/bn-586.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/bn-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -993,5 +999,8 @@
 	popl	%ebp
 	ret
 .size	bn_sub_words,.-.L_bn_sub_words_begin
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/co-586.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/co-586.S
index f1e67caf..bb75ab4c 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/co-586.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/co-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1262,5 +1268,8 @@
 	popl	%esi
 	ret
 .size	bn_sqr_comba4,.-.L_bn_sqr_comba4_begin
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-ssse3-x86.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-ssse3-x86.S
index 840e438..19fd370 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-ssse3-x86.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-ssse3-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -290,5 +296,8 @@
 .align	16
 .Llow4_mask:
 .long	252645135,252645135,252645135,252645135
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-x86.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-x86.S
index 13b9445..e912e86 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-x86.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -326,5 +332,8 @@
 .byte	82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112
 .byte	112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62
 .byte	0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/md5-586.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/md5-586.S
index 5887234..d28e1f4 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/md5-586.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/md5-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -684,5 +690,8 @@
 	popl	%esi
 	ret
 .size	md5_block_asm_data_order,.-.L_md5_block_asm_data_order_begin
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/sha1-586.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/sha1-586.S
index e224da4d..b63882db 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/sha1-586.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/sha1-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -3804,5 +3810,8 @@
 .byte	102,111,114,109,32,102,111,114,32,120,56,54,44,32,67,82
 .byte	89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112
 .byte	114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/sha256-586.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/sha256-586.S
index dcaf875..8eb929f 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/sha256-586.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/sha256-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -5563,5 +5569,8 @@
 	popl	%ebp
 	ret
 .size	sha256_block_data_order,.-.L_sha256_block_data_order_begin
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/sha512-586.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/sha512-586.S
index 282cddaa..eb0695ef 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/sha512-586.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/sha512-586.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2833,5 +2839,8 @@
 .byte	67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97
 .byte	112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103
 .byte	62,0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/vpaes-x86.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/vpaes-x86.S
index 66bd5f5..efd56e3 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/vpaes-x86.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/vpaes-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -704,5 +710,8 @@
 	popl	%ebp
 	ret
 .size	vpaes_cbc_encrypt,.-.L_vpaes_cbc_encrypt_begin
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/fipsmodule/x86-mont.S b/third_party/boringssl/linux-x86/crypto/fipsmodule/x86-mont.S
index 5de3251..2085caf 100644
--- a/third_party/boringssl/linux-x86/crypto/fipsmodule/x86-mont.S
+++ b/third_party/boringssl/linux-x86/crypto/fipsmodule/x86-mont.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -480,5 +486,8 @@
 .byte	54,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121
 .byte	32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46
 .byte	111,114,103,62,0
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86/crypto/test/trampoline-x86.S b/third_party/boringssl/linux-x86/crypto/test/trampoline-x86.S
index e7162dfa..3d560af 100644
--- a/third_party/boringssl/linux-x86/crypto/test/trampoline-x86.S
+++ b/third_party/boringssl/linux-x86/crypto/test/trampoline-x86.S
@@ -1,7 +1,13 @@
 // This file is generated from a similarly-named Perl script in the BoringSSL
 // source tree. Do not edit by hand.
 
-#if defined(__i386__)
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
+#endif
+#endif
+
+#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -202,5 +208,8 @@
 	pxor	%xmm7,%xmm7
 	ret
 .size	abi_test_clobber_xmm7,.-.L_abi_test_clobber_xmm7_begin
+#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && defined(__ELF__)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-.section	.note.GNU-stack,"",@progbits
diff --git a/third_party/boringssl/linux-x86_64/crypto/chacha/chacha-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/chacha/chacha-x86_64.S
index b862f4e..45eba8a 100644
--- a/third_party/boringssl/linux-x86_64/crypto/chacha/chacha-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/chacha/chacha-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1630,4 +1630,7 @@
 .cfi_endproc	
 .size	ChaCha20_8x,.-ChaCha20_8x
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
index 2e41e91f..f3f7334 100644
--- a/third_party/boringssl/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -3076,4 +3076,7 @@
 .cfi_endproc	
 .size	aes256gcmsiv_kdf, .-aes256gcmsiv_kdf
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S b/third_party/boringssl/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
index 2f3f641a..584672c 100644
--- a/third_party/boringssl/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -8919,4 +8919,7 @@
 .cfi_endproc	
 .size	chacha20_poly1305_seal_avx2, .-chacha20_poly1305_seal_avx2
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
index b28f7f8..2954ed2 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -305,6 +305,9 @@
 	vpaddb	%xmm2,%xmm1,%xmm0
 	movq	%r13,112+8(%rsp)
 	leaq	96(%rdi),%rdi
+
+	prefetcht0	512(%rdi)
+	prefetcht0	576(%rdi)
 	vaesenclast	%xmm5,%xmm11,%xmm11
 	vpaddb	%xmm2,%xmm0,%xmm5
 	movq	%r12,120+8(%rsp)
@@ -849,4 +852,7 @@
 .byte	65,69,83,45,78,73,32,71,67,77,32,109,111,100,117,108,101,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	64
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S
index 2d4654f..d4128105 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2503,4 +2503,7 @@
 .byte	65,69,83,32,102,111,114,32,73,110,116,101,108,32,65,69,83,45,78,73,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	64
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S
index b5fbdc8..890300bb 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -424,4 +424,7 @@
 .Llow4_mask:
 .quad	0x0f0f0f0f0f0f0f0f, 0x0f0f0f0f0f0f0f0f
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S
index 91cea67..146dcf4a 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1124,4 +1124,7 @@
 .byte	71,72,65,83,72,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	64
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/md5-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/md5-x86_64.S
index 4f08207..88ba8b2 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/md5-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/md5-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -699,4 +699,7 @@
 .cfi_endproc	
 .size	md5_block_asm_data_order,.-md5_block_asm_data_order
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
index 655f1a2..67cd02b 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -4540,4 +4540,7 @@
 .cfi_endproc	
 .size	ecp_nistz256_point_add_affinex,.-ecp_nistz256_point_add_affinex
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S
index cf056e3e..5c0eaf7 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -340,4 +340,7 @@
 
 .size	beeu_mod_inverse_vartime, .-beeu_mod_inverse_vartime
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S
index b392637..44eb652 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -60,4 +60,7 @@
 .cfi_endproc	
 .size	CRYPTO_rdrand_multiple8_buf,.-CRYPTO_rdrand_multiple8_buf
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S
index 0f8a978..738a578 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1746,4 +1746,7 @@
 .long	4,4,4,4, 4,4,4,4
 .align	64
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S
index cf2e7bc..34b8e3f 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -5465,4 +5465,7 @@
 .byte	83,72,65,49,32,98,108,111,99,107,32,116,114,97,110,115,102,111,114,109,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	64
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S
index 6ce216f24..78ae64d0 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -4181,4 +4181,7 @@
 .cfi_endproc	
 .size	sha256_block_data_order_avx,.-sha256_block_data_order_avx
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S
index 45a58a1..a89ea1a 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -2989,4 +2989,7 @@
 .cfi_endproc	
 .size	sha512_block_data_order_avx,.-sha512_block_data_order_avx
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S
index b651713..82b8289 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1130,4 +1130,7 @@
 .align	64
 .size	_vpaes_consts,.-_vpaes_consts
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont.S
index e39b5ca..6dfb885 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1257,4 +1257,7 @@
 .byte	77,111,110,116,103,111,109,101,114,121,32,77,117,108,116,105,112,108,105,99,97,116,105,111,110,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	16
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S
index a4591ed..a983481f 100644
--- a/third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S
+++ b/third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -3622,4 +3622,7 @@
 .long	2,2, 2,2
 .byte	77,111,110,116,103,111,109,101,114,121,32,77,117,108,116,105,112,108,105,99,97,116,105,111,110,32,119,105,116,104,32,115,99,97,116,116,101,114,47,103,97,116,104,101,114,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/linux-x86_64/crypto/test/trampoline-x86_64.S b/third_party/boringssl/linux-x86_64/crypto/test/trampoline-x86_64.S
index b7d6101b..3abbbb0 100644
--- a/third_party/boringssl/linux-x86_64/crypto/test/trampoline-x86_64.S
+++ b/third_party/boringssl/linux-x86_64/crypto/test/trampoline-x86_64.S
@@ -7,7 +7,7 @@
 #endif
 #endif
 
-#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(__x86_64__) && !defined(OPENSSL_NO_ASM) && defined(__ELF__)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -515,4 +515,7 @@
 	.byte	0xf3,0xc3
 .size	abi_test_set_direction_flag,.-abi_test_set_direction_flag
 #endif
-.section	.note.GNU-stack,"",@progbits
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
+#endif
diff --git a/third_party/boringssl/win-aarch64/crypto/chacha/chacha-armv8.S b/third_party/boringssl/win-aarch64/crypto/chacha/chacha-armv8.S
index 1e4947a..8a815438 100644
--- a/third_party/boringssl/win-aarch64/crypto/chacha/chacha-armv8.S
+++ b/third_party/boringssl/win-aarch64/crypto/chacha/chacha-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1996,5 +1995,8 @@
 	AARCH64_VALIDATE_LINK_REGISTER
 	ret
 
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S b/third_party/boringssl/win-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S
index 9f30332..8c22c4d 100644
--- a/third_party/boringssl/win-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S
+++ b/third_party/boringssl/win-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -3021,5 +3020,8 @@
 	b	Lopen_128_hash_64
 .cfi_endproc
 
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/aesv8-armx64.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/aesv8-armx64.S
index 62993f9..5afdfaba 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/aesv8-armx64.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/aesv8-armx64.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -809,5 +808,8 @@
 	ret
 
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/armv8-mont.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/armv8-mont.S
index e1bee28..9b4a9af 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/armv8-mont.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/armv8-mont.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1437,5 +1436,8 @@
 .byte	77,111,110,116,103,111,109,101,114,121,32,77,117,108,116,105,112,108,105,99,97,116,105,111,110,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	4
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/ghash-neon-armv8.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/ghash-neon-armv8.S
index 6881d0917..bee5b0e 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/ghash-neon-armv8.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/ghash-neon-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -347,5 +346,8 @@
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,100,101,114,105,118,101,100,32,102,114,111,109,32,65,82,77,118,52,32,118,101,114,115,105,111,110,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index ccf3761..1c463b3 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -579,5 +578,8 @@
 .align	2
 .align	2
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S
index d84e536c..a85f6405 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1748,5 +1747,8 @@
 
 	ret
 
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S
index 0a3121f..db796f0d 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -315,5 +314,8 @@
 	AARCH64_VALIDATE_LINK_REGISTER
 	ret
 
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha1-armv8.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha1-armv8.S
index 3c9e4e46..8f528ac 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha1-armv8.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha1-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1237,5 +1236,8 @@
 .byte	83,72,65,49,32,98,108,111,99,107,32,116,114,97,110,115,102,111,114,109,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha256-armv8.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha256-armv8.S
index 15970afc..9af62ec5 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha256-armv8.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha256-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1214,5 +1213,8 @@
 	ret
 
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha512-armv8.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha512-armv8.S
index b2b5a7e..8fac36e 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha512-armv8.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/sha512-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1616,5 +1615,8 @@
 	ret
 
 #endif
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/fipsmodule/vpaes-armv8.S b/third_party/boringssl/win-aarch64/crypto/fipsmodule/vpaes-armv8.S
index c97ec12..bba4e86f 100644
--- a/third_party/boringssl/win-aarch64/crypto/fipsmodule/vpaes-armv8.S
+++ b/third_party/boringssl/win-aarch64/crypto/fipsmodule/vpaes-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -1268,5 +1267,8 @@
 	AARCH64_VALIDATE_LINK_REGISTER
 	ret
 
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-aarch64/crypto/test/trampoline-armv8.S b/third_party/boringssl/win-aarch64/crypto/test/trampoline-armv8.S
index 4e17d4b3..5dbfcfc 100644
--- a/third_party/boringssl/win-aarch64/crypto/test/trampoline-armv8.S
+++ b/third_party/boringssl/win-aarch64/crypto/test/trampoline-armv8.S
@@ -8,8 +8,7 @@
 #define OPENSSL_NO_ASM
 #endif
 
-#if !defined(OPENSSL_NO_ASM)
-#if defined(__aarch64__)
+#if !defined(OPENSSL_NO_ASM) && defined(__AARCH64EL__) && defined(_WIN32)
 #if defined(BORINGSSL_PREFIX)
 #include <boringssl_prefix_symbols_asm.h>
 #endif
@@ -756,5 +755,8 @@
 	fmov	v15.d[1], xzr
 	ret
 
+#endif  // !OPENSSL_NO_ASM && defined(__AARCH64EL__) && defined(_WIN32)
+#if defined(__ELF__)
+// See https://www.airs.com/blog/archives/518.
+.section .note.GNU-stack,"",%progbits
 #endif
-#endif  // !OPENSSL_NO_ASM
diff --git a/third_party/boringssl/win-x86/crypto/chacha/chacha-x86.asm b/third_party/boringssl/win-x86/crypto/chacha/chacha-x86.asm
index 34393af..52c1f67 100644
--- a/third_party/boringssl/win-x86/crypto/chacha/chacha-x86.asm
+++ b/third_party/boringssl/win-x86/crypto/chacha/chacha-x86.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -973,3 +974,4 @@
 db	114,103,62,0
 segment	.bss
 common	_OPENSSL_ia32cap_P 16
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/aesni-x86.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/aesni-x86.asm
index 9ea1375..567939e 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/aesni-x86.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/aesni-x86.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -2459,3 +2460,4 @@
 db	115,108,46,111,114,103,62,0
 segment	.bss
 common	_OPENSSL_ia32cap_P 16
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/bn-586.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/bn-586.asm
index 4d1b793a..485af2e 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/bn-586.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/bn-586.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -975,3 +976,4 @@
 	ret
 segment	.bss
 common	_OPENSSL_ia32cap_P 16
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/co-586.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/co-586.asm
index 7c2afe83..e17bfc6 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/co-586.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/co-586.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -1256,3 +1257,4 @@
 	pop	edi
 	pop	esi
 	ret
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/ghash-ssse3-x86.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/ghash-ssse3-x86.asm
index e0192fc..d2b34a57 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/ghash-ssse3-x86.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/ghash-ssse3-x86.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -290,3 +291,4 @@
 align	16
 L$low4_mask:
 dd	252645135,252645135,252645135,252645135
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/ghash-x86.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/ghash-x86.asm
index 3703cb5a..e1b953e6 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/ghash-x86.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/ghash-x86.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -323,3 +324,4 @@
 db	82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112
 db	112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62
 db	0
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/md5-586.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/md5-586.asm
index e09bd0c2..327409203 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/md5-586.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/md5-586.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -687,3 +688,4 @@
 	pop	edi
 	pop	esi
 	ret
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/sha1-586.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/sha1-586.asm
index 4b05c9d..859edf1 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/sha1-586.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/sha1-586.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -3804,3 +3805,4 @@
 db	114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 segment	.bss
 common	_OPENSSL_ia32cap_P 16
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/sha256-586.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/sha256-586.asm
index 5d6661d0..1d8c3f3 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/sha256-586.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/sha256-586.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -5569,3 +5570,4 @@
 	ret
 segment	.bss
 common	_OPENSSL_ia32cap_P 16
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/sha512-586.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/sha512-586.asm
index f2c47a7..435346f 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/sha512-586.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/sha512-586.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -2839,3 +2840,4 @@
 db	62,0
 segment	.bss
 common	_OPENSSL_ia32cap_P 16
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/vpaes-x86.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/vpaes-x86.asm
index 49f8866..f5e9cd27 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/vpaes-x86.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/vpaes-x86.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -672,3 +673,4 @@
 	pop	ebx
 	pop	ebp
 	ret
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/fipsmodule/x86-mont.asm b/third_party/boringssl/win-x86/crypto/fipsmodule/x86-mont.asm
index 14aa988..6cac36a 100644
--- a/third_party/boringssl/win-x86/crypto/fipsmodule/x86-mont.asm
+++ b/third_party/boringssl/win-x86/crypto/fipsmodule/x86-mont.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -483,3 +484,4 @@
 db	111,114,103,62,0
 segment	.bss
 common	_OPENSSL_ia32cap_P 16
+%endif
diff --git a/third_party/boringssl/win-x86/crypto/test/trampoline-x86.asm b/third_party/boringssl/win-x86/crypto/test/trampoline-x86.asm
index 5fb72c7..36e3ad71 100644
--- a/third_party/boringssl/win-x86/crypto/test/trampoline-x86.asm
+++ b/third_party/boringssl/win-x86/crypto/test/trampoline-x86.asm
@@ -4,6 +4,7 @@
 %ifdef BORINGSSL_PREFIX
 %include "boringssl_prefix_symbols_nasm.inc"
 %endif
+%ifidn __OUTPUT_FORMAT__, win32
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
@@ -154,3 +155,4 @@
 L$_abi_test_clobber_xmm7_begin:
 	pxor	xmm7,xmm7
 	ret
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/chacha/chacha-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/chacha/chacha-x86_64.asm
index a3c2938..ba59f50 100644
--- a/third_party/boringssl/win-x86_64/crypto/chacha/chacha-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/chacha/chacha-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -1924,3 +1925,4 @@
 DB	9,0,0,0
 	DD	full_handler wrt ..imagebase
 	DD	$L$8x_body wrt ..imagebase,$L$8x_epilogue wrt ..imagebase
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm
index e711826..3f1b472c 100644
--- a/third_party/boringssl/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -3275,3 +3276,4 @@
 	DB	0F3h,0C3h		;repret
 
 $L$SEH_end_aes256gcmsiv_kdf:
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm b/third_party/boringssl/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm
index 7e3d6dd..dbb887e 100644
--- a/third_party/boringssl/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -8944,3 +8945,4 @@
 	jmp	NEAR $L$seal_sse_tail_16
 
 
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm
index 2b51a2684..fb6b9c1 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -304,6 +305,9 @@
 	vpaddb	xmm0,xmm1,xmm2
 	mov	QWORD[((112+8))+rsp],r13
 	lea	rdi,[96+rdi]
+
+	prefetcht0	[512+rdi]
+	prefetcht0	[576+rdi]
 	vaesenclast	xmm11,xmm11,xmm5
 	vpaddb	xmm5,xmm0,xmm2
 	mov	QWORD[((120+8))+rsp],r12
@@ -1031,3 +1035,4 @@
 DB	9,0,0,0
 	DD	gcm_se_handler wrt ..imagebase
 	DD	$L$gcm_enc_body wrt ..imagebase,$L$gcm_enc_abort wrt ..imagebase
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm
index 342c152..7041af5d 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -2804,3 +2805,4 @@
 $L$SEH_info_key:
 DB	0x01,0x04,0x01,0x00
 DB	0x04,0x02,0x00,0x00
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.asm
index 434ba10e..3395840 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -493,3 +494,4 @@
 
 DB	$L$ghash_seh_allocstack-$L$ghash_seh_begin
 DB	98
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm
index 194ea8d..3cbcfeb 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -1219,3 +1220,4 @@
 DB	0x0c,0x78,0x01,0x00
 DB	0x08,0x68,0x00,0x00
 DB	0x04,0x01,0x15,0x00
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/md5-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/md5-x86_64.asm
index 646201b..456b516 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/md5-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/md5-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -794,3 +795,4 @@
 $L$SEH_info_md5_block_asm_data_order:
 DB	9,0,0,0
 	DD	se_handler wrt ..imagebase
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm
index 215f5d2..c58ea46 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -4982,3 +4983,4 @@
 	DD	full_handler wrt ..imagebase
 	DD	$L$add_affinex_body wrt ..imagebase,$L$add_affinex_epilogue wrt ..imagebase
 	DD	32*15+56,0
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.asm
index 563699d..915cb51 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -337,3 +338,4 @@
 
 
 $L$SEH_end_beeu_mod_inverse_vartime:
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm
index 89b91de1..f25a6d5 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -56,3 +57,4 @@
 	DB	0F3h,0C3h		;repret
 
 
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm
index 74e2705..3ad762e 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -1970,3 +1971,4 @@
 DB	0x09,0x68,0x00,0x00
 DB	0x04,0x01,0x15,0x00
 DB	0x00,0xb3,0x00,0x00
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm
index 1654df1..0e0d3a2 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -5772,3 +5773,4 @@
 DB	9,0,0,0
 	DD	ssse3_handler wrt ..imagebase
 	DD	$L$prologue_avx2 wrt ..imagebase,$L$epilogue_avx2 wrt ..imagebase
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm
index 49be6f6..2f4e22a 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -4414,3 +4415,4 @@
 DB	9,0,0,0
 	DD	se_handler wrt ..imagebase
 	DD	$L$prologue_avx wrt ..imagebase,$L$epilogue_avx wrt ..imagebase
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm
index 33dc2c2..3668b46 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -3137,3 +3138,4 @@
 DB	9,0,0,0
 	DD	se_handler wrt ..imagebase
 	DD	$L$prologue_avx wrt ..imagebase,$L$epilogue_avx wrt ..imagebase
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm
index ccfc870a..ca8939d98 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -1470,3 +1471,4 @@
 DB	9,0,0,0
 	DD	se_handler wrt ..imagebase
 	DD	$L$ctr32_body wrt ..imagebase,$L$ctr32_epilogue wrt ..imagebase
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont.asm
index d6d8bdd..7e0cf1b 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -1479,3 +1480,4 @@
 	DD	sqr_handler wrt ..imagebase
 	DD	$L$mulx4x_prologue wrt ..imagebase,$L$mulx4x_body wrt ..imagebase,$L$mulx4x_epilogue wrt ..imagebase
 ALIGN	8
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm b/third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm
index e487819..c4e7fd88 100644
--- a/third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm
+++ b/third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -3847,3 +3848,4 @@
 DB	0x0b,0x01,0x21,0x00
 DB	0x04,0xa3,0x00,0x00
 ALIGN	8
+%endif
diff --git a/third_party/boringssl/win-x86_64/crypto/test/trampoline-x86_64.asm b/third_party/boringssl/win-x86_64/crypto/test/trampoline-x86_64.asm
index 9900669..b5e60f704 100644
--- a/third_party/boringssl/win-x86_64/crypto/test/trampoline-x86_64.asm
+++ b/third_party/boringssl/win-x86_64/crypto/test/trampoline-x86_64.asm
@@ -1,6 +1,7 @@
 ; This file is generated from a similarly-named Perl script in the BoringSSL
 ; source tree. Do not edit by hand.
 
+%ifidn __OUTPUT_FORMAT__, win64
 default	rel
 %define XMMWORD
 %define YMMWORD
@@ -680,3 +681,4 @@
 
 DB	$L$abi_test_bad_unwind_epilog_seh_push_r12-$L$abi_test_bad_unwind_epilog_seh_begin
 DB	192
+%endif
diff --git a/third_party/jdk/3pp/3pp.pb b/third_party/jdk/3pp/3pp.pb
index 9125264..7271e85d 100644
--- a/third_party/jdk/3pp/3pp.pb
+++ b/third_party/jdk/3pp/3pp.pb
@@ -4,13 +4,7 @@
 
 create {
   source {
-    url {
-      # See the link "[Binaries]" in
-      # https://wiki.openjdk.java.net/display/JDKUpdates/JDK11u#JDK11u-Releases
-      download_url: "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15%2B10/OpenJDK11U-jdk_x64_linux_hotspot_11.0.15_10.tar.gz"
-      version: "11.0.15+10"
-    }
-    patch_version: 'cr0'
+    script { name: "fetch.py" }
     unpack_archive: true
     subdir: 'current'
   }
diff --git a/third_party/jdk/3pp/fetch.py b/third_party/jdk/3pp/fetch.py
new file mode 100755
index 0000000..4a08bd6
--- /dev/null
+++ b/third_party/jdk/3pp/fetch.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# 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 argparse
+import hashlib
+import json
+import pathlib
+import urllib.request
+
+# JDK11 releases can be found from the following urls:
+# https://wiki.openjdk.java.net/display/JDKUpdates/JDK11u#JDK11u-Releases
+# https://github.com/adoptium/temurin11-binaries/releases/
+
+_LATEST_URL = 'https://api.github.com/repos/adoptium/temurin11-binaries/releases/latest'
+
+
+def get_latest_release():
+    """Returns data of the latest release."""
+    return json.load(urllib.request.urlopen(_LATEST_URL))
+
+
+def do_latest():
+    # Make the version change every time this file changes.
+    md5 = hashlib.md5()
+    md5.update(pathlib.Path(__file__).read_bytes())
+    file_hash = md5.hexdigest()[:10]
+    release = get_latest_release()
+    print('{}.{}'.format(release['name'], file_hash))
+
+
+def do_get_url():
+    release = get_latest_release()
+    # version without file hash.
+    version = release['name']
+    release_url = None
+    for asset in release['assets']:
+        name = asset['name']
+        if 'jdk_x64_linux_' in name and name.endswith('.tar.gz'):
+            if release_url is not None:
+                raise Exception(
+                    'Multiple valid assets found: \n{}\n{}\n'.format(
+                        release_url, asset['browser_download_url']))
+            release_url = asset['browser_download_url']
+
+    if release_url is None:
+        raise Exception(f'jdk_x64_linux asset not found for {version}.')
+
+    partial_manifest = {
+        'url': [release_url],
+        'ext': '.tar.gz',
+    }
+    print(json.dumps(partial_manifest))
+
+
+def main():
+    ap = argparse.ArgumentParser()
+    sub = ap.add_subparsers(required=True)
+
+    latest = sub.add_parser("latest")
+    latest.set_defaults(func=do_latest)
+
+    download = sub.add_parser("get_url")
+    download.set_defaults(func=do_get_url)
+
+    opts = ap.parse_args()
+    opts.func()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/third_party/jdk/README.chromium b/third_party/jdk/README.chromium
index ae8c97ab..97be8f5 100644
--- a/third_party/jdk/README.chromium
+++ b/third_party/jdk/README.chromium
@@ -12,15 +12,10 @@
 
 How to update:
 For the `jdk` directory:
-1. Find the specific version you want to update from
-   https://wiki.openjdk.java.net/display/JDKUpdates/JDK11u#JDK11u-Releases
-2. Click the link "[Binaries]" for the specific version.
-3. Copy the link address of the asset "OpenJDK11U-jre_x64_linux_<specific_version>.tar.gz"
-4. Update the "download_url" and "version" in 3pp/3pp.pb with the desired values.
-5. Upload and land the CL.
-6. Wait for the 3pp packager (https://ci.chromium.org/p/chromium/builders/ci/3pp-linux-amd64-packager)
-   to create the new instance at https://chrome-infra-packages.appspot.com/p/chromium/third_party/jdk/+/
-7. Update instance ID in //DEPS
+1. 3pp automatically uploads new releases at https://chrome-infra-packages.appspot.com/p/chromium/third_party/jdk/+/
+2. Each instance is tagged with version:2@<jdk version>.<md5 hash>.
+3. Choose the cipd instance you wish to roll.
+7. Update the cipd instance ID in //DEPS
 
 Local Modifications:
 None
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn
index fc9fe107..9570983 100644
--- a/third_party/nearby/BUILD.gn
+++ b/third_party/nearby/BUILD.gn
@@ -497,8 +497,12 @@
     "src/internal/platform/runnable.h",
     "src/internal/platform/socket.h",
     "src/internal/platform/types.h",
+    "src/internal/platform/wifi_credential.h",
   ]
-  public_deps = [ "//third_party/abseil-cpp:absl" ]
+  public_deps = [
+    ":connections_enums_proto",
+    "//third_party/abseil-cpp:absl",
+  ]
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
 }
diff --git a/third_party/webgpu-cts/ts_sources.txt b/third_party/webgpu-cts/ts_sources.txt
index 07a2e99..dabcba3d 100644
--- a/third_party/webgpu-cts/ts_sources.txt
+++ b/third_party/webgpu-cts/ts_sources.txt
@@ -416,6 +416,7 @@
 src/webgpu/shader/execution/expression/unary/unary.ts
 src/webgpu/shader/execution/expression/unary/bool_logical.spec.ts
 src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts
+src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts
 src/webgpu/shader/execution/memory_model/memory_model_setup.ts
 src/webgpu/shader/execution/memory_model/atomicity.spec.ts
 src/webgpu/shader/execution/memory_model/barrier.spec.ts
@@ -442,7 +443,6 @@
 src/webgpu/shader/validation/shader_io/interpolate.spec.ts
 src/webgpu/shader/validation/shader_io/invariant.spec.ts
 src/webgpu/shader/validation/shader_io/locations.spec.ts
-src/webgpu/shader/validation/shader_io/shareable_types.spec.ts
 src/webgpu/shader/validation/static_assert/static_assert.spec.ts
 src/webgpu/util/color_space_conversion.ts
 src/webgpu/util/copy_to_texture.ts
diff --git a/tools/android/touch_replay/touch_replay.cc b/tools/android/touch_replay/touch_replay.cc
index cb04b97..a1335ff 100644
--- a/tools/android/touch_replay/touch_replay.cc
+++ b/tools/android/touch_replay/touch_replay.cc
@@ -46,7 +46,7 @@
   int code;
   int value;
 
-  uint64_t InMilliseconds() {
+  uint64_t InMilliseconds() const {
     return base::checked_cast<uint64_t>(sec) * 1000 +
            base::checked_cast<uint64_t>(usec / 1000);
   }
@@ -229,6 +229,15 @@
   }
 }
 
+void SleepUntil(uint64_t record_time, int64_t timebase_offset_ms) {
+  int64_t current_time =
+      base::TimeTicks::Now().ToUptimeMillis() - timebase_offset_ms;
+  int64_t sleep_millis = record_time - current_time;
+  if (sleep_millis > 0) {
+    SleepMillis(sleep_millis);
+  }
+}
+
 bool RecordForever(const base::FilePath& file_path) {
   // Open the dump file for writing.
   base::File dump_file(file_path,
@@ -298,6 +307,20 @@
   }
 }
 
+void ValidateRecords(const std::vector<TouchInputEventRecord>& records) {
+  // Ensure records are monotonically non-decreasing.
+  uint64_t last_ms = 0;
+  for (const TouchInputEventRecord& record : records) {
+    uint64_t current_ms = record.InMilliseconds();
+    if (current_ms < last_ms) {
+      LOG(ERROR) << "Log timestamps are required to be "
+                 << "monotonically non-decreasing";
+      abort();
+    }
+    last_ms = current_ms;
+  }
+}
+
 bool Replay(const base::FilePath& file_path) {
   // Open the dump file for reading.
   base::File dump_file(file_path,
@@ -337,6 +360,8 @@
     return false;
   }
 
+  ValidateRecords(records);
+
   // Open the device.
   base::FilePath device_path(device_name);
   std::unique_ptr<InputDevice> device =
@@ -352,24 +377,21 @@
   // emulating touch events often got delayed by an order of 1ms on modern
   // Android devices. Therefore, emulating with precision higher than 1ms
   // would be nontrivial.
-  uint64_t log_start_millis = records[0].InMilliseconds();
-  base::TimeTicks replay_start_ticks = base::TimeTicks::Now();
-  uint64_t current_millis = log_start_millis;
-  uint64_t next_millis;
-  int chunk_first = 0;
   int chunk_last = 0;
+  // Offset between our two timebases.
+  int64_t offset_ms =
+      base::TimeTicks::Now().ToUptimeMillis() - records[0].InMilliseconds();
   while (chunk_last < num_records) {
+    SleepUntil(records[chunk_last].InMilliseconds(), offset_ms);
+    uint64_t chunk_end_ms = base::TimeTicks::Now().ToUptimeMillis() - offset_ms;
+
+    int chunk_first = chunk_last;
     // Arrange the chunk. Clamping to millisecond intervals should be OK
     // because of the overall 1ms precision.
-    do {
-      next_millis = records[chunk_last].InMilliseconds();
-      if (next_millis < current_millis) {
-        LOG(ERROR) << "Log timestamps are required to be "
-                   << "monotonically non-decreasing";
-        abort();
-      }
+    while (chunk_last < num_records &&
+           records[chunk_last].InMilliseconds() <= chunk_end_ms) {
       chunk_last++;
-    } while (chunk_last < num_records && current_millis == next_millis);
+    }
 
     // Send all events from the chunk to the device without delays in between.
     device->SendEvents(records, chunk_first, chunk_last);
@@ -379,16 +401,6 @@
     for (int j = chunk_first; j < chunk_last; j++) {
       PrintProgressMarkers(j);
     }
-    chunk_first = chunk_last;
-
-    // Sleep until it is time for the next chunk.
-    uint64_t elapsed = base::checked_cast<uint64_t>(
-        (base::TimeTicks::Now() - replay_start_ticks).InMilliseconds());
-    uint64_t next_chunk_offset_millis = next_millis - log_start_millis;
-    if (elapsed < next_chunk_offset_millis) {
-      SleepMillis(next_chunk_offset_millis - elapsed);
-    }
-    current_millis = next_millis;
   }
   std::cout << std::endl;
   return true;
diff --git a/tools/grit/grit.py b/tools/grit/grit.py
index 5866da1..c6cbf41 100755
--- a/tools/grit/grit.py
+++ b/tools/grit/grit.py
@@ -6,7 +6,6 @@
 '''Bootstrapping for GRIT.
 '''
 
-from __future__ import print_function
 
 import os
 import sys
diff --git a/tools/grit/grit/__init__.py b/tools/grit/grit/__init__.py
index a704fa1..fa82d3a 100644
--- a/tools/grit/grit/__init__.py
+++ b/tools/grit/grit/__init__.py
@@ -5,7 +5,6 @@
 '''Package 'grit'
 '''
 
-from __future__ import print_function
 
 import os
 import sys
diff --git a/tools/grit/grit/clique.py b/tools/grit/grit/clique.py
index f1a24b02..01560f6 100644
--- a/tools/grit/grit/clique.py
+++ b/tools/grit/grit/clique.py
@@ -6,7 +6,6 @@
 collections of cliques (uber-cliques).
 '''
 
-from __future__ import print_function
 
 import re
 
@@ -20,7 +19,7 @@
 from grit import tclib
 
 
-class UberClique(object):
+class UberClique:
   '''A factory (NOT a singleton factory) for making cliques.  It has several
   methods for working with the cliques created using the factory.
   '''
@@ -195,8 +194,7 @@
     with the same ID.
     '''
     for cliques in self.cliques_.values():
-      for c in cliques:
-        yield c
+      yield from cliques
 
   def GenerateXtbParserCallback(self, lang, debug=False):
     '''Creates a callback function as required by grit.xtb_reader.Parse().
@@ -242,7 +240,7 @@
     return Callback
 
 
-class CustomType(object):
+class CustomType:
   '''A base class you should implement if you wish to specify a custom type
   for a message clique (i.e. custom validation and optional modification of
   translations).'''
@@ -274,7 +272,7 @@
     '''
     contents = translation.GetContent()
     for ix in range(len(contents)):
-      if (isinstance(contents[ix], six.string_types)):
+      if (isinstance(contents[ix], str)):
         contents[ix] = self.ModifyTextPart(lang, contents[ix])
 
 
@@ -299,7 +297,7 @@
              })
 
 
-class MessageClique(object):
+class MessageClique:
   '''A message along with all of its translations.  Also code to bring
   translations together with their original message.'''
 
diff --git a/tools/grit/grit/clique_unittest.py b/tools/grit/grit/clique_unittest.py
index 82e8b720..27cde7b 100755
--- a/tools/grit/grit/clique_unittest.py
+++ b/tools/grit/grit/clique_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.clique'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -15,7 +14,7 @@
 import re
 import unittest
 
-from six import StringIO
+from io import StringIO
 
 from grit import clique
 from grit import exception
@@ -32,8 +31,8 @@
                           tclib.Placeholder('USERNAME', '%s', 'Joi')])
     c = factory.MakeClique(msg)
 
-    self.failUnless(c.GetMessage() == msg)
-    self.failUnless(c.GetId() == msg.GetId())
+    self.assertTrue(c.GetMessage() == msg)
+    self.assertTrue(c.GetId() == msg.GetId())
 
     msg_fr = tclib.Translation(text='Bonjour USERNAME, comment ca va?',
                                id=msg.GetId(), placeholders=[
@@ -47,9 +46,9 @@
 
     # sort() sorts lists in-place and does not return them
     for lang in ('en', 'fr', 'de'):
-      self.failUnless(lang in c.clique)
+      self.assertTrue(lang in c.clique)
 
-    self.failUnless(c.MessageForLanguage('fr').GetRealContent() ==
+    self.assertTrue(c.MessageForLanguage('fr').GetRealContent() ==
                     msg_fr.GetRealContent())
 
     try:
@@ -58,11 +57,11 @@
     except:
       pass
 
-    self.failUnless(c.MessageForLanguage('zh-CN', True) != None)
+    self.assertTrue(c.MessageForLanguage('zh-CN', True) != None)
 
     rex = re.compile('fr|de|bingo')
-    self.failUnless(len(c.AllMessagesThatMatch(rex, False)) == 2)
-    self.failUnless(
+    self.assertTrue(len(c.AllMessagesThatMatch(rex, False)) == 2)
+    self.assertTrue(
         c.AllMessagesThatMatch(rex, True)[pseudo.PSEUDO_LANG] is not None)
 
   def testBestClique(self):
@@ -84,16 +83,16 @@
       text = msg.GetRealContent()
       description = msg.GetDescription()
       if text == 'Alfur':
-        self.failUnless(description == 'alfaholl')
+        self.assertTrue(description == 'alfaholl')
       elif text == 'Gryla':
-        self.failUnless(description == 'vondakerling')
+        self.assertTrue(description == 'vondakerling')
       elif text == 'Leppaludi':
-        self.failUnless(description == 'ID: IDS_LL')
-    self.failUnless(count_best_cliques == 5)
+        self.assertTrue(description == 'ID: IDS_LL')
+    self.assertTrue(count_best_cliques == 5)
 
   def testAllInUberClique(self):
     resources = grd_reader.Parse(
-        StringIO(u'''<?xml version="1.0" encoding="UTF-8"?>
+        StringIO('''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
   <release seq="3">
     <messages>
@@ -113,14 +112,14 @@
     for clique_list in resources.UberClique().cliques_.values():
       for clique in clique_list:
         content_list.append(clique.GetMessage().GetRealContent())
-    self.failUnless('Hello %s, how are you doing today?' in content_list)
-    self.failUnless('Jack "Black" Daniels' in content_list)
-    self.failUnless('Hello!' in content_list)
+    self.assertTrue('Hello %s, how are you doing today?' in content_list)
+    self.assertTrue('Jack "Black" Daniels' in content_list)
+    self.assertTrue('Hello!' in content_list)
 
   def testCorrectExceptionIfWrongEncodingOnResourceFile(self):
     '''This doesn't really belong in this unittest file, but what the heck.'''
     resources = grd_reader.Parse(
-        StringIO(u'''<?xml version="1.0" encoding="UTF-8"?>
+        StringIO('''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
   <release seq="3">
     <structures>
@@ -137,7 +136,7 @@
       tclib.Message(text='Hello USERNAME',
                     placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')]),
     ]
-    self.failUnless(messages[0].GetId() == messages[1].GetId())
+    self.assertTrue(messages[0].GetId() == messages[1].GetId())
 
     # Both of the above would share a translation.
     translation = tclib.Translation(id=messages[0].GetId(),
@@ -151,9 +150,9 @@
     for clq in cliques:
       clq.AddTranslation(translation, 'fr')
 
-    self.failUnless(cliques[0].MessageForLanguage('fr').GetRealContent() ==
+    self.assertTrue(cliques[0].MessageForLanguage('fr').GetRealContent() ==
                     'Bonjour $1')
-    self.failUnless(cliques[1].MessageForLanguage('fr').GetRealContent() ==
+    self.assertTrue(cliques[1].MessageForLanguage('fr').GetRealContent() ==
                     'Bonjour %s')
 
   def testMissingTranslations(self):
@@ -163,17 +162,17 @@
 
     cliques[1].MessageForLanguage('fr', False, True)
 
-    self.failUnless(not factory.HasMissingTranslations())
+    self.assertTrue(not factory.HasMissingTranslations())
 
     cliques[0].MessageForLanguage('de', False, False)
 
-    self.failUnless(factory.HasMissingTranslations())
+    self.assertTrue(factory.HasMissingTranslations())
 
     report = factory.MissingTranslationsReport()
-    self.failUnless(report.count('WARNING') == 1)
-    self.failUnless(report.count('8053599568341804890 "Goodbye" fr') == 1)
-    self.failUnless(report.count('ERROR') == 1)
-    self.failUnless(report.count('800120468867715734 "Hello" de') == 1)
+    self.assertTrue(report.count('WARNING') == 1)
+    self.assertTrue(report.count('8053599568341804890 "Goodbye" fr') == 1)
+    self.assertTrue(report.count('ERROR') == 1)
+    self.assertTrue(report.count('800120468867715734 "Hello" de') == 1)
 
   def testCustomTypes(self):
     factory = clique.UberClique()
@@ -191,22 +190,22 @@
       'grit.clique_unittest.DummyCustomType', clique.CustomType))
     translation = tclib.Translation(id=message.GetId(), text='Bilingo bolongo')
     c.AddTranslation(translation, 'fr')
-    self.failUnless(c.MessageForLanguage('fr').GetRealContent().startswith('jjj'))
+    self.assertTrue(c.MessageForLanguage('fr').GetRealContent().startswith('jjj'))
 
   def testWhitespaceMessagesAreNontranslateable(self):
     factory = clique.UberClique()
 
     message = tclib.Message(text=' \t')
     c = factory.MakeClique(message, translateable=True)
-    self.failIf(c.IsTranslateable())
+    self.assertFalse(c.IsTranslateable())
 
     message = tclib.Message(text='\n \n ')
     c = factory.MakeClique(message, translateable=True)
-    self.failIf(c.IsTranslateable())
+    self.assertFalse(c.IsTranslateable())
 
     message = tclib.Message(text='\n hello')
     c = factory.MakeClique(message, translateable=True)
-    self.failUnless(c.IsTranslateable())
+    self.assertTrue(c.IsTranslateable())
 
   def testEachCliqueKeptSorted(self):
     factory = clique.UberClique()
@@ -218,10 +217,10 @@
     clique_a = factory.MakeClique(msg_a, translateable=True)
     clique_c = factory.MakeClique(msg_c, translateable=True)
     clique_list = factory.cliques_[clique_a.GetId()]
-    self.failUnless(len(clique_list) == 3)
-    self.failUnless(clique_list[0] == clique_a)
-    self.failUnless(clique_list[1] == clique_b)
-    self.failUnless(clique_list[2] == clique_c)
+    self.assertTrue(len(clique_list) == 3)
+    self.assertTrue(clique_list[0] == clique_a)
+    self.assertTrue(clique_list[1] == clique_b)
+    self.assertTrue(clique_list[2] == clique_c)
 
   def testBestCliqueSortIsStable(self):
     factory = clique.UberClique()
@@ -236,19 +235,19 @@
     # Insert in an order that tests all outcomes.
     clique_no_description = factory.MakeClique(msg_no_description,
                                                translateable=True)
-    self.failUnless(factory.BestClique(clique_id) == clique_no_description)
+    self.assertTrue(factory.BestClique(clique_id) == clique_no_description)
     clique_id_description_b = factory.MakeClique(msg_id_description_b,
                                                  translateable=True)
-    self.failUnless(factory.BestClique(clique_id) == clique_id_description_b)
+    self.assertTrue(factory.BestClique(clique_id) == clique_id_description_b)
     clique_id_description_a = factory.MakeClique(msg_id_description_a,
                                                  translateable=True)
-    self.failUnless(factory.BestClique(clique_id) == clique_id_description_a)
+    self.assertTrue(factory.BestClique(clique_id) == clique_id_description_a)
     clique_description_y = factory.MakeClique(msg_description_y,
                                               translateable=True)
-    self.failUnless(factory.BestClique(clique_id) == clique_description_y)
+    self.assertTrue(factory.BestClique(clique_id) == clique_description_y)
     clique_description_x = factory.MakeClique(msg_description_x,
                                               translateable=True)
-    self.failUnless(factory.BestClique(clique_id) == clique_description_x)
+    self.assertTrue(factory.BestClique(clique_id) == clique_description_x)
 
 
 class DummyCustomType(clique.CustomType):
diff --git a/tools/grit/grit/constants.py b/tools/grit/grit/constants.py
index feafba73..3e02457 100644
--- a/tools/grit/grit/constants.py
+++ b/tools/grit/grit/constants.py
@@ -5,12 +5,11 @@
 '''Constant definitions for GRIT.
 '''
 
-from __future__ import print_function
 
 # This is the Icelandic noun meaning "grit" and is used to check that our
 # input files are in the correct encoding.  The middle character gets encoded
 # as two bytes in UTF-8, so this is sufficient to detect incorrect encoding.
-ENCODING_CHECK = u'm\u00f6l'
+ENCODING_CHECK = 'm\u00f6l'
 
 # A special language, translations into which are always "TTTTTT".
 CONSTANT_LANGUAGE = 'x_constant'
diff --git a/tools/grit/grit/exception.py b/tools/grit/grit/exception.py
index 72139c3..f6c73b2 100644
--- a/tools/grit/grit/exception.py
+++ b/tools/grit/grit/exception.py
@@ -5,7 +5,6 @@
 '''Exception types for GRIT.
 '''
 
-from __future__ import print_function
 
 class Base(Exception):
   '''A base exception that uses the class's docstring in addition to any
@@ -17,7 +16,7 @@
         msg = self.__doc__ + ': ' + msg
     else:
       msg = self.__doc__
-    super(Base, self).__init__(msg)
+    super().__init__(msg)
 
 
 class Parsing(Base):
diff --git a/tools/grit/grit/extern/BogoFP.py b/tools/grit/grit/extern/BogoFP.py
index 13edd09..82f7e70fea 100644
--- a/tools/grit/grit/extern/BogoFP.py
+++ b/tools/grit/grit/extern/BogoFP.py
@@ -9,7 +9,6 @@
     grit.py -h grit.extern.BogoFP xmb /tmp/foo
 """
 
-from __future__ import print_function
 
 import grit.extern.FP
 
diff --git a/tools/grit/grit/extern/FP.py b/tools/grit/grit/extern/FP.py
index 201856b..8ab1b6e5 100644
--- a/tools/grit/grit/extern/FP.py
+++ b/tools/grit/grit/extern/FP.py
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from __future__ import print_function
 
 try:
   import hashlib
diff --git a/tools/grit/grit/extern/tclib.py b/tools/grit/grit/extern/tclib.py
index c5d705e1..e243297 100644
--- a/tools/grit/grit/extern/tclib.py
+++ b/tools/grit/grit/extern/tclib.py
@@ -10,7 +10,6 @@
 # for creating Windows .rc and .h files.  These are the only parts needed by
 # the Chrome build process.
 
-from __future__ import print_function
 
 from grit.extern import FP
 
@@ -56,7 +55,7 @@
 # -----------------------------------------------------------
 # The Placeholder class represents a placeholder in a message.
 
-class Placeholder(object):
+class Placeholder:
   # String representation
   def __str__(self):
     return '%s, "%s", "%s"' % \
@@ -100,7 +99,7 @@
 # BaseMessage is the common parent class of Message and Translation.
 # It is not meant for direct use.
 
-class BaseMessage(object):
+class BaseMessage:
   # Three types of message construction is supported. If the message text is a
   # simple string with no dynamic content, you can pass it to the constructor
   # as the "text" parameter. Otherwise, you can omit "text" and assemble the
diff --git a/tools/grit/grit/format/android_xml.py b/tools/grit/grit/format/android_xml.py
index c7c7764..3d7f75b 100644
--- a/tools/grit/grit/format/android_xml.py
+++ b/tools/grit/grit/format/android_xml.py
@@ -59,7 +59,6 @@
   </plurals>
 """
 
-from __future__ import print_function
 
 import os
 import re
@@ -81,7 +80,7 @@
 
 # Most strings are output as a <string> element. Note the double quotes
 # around the value to preserve whitespace.
-_STRING_TEMPLATE = u'<string name="%s">"%s"</string>\n'
+_STRING_TEMPLATE = '<string name="%s">"%s"</string>\n'
 
 # Some strings are output as a <plurals> element.
 _PLURALS_TEMPLATE = '<plurals name="%s">\n%s</plurals>\n'
diff --git a/tools/grit/grit/format/android_xml_unittest.py b/tools/grit/grit/format/android_xml_unittest.py
index effd7df..5aa43cd 100755
--- a/tools/grit/grit/format/android_xml_unittest.py
+++ b/tools/grit/grit/format/android_xml_unittest.py
@@ -5,7 +5,6 @@
 
 """Unittest for android_xml.py."""
 
-from __future__ import print_function
 
 import os
 import sys
@@ -14,7 +13,7 @@
 if __name__ == '__main__':
   sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
 
-from six import StringIO
+from io import StringIO
 
 from grit import util
 from grit.format import android_xml
@@ -139,7 +138,7 @@
     self.assertTrue(android_xml.ShouldOutputNode(msg_world, tagged_only=False))
 
 
-class DummyOutput(object):
+class DummyOutput:
 
   def __init__(self, type, language):
     self.type = type
diff --git a/tools/grit/grit/format/c_format_unittest.py b/tools/grit/grit/format/c_format_unittest.py
index 099ece3..8c0f6b3 100755
--- a/tools/grit/grit/format/c_format_unittest.py
+++ b/tools/grit/grit/format/c_format_unittest.py
@@ -21,7 +21,7 @@
 class CFormatUnittest(unittest.TestCase):
 
   def testMessages(self):
-    root = util.ParseGrdForUnittest(u"""
+    root = util.ParseGrdForUnittest("""
     <messages>
       <message name="IDS_QUESTIONS">Do you want to play questions?</message>
       <message name="IDS_QUOTES">
@@ -41,7 +41,7 @@
     buf = io.StringIO()
     build.RcBuilder.ProcessNode(root, DummyOutput('c_format', 'en'), buf)
     output = util.StripBlankLinesAndComments(buf.getvalue())
-    self.assertEqual(u"""\
+    self.assertEqual("""\
 #include "resource.h"
 const char* GetString(int id) {
   switch (id) {
@@ -59,7 +59,7 @@
 }""", output)
 
 
-class DummyOutput(object):
+class DummyOutput:
 
   def __init__(self, type, language):
     self.type = type
diff --git a/tools/grit/grit/format/chrome_messages_json.py b/tools/grit/grit/format/chrome_messages_json.py
index d5e14592..1459b21 100644
--- a/tools/grit/grit/format/chrome_messages_json.py
+++ b/tools/grit/grit/format/chrome_messages_json.py
@@ -5,7 +5,6 @@
 """Formats as a .json file that can be used to localize Google Chrome
 extensions."""
 
-from __future__ import print_function
 
 from json import JSONEncoder
 
diff --git a/tools/grit/grit/format/chrome_messages_json_unittest.py b/tools/grit/grit/format/chrome_messages_json_unittest.py
index 840dedf..fac8e9f 100755
--- a/tools/grit/grit/format/chrome_messages_json_unittest.py
+++ b/tools/grit/grit/format/chrome_messages_json_unittest.py
@@ -26,7 +26,7 @@
   maxDiff = None
 
   def testMessages(self):
-    root = util.ParseGrdForUnittest(u"""
+    root = util.ParseGrdForUnittest("""
     <messages>
       <message name="IDS_SIMPLE_MESSAGE">
               Simple message.
@@ -62,7 +62,7 @@
     build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'en'),
                                 buf)
     output = buf.getvalue()
-    test = u"""
+    test = """
 {
   "SIMPLE_MESSAGE": {
     "message": "Simple message."
@@ -116,7 +116,7 @@
     build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'fr'),
                                 buf)
     output = buf.getvalue()
-    test = u"""
+    test = """
 {
   "ID_HELLO": {
     "message": "H\u00e9P\u00e9ll\u00f4P\u00f4!"
@@ -146,11 +146,11 @@
     build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'fr'),
                                 buf)
     output = buf.getvalue()
-    test = u'{}'
+    test = '{}'
     self.assertEqual(test, output)
 
   def testVerifyMinification(self):
-    root = util.ParseGrdForUnittest(u"""
+    root = util.ParseGrdForUnittest("""
     <messages>
       <message name="IDS">
         <ph name="BEGIN">$1<ex>a</ex></ph>test<ph name="END">$2<ex>b</ex></ph>
@@ -162,12 +162,12 @@
     build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'en'),
                                 buf)
     output = buf.getvalue()
-    test = (u'{"IDS":{"message":"$1$test$2$","placeholders":'
-            u'{"1":{"content":"$1"},"2":{"content":"$2"}}}}')
+    test = ('{"IDS":{"message":"$1$test$2$","placeholders":'
+            '{"1":{"content":"$1"},"2":{"content":"$2"}}}}')
     self.assertEqual(test, output)
 
 
-class DummyOutput(object):
+class DummyOutput:
 
   def __init__(self, type, language):
     self.type = type
diff --git a/tools/grit/grit/format/data_pack.py b/tools/grit/grit/format/data_pack.py
index ec0a1007..5d665c91 100755
--- a/tools/grit/grit/format/data_pack.py
+++ b/tools/grit/grit/format/data_pack.py
@@ -37,7 +37,7 @@
   pass
 
 
-class DataPackSizes(object):
+class DataPackSizes:
   def __init__(self, header, id_table, alias_table, data):
     self.header = header
     self.id_table = id_table
@@ -61,7 +61,7 @@
     return self.__class__.__name__ + repr(self.__dict__)
 
 
-class DataPackContents(object):
+class DataPackContents:
   def __init__(self, resources, encoding, version, aliases, sizes):
     # Map of resource_id -> str.
     self.resources = resources
@@ -208,7 +208,7 @@
 
 def ReadGrdInfo(grd_file):
   info_dict = {}
-  with open(grd_file + '.info', 'rt') as f:
+  with open(grd_file + '.info') as f:
     for line in f:
       item = GrdInfoItem._make(line.strip().split(','))
       info_dict[int(item.id)] = item
@@ -243,7 +243,7 @@
     lines = util.ReadFile(allowlist_file, 'utf-8').strip().splitlines()
     if not lines:
       raise Exception('Allowlist file should not be empty')
-    allowlist = set(int(x) for x in lines)
+    allowlist = {int(x) for x in lines}
   inputs = [(p.resources, p.encoding) for p in input_data_packs]
   resources, encoding = RePackFromDataPackStrings(inputs, allowlist,
                                                   suppress_removed_key_output)
@@ -252,7 +252,7 @@
     output_info_filepath = output_file + '.info'
   with open(output_info_filepath, 'w') as output_info_file:
     for filename in input_info_files:
-      with open(filename, 'r') as info_file:
+      with open(filename) as info_file:
         output_info_file.writelines(info_file.readlines())
 
 
@@ -293,9 +293,9 @@
                      ' vs ' + str(input_encoding))
 
     if allowlist:
-      allowlisted_resources = dict([(key, input_resources[key])
+      allowlisted_resources = {key: input_resources[key]
                                     for key in input_resources.keys()
-                                    if key in allowlist])
+                                    if key in allowlist}
       resources.update(allowlisted_resources)
       removed_keys = [
           key for key in input_resources.keys() if key not in allowlist
diff --git a/tools/grit/grit/format/data_pack_unittest.py b/tools/grit/grit/format/data_pack_unittest.py
index e7f8839..2e47bf8 100755
--- a/tools/grit/grit/format/data_pack_unittest.py
+++ b/tools/grit/grit/format/data_pack_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.format.data_pack'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -58,7 +57,7 @@
         10: b'this is id 4',
     }
     data = data_pack.WriteDataPackToString(input_resources, data_pack.UTF8)
-    self.assertEquals(data, expected_data)
+    self.assertEqual(data, expected_data)
 
     expected_data_pack = data_pack.DataPackContents({
         1: b'',
diff --git a/tools/grit/grit/format/gen_predetermined_ids.py b/tools/grit/grit/format/gen_predetermined_ids.py
index fa6424c..ba8a20c 100755
--- a/tools/grit/grit/format/gen_predetermined_ids.py
+++ b/tools/grit/grit/format/gen_predetermined_ids.py
@@ -9,7 +9,6 @@
 a while and its output checked in. See tools/gritsettings/README.md for details.
 """
 
-from __future__ import print_function
 
 import os
 import re
@@ -55,7 +54,7 @@
     An array of ordered resource ids.
   """
   ordered_resource_ids = []
-  with open(path, "r") as f:
+  with open(path) as f:
     for match in ORDERED_RESOURCE_IDS_REGEX.finditer(f.read()):
       ordered_resource_ids.append(int(match.group(1)))
   return ordered_resource_ids
@@ -109,7 +108,7 @@
   for root, dirnames, filenames in os.walk(out_dir + '/gen'):
     for filename in filenames:
       if filename.endswith(('_resources.h', '_settings.h', '_strings.h')):
-        with open(os.path.join(root, filename), "r") as f:
+        with open(os.path.join(root, filename)) as f:
           ReadResourceIdsFromFile(f, original_resources)
   return original_resources
 
diff --git a/tools/grit/grit/format/gzip_string.py b/tools/grit/grit/format/gzip_string.py
index 89eac410..6010088 100644
--- a/tools/grit/grit/format/gzip_string.py
+++ b/tools/grit/grit/format/gzip_string.py
@@ -4,7 +4,6 @@
 """Provides gzip utilities for strings.
 """
 
-from __future__ import print_function
 
 import gzip
 import io
diff --git a/tools/grit/grit/format/gzip_string_unittest.py b/tools/grit/grit/format/gzip_string_unittest.py
index 6860971..31e3a64 100755
--- a/tools/grit/grit/format/gzip_string_unittest.py
+++ b/tools/grit/grit/format/gzip_string_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.format.gzip_string'''
 
-from __future__ import print_function
 
 import gzip
 import io
@@ -32,7 +31,7 @@
                b'<finished NOW>')
 
       compressed = gzip_string.GzipStringRsyncable(input)
-      self.failUnless(header_begin == compressed[:2])
+      self.assertTrue(header_begin == compressed[:2])
 
       compressed_file = io.BytesIO()
       compressed_file.write(compressed)
@@ -40,7 +39,7 @@
 
       with gzip.GzipFile(mode='rb', fileobj=compressed_file) as f:
         output = f.read()
-      self.failUnless(output == input)
+      self.assertTrue(output == input)
 
   def testGzipString(self):
     header_begin = b'\x1f\x8b'  # gzip first two bytes
@@ -50,7 +49,7 @@
              b'<finished NOW>')
 
     compressed = gzip_string.GzipString(input)
-    self.failUnless(header_begin == compressed[:2])
+    self.assertTrue(header_begin == compressed[:2])
 
     compressed_file = io.BytesIO()
     compressed_file.write(compressed)
@@ -58,7 +57,7 @@
 
     with gzip.GzipFile(mode='rb', fileobj=compressed_file) as f:
       output = f.read()
-    self.failUnless(output == input)
+    self.assertTrue(output == input)
 
 
 if __name__ == '__main__':
diff --git a/tools/grit/grit/format/html_inline.py b/tools/grit/grit/format/html_inline.py
index e2a5dc0..b07b478 100755
--- a/tools/grit/grit/format/html_inline.py
+++ b/tools/grit/grit/format/html_inline.py
@@ -10,7 +10,6 @@
 dependencies. It recursively inlines the included files.
 """
 
-from __future__ import print_function
 
 import os
 import re
@@ -587,7 +586,7 @@
         strip_whitespace=strip_whitespace,
         rewrite_function=rewrite_function,
         filename_expansion_function=filename_expansion_function).inlined_data
-  except IOError as e:
+  except OSError as e:
     raise Exception("Failed to open %s while trying to flatten %s. (%s)" %
                     (e.filename, input_filename, e.strerror))
 
@@ -627,7 +626,7 @@
         strip_whitespace=False,
         rewrite_function=rewrite_function,
         filename_expansion_function=filename_expansion_function).inlined_files
-  except IOError as e:
+  except OSError as e:
     raise Exception("Failed to open %s while trying to flatten %s. (%s)" %
                     (e.filename, filename, e.strerror))
 
diff --git a/tools/grit/grit/format/html_inline_unittest.py b/tools/grit/grit/format/html_inline_unittest.py
index db8c726c..0aa26c3f 100755
--- a/tools/grit/grit/format/html_inline_unittest.py
+++ b/tools/grit/grit/format/html_inline_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.format.html_inline'''
 
-from __future__ import print_function
 
 import os
 import re
@@ -19,7 +18,7 @@
 from grit.format import html_inline
 
 
-class FakeGrdNode(object):
+class FakeGrdNode:
   def EvaluateCondition(self, cond):
     return eval(cond)
 
@@ -82,7 +81,7 @@
     resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'),
                                                  None)
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
+    self.assertEqual(resources, source_resources)
     tmp_dir.CleanUp()
 
   def testUnmatchedEndIfBlock(self):
@@ -107,7 +106,7 @@
 
     with self.assertRaises(Exception) as cm:
       html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'), None)
-    self.failUnlessEqual(str(cm.exception), 'Unmatched </if>')
+    self.assertEqual(str(cm.exception), 'Unmatched </if>')
     tmp_dir.CleanUp()
 
   def testCompressedJavaScript(self):
@@ -127,7 +126,7 @@
     resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.js'),
                                                  None)
     resources.add(tmp_dir.GetPath('index.js'))
-    self.failUnlessEqual(resources, source_resources)
+    self.assertEqual(resources, source_resources)
     tmp_dir.CleanUp()
 
   def testInlineCSSImports(self):
@@ -182,8 +181,8 @@
     result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
 
     tmp_dir.CleanUp()
@@ -262,8 +261,8 @@
     result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
 
     tmp_dir.CleanUp()
@@ -312,8 +311,8 @@
     result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
     tmp_dir.CleanUp()
 
@@ -356,7 +355,7 @@
     resources = html_inline.GetResourceFilenames(tmp_dir.GetPath('index.html'),
                                                  None)
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
+    self.assertEqual(resources, source_resources)
     tmp_dir.CleanUp()
 
   def testInlineCSSLinks(self):
@@ -402,8 +401,8 @@
     result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
     tmp_dir.CleanUp()
 
@@ -453,8 +452,8 @@
         filename_expansion_function=replacer('WHICH', '1'))
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
 
     # Test names-only inlining.
@@ -465,7 +464,7 @@
         filename_expansion_function=replacer('WHICH', '1'))
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
+    self.assertEqual(resources, source_resources)
     tmp_dir.CleanUp()
 
   def testWithCloseTags(self):
@@ -530,8 +529,8 @@
         None)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
     tmp_dir.CleanUp()
 
@@ -553,8 +552,8 @@
     result = html_inline.DoInline(tmp_dir.GetPath('include.js'), None)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('include.js'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
     tmp_dir.CleanUp()
 
@@ -590,8 +589,8 @@
     resources = result.inlined_files
 
     resources.add(tmp_dir.GetPath('if.js'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
     tmp_dir.CleanUp()
 
@@ -821,8 +820,8 @@
         None)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
     tmp_dir.CleanUp()
 
@@ -853,8 +852,8 @@
     result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
     tmp_dir.CleanUp()
 
@@ -910,8 +909,8 @@
     result = html_inline.DoInline(tmp_dir.GetPath('index.html'), None)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
-    self.failUnlessEqual(expected_inlined,
+    self.assertEqual(resources, source_resources)
+    self.assertEqual(expected_inlined,
                          util.FixLineEnd(result.inlined_data, '\n'))
     tmp_dir.CleanUp()
 
@@ -980,13 +979,13 @@
         FakeGrdNode())
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
+    self.assertEqual(resources, source_resources)
 
     # ignore whitespace
     expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
     actually_inlined = re.sub(r'\s+', ' ',
                               util.FixLineEnd(result.inlined_data, '\n'))
-    self.failUnlessEqual(expected_inlined, actually_inlined);
+    self.assertEqual(expected_inlined, actually_inlined);
     tmp_dir.CleanUp()
 
   def testPreprocessOnlyEvaluatesIncludeAndIf(self):
@@ -1034,13 +1033,13 @@
                                   preprocess_only=True)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
+    self.assertEqual(resources, source_resources)
 
     # Ignore whitespace
     expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
     actually_inlined = re.sub(r'\s+', ' ',
                               util.FixLineEnd(result.inlined_data, '\n'))
-    self.failUnlessEqual(expected_inlined, actually_inlined)
+    self.assertEqual(expected_inlined, actually_inlined)
 
     tmp_dir.CleanUp()
 
@@ -1078,13 +1077,13 @@
                                   preprocess_only=True)
     resources = result.inlined_files
     resources.add(tmp_dir.GetPath('index.html'))
-    self.failUnlessEqual(resources, source_resources)
+    self.assertEqual(resources, source_resources)
 
     # Ignore whitespace
     expected_inlined = re.sub(r'\s+', ' ', expected_inlined)
     actually_inlined = re.sub(r'\s+', ' ',
                               util.FixLineEnd(result.inlined_data, '\n'))
-    self.failUnlessEqual(expected_inlined, actually_inlined)
+    self.assertEqual(expected_inlined, actually_inlined)
 
     tmp_dir.CleanUp()
 
diff --git a/tools/grit/grit/format/policy_templates_json.py b/tools/grit/grit/format/policy_templates_json.py
index f76dfe8f..852f005d 100644
--- a/tools/grit/grit/format/policy_templates_json.py
+++ b/tools/grit/grit/format/policy_templates_json.py
@@ -5,7 +5,6 @@
 """Translates policy_templates.json files.
 """
 
-from __future__ import print_function
 
 from grit.node import structure
 
diff --git a/tools/grit/grit/format/policy_templates_json_unittest.py b/tools/grit/grit/format/policy_templates_json_unittest.py
index 363d7090..558da311 100755
--- a/tools/grit/grit/format/policy_templates_json_unittest.py
+++ b/tools/grit/grit/format/policy_templates_json_unittest.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python3
-# coding: utf-8
 # Copyright 2017 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -7,7 +6,6 @@
 """Unittest for policy_templates_json.py.
 """
 
-from __future__ import print_function
 
 import os
 import sys
@@ -19,7 +17,7 @@
 import tempfile
 import unittest
 
-from six import StringIO
+from io import StringIO
 
 from grit import grd_reader
 from grit.tool import build
@@ -151,7 +149,7 @@
     # Caption and message texts get taken from xtb.
     # desc is 'translated' to some pseudo-English
     #   'ThïPïs pôPôlïPïcýPý dôéPôés stüPüff'.
-    expected = u"""{
+    expected = """{
   "policy_definitions": [
     {
       "caption": "%s",
@@ -194,7 +192,7 @@
     self.assertEqual(expected, output)
 
 
-class DummyOutput(object):
+class DummyOutput:
 
   def __init__(self, type, language):
     self.type = type
diff --git a/tools/grit/grit/format/rc_header.py b/tools/grit/grit/format/rc_header.py
index 7cc2c0e..6876d36 100644
--- a/tools/grit/grit/format/rc_header.py
+++ b/tools/grit/grit/format/rc_header.py
@@ -5,7 +5,6 @@
 '''Item formatters for RC headers.
 '''
 
-from __future__ import print_function
 
 
 def Format(root, lang='en', output_dir='.'):
diff --git a/tools/grit/grit/format/rc_header_unittest.py b/tools/grit/grit/format/rc_header_unittest.py
index d3374326..b49a9bf 100755
--- a/tools/grit/grit/format/rc_header_unittest.py
+++ b/tools/grit/grit/format/rc_header_unittest.py
@@ -8,7 +8,6 @@
 # GRD samples exceed the 80 character limit.
 # pylint: disable-msg=C6310
 
-from __future__ import print_function
 
 import os
 import sys
@@ -44,8 +43,8 @@
           <structure type="version" name="VS_VERSION_INFO" file="rc_files/version.rc" />
         </structures>''')
     output = self.FormatAll(grd)
-    self.failUnless(output.count('IDS_GREETING10000'))
-    self.failUnless(output.count('ID_LOGO300'))
+    self.assertTrue(output.count('IDS_GREETING10000'))
+    self.assertTrue(output.count('ID_LOGO300'))
 
   def testOnlyDefineResourcesThatSatisfyOutputCondition(self):
     grd = util.ParseGrdForUnittest('''
@@ -76,10 +75,10 @@
           </message>
        </messages>''')
     output = self.FormatAll(grd)
-    self.failUnless(output.count('IDS_FIRSTPRESENTSTRING10000'))
-    self.failIf(output.count('IDS_MISSINGSTRING'))
-    self.failUnless(output.count('IDS_LANGUAGESPECIFICSTRING10002'))
-    self.failUnless(output.count('IDS_THIRDPRESENTSTRING10003'))
+    self.assertTrue(output.count('IDS_FIRSTPRESENTSTRING10000'))
+    self.assertFalse(output.count('IDS_MISSINGSTRING'))
+    self.assertTrue(output.count('IDS_LANGUAGESPECIFICSTRING10002'))
+    self.assertTrue(output.count('IDS_THIRDPRESENTSTRING10003'))
 
   def testEmit(self):
     grd = util.ParseGrdForUnittest('''
diff --git a/tools/grit/grit/format/rc_unittest.py b/tools/grit/grit/format/rc_unittest.py
index a68383a..6200a86a 100755
--- a/tools/grit/grit/format/rc_unittest.py
+++ b/tools/grit/grit/format/rc_unittest.py
@@ -31,7 +31,7 @@
 '''
 
 
-class DummyOutput(object):
+class DummyOutput:
   def __init__(self, type, language, file = 'hello.gif'):
     self.type = type
     self.language = language
@@ -69,7 +69,7 @@
     buf = io.StringIO()
     build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
     output = util.StripBlankLinesAndComments(buf.getvalue())
-    self.assertEqual(_PREAMBLE + u'''\
+    self.assertEqual(_PREAMBLE + '''\
 STRINGTABLE
 BEGIN
   IDS_BTN_GO      "Go!"
@@ -91,7 +91,7 @@
     buf = io.StringIO()
     build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
     output = util.StripBlankLinesAndComments(buf.getvalue())
-    expected = _PREAMBLE + u'''\
+    expected = _PREAMBLE + '''\
 IDC_KLONKMENU MENU
 BEGIN
     POPUP "&File"
@@ -169,8 +169,8 @@
     build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
     output = util.StripBlankLinesAndComments(buf.getvalue())
     expected = (_PREAMBLE +
-                u'IDR_HTML           HTML               "%s"\n'
-                u'IDR_HTML2          HTML               "%s"'
+                'IDR_HTML           HTML               "%s"\n'
+                'IDR_HTML2          HTML               "%s"'
                 % (util.normpath('/temp/bingo.html').replace('\\', '\\\\'),
                    util.normpath('/temp/bingo2.html').replace('\\', '\\\\')))
     # hackety hack to work on win32&lin
@@ -188,8 +188,8 @@
     build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
     output = util.StripBlankLinesAndComments(buf.getvalue())
     expected = (_PREAMBLE +
-                u'TEXT_ONE           TXT                "%s"\n'
-                u'TEXT_TWO           TXT                "%s"'
+                'TEXT_ONE           TXT                "%s"\n'
+                'TEXT_TWO           TXT                "%s"'
                 % (util.normpath('/temp/bingo.txt').replace('\\', '\\\\'),
                    'bingo2.txt'))
     # hackety hack to work on win32&lin
@@ -210,7 +210,7 @@
     output = util.StripBlankLinesAndComments(buf.getvalue())
 
     expected = (_PREAMBLE +
-        u'HTML_FILE1         BINDATA            "HTML_FILE1_include_test.html"')
+        'HTML_FILE1         BINDATA            "HTML_FILE1_include_test.html"')
     # hackety hack to work on win32&lin
     output = re.sub(r'"[c-zC-Z]:', '"', output)
     self.assertEqual(expected, output)
@@ -218,19 +218,19 @@
     file_contents = util.ReadFile(output_file, 'utf-8')
 
     # Check for the content added by the <include> tag.
-    self.failUnless(file_contents.find('Hello Include!') != -1)
+    self.assertTrue(file_contents.find('Hello Include!') != -1)
     # Check for the content that was removed by if tag.
-    self.failUnless(file_contents.find('should be removed') == -1)
+    self.assertTrue(file_contents.find('should be removed') == -1)
     # Check for the content that was kept in place by if.
-    self.failUnless(file_contents.find('should be kept') != -1)
-    self.failUnless(file_contents.find('in the middle...') != -1)
-    self.failUnless(file_contents.find('at the end...') != -1)
+    self.assertTrue(file_contents.find('should be kept') != -1)
+    self.assertTrue(file_contents.find('in the middle...') != -1)
+    self.assertTrue(file_contents.find('at the end...') != -1)
     # Check for nested content that was kept
-    self.failUnless(file_contents.find('nested true should be kept') != -1)
-    self.failUnless(file_contents.find('silbing true should be kept') != -1)
+    self.assertTrue(file_contents.find('nested true should be kept') != -1)
+    self.assertTrue(file_contents.find('silbing true should be kept') != -1)
     # Check for removed "<if>" and "</if>" tags.
-    self.failUnless(file_contents.find('<if expr=') == -1)
-    self.failUnless(file_contents.find('</if>') == -1)
+    self.assertTrue(file_contents.find('<if expr=') == -1)
+    self.assertTrue(file_contents.find('</if>') == -1)
     os.remove(output_file)
 
   def testStructureNodeOutputfile(self):
@@ -247,14 +247,14 @@
 
     output_dir = tempfile.gettempdir()
     en_file = struct.FileForLanguage('en', output_dir)
-    self.failUnless(en_file == input_file)
+    self.assertTrue(en_file == input_file)
     fr_file = struct.FileForLanguage('fr', output_dir)
-    self.failUnless(fr_file == os.path.join(output_dir, 'fr_simple.html'))
+    self.assertTrue(fr_file == os.path.join(output_dir, 'fr_simple.html'))
 
     contents = util.ReadFile(fr_file, 'utf-8')
 
-    self.failUnless(contents.find('<p>') != -1)  # should contain the markup
-    self.failUnless(contents.find('Hello!') == -1)  # should be translated
+    self.assertTrue(contents.find('<p>') != -1)  # should contain the markup
+    self.assertTrue(contents.find('Hello!') == -1)  # should be translated
     os.remove(fr_file)
 
   def testChromeHtmlNodeOutputfile(self):
@@ -276,7 +276,7 @@
                                 buf)
     output = util.StripBlankLinesAndComments(buf.getvalue())
     expected = (_PREAMBLE +
-        u'HTML_FILE1         BINDATA            "HTML_FILE1_chrome_html.html"')
+        'HTML_FILE1         BINDATA            "HTML_FILE1_chrome_html.html"')
     # hackety hack to work on win32&lin
     output = re.sub(r'"[c-zC-Z]:', '"', output)
     self.assertEqual(expected, output)
@@ -284,9 +284,9 @@
     file_contents = util.ReadFile(output_file, 'utf-8')
 
     # Check for the content added by the <include> tag.
-    self.failUnless(file_contents.find('Hello Include!') != -1)
+    self.assertTrue(file_contents.find('Hello Include!') != -1)
     # Check for inserted -webkit-image-set.
-    self.failUnless(file_contents.find('content: -webkit-image-set') != -1)
+    self.assertTrue(file_contents.find('content: -webkit-image-set') != -1)
     os.remove(output_file)
 
   def testSubstitutionHtml(self):
@@ -309,12 +309,12 @@
     output_dir = tempfile.gettempdir()
     struct, = root.GetChildrenOfType(structure.StructureNode)
     ar_file = struct.FileForLanguage('ar', output_dir)
-    self.failUnless(ar_file == os.path.join(output_dir,
+    self.assertTrue(ar_file == os.path.join(output_dir,
                                             'ar_toolbar_about.html'))
 
     contents = util.ReadFile(ar_file, 'utf-8')
 
-    self.failUnless(contents.find('dir="RTL"') != -1)
+    self.assertTrue(contents.find('dir="RTL"') != -1)
     os.remove(ar_file)
 
   def testFallbackToEnglish(self):
diff --git a/tools/grit/grit/format/resource_map.py b/tools/grit/grit/format/resource_map.py
index 16413822..fcad84b 100644
--- a/tools/grit/grit/format/resource_map.py
+++ b/tools/grit/grit/format/resource_map.py
@@ -6,7 +6,6 @@
 resource_map_source files.  A resource map is a mapping between resource names
 (string) and the internal resource ID.'''
 
-from __future__ import print_function
 
 import os
 from functools import partial
diff --git a/tools/grit/grit/format/resource_map_unittest.py b/tools/grit/grit/format/resource_map_unittest.py
index 06f35211..5d19441 100755
--- a/tools/grit/grit/format/resource_map_unittest.py
+++ b/tools/grit/grit/format/resource_map_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.format.resource_map'''
 
-from __future__ import print_function
 
 import os
 import sys
diff --git a/tools/grit/grit/gather/admin_template.py b/tools/grit/grit/gather/admin_template.py
index 46f3e39..603887b 100644
--- a/tools/grit/grit/gather/admin_template.py
+++ b/tools/grit/grit/gather/admin_template.py
@@ -5,7 +5,6 @@
 '''Gatherer for administrative template files.
 '''
 
-from __future__ import print_function
 
 import re
 
diff --git a/tools/grit/grit/gather/admin_template_unittest.py b/tools/grit/grit/gather/admin_template_unittest.py
index bf947744..744cbb6 100755
--- a/tools/grit/grit/gather/admin_template_unittest.py
+++ b/tools/grit/grit/gather/admin_template_unittest.py
@@ -30,12 +30,12 @@
         'gotcha = "bingolabongola "the wise" fingulafongula" \n')
     gatherer = admin_template.AdmGatherer(pseudofile)
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 2)
-    self.failUnless(gatherer.GetCliques()[1].GetMessage().GetRealContent() ==
+    self.assertTrue(len(gatherer.GetCliques()) == 2)
+    self.assertTrue(gatherer.GetCliques()[1].GetMessage().GetRealContent() ==
                     'bingolabongola "the wise" fingulafongula')
 
     translation = gatherer.Translate('en')
-    self.failUnless(translation == gatherer.GetText().strip())
+    self.assertTrue(translation == gatherer.GetText().strip())
 
   def testErrorHandling(self):
     pseudofile = io.StringIO(
@@ -57,10 +57,10 @@
   )
 
   def VerifyCliquesFromAdmFile(self, cliques):
-    self.failUnless(len(cliques) > 20)
+    self.assertTrue(len(cliques) > 20)
     for clique, expected in zip(cliques, self._TRANSLATABLES_FROM_FILE):
       text = clique.GetMessage().GetRealContent()
-      self.failUnless(text == expected)
+      self.assertTrue(text == expected)
 
   def testFromFile(self):
     fname = util.PathFromRoot('grit/testdata/GoogleDesktop.adm')
@@ -104,8 +104,8 @@
       tool.res = grd
       tool.Process()
 
-      self.failUnless(os.path.isfile(dirname.GetPath('de_GoogleDesktop.adm')))
-      self.failUnless(os.path.isfile(dirname.GetPath('de_README.txt')))
+      self.assertTrue(os.path.isfile(dirname.GetPath('de_GoogleDesktop.adm')))
+      self.assertTrue(os.path.isfile(dirname.GetPath('de_README.txt')))
     finally:
       dirname.CleanUp()
 
diff --git a/tools/grit/grit/gather/chrome_html.py b/tools/grit/grit/gather/chrome_html.py
index 63b9e5f..c73c0a1 100644
--- a/tools/grit/grit/gather/chrome_html.py
+++ b/tools/grit/grit/gather/chrome_html.py
@@ -14,7 +14,6 @@
 referencing all available images.
 """
 
-from __future__ import print_function
 
 import os
 import re
@@ -287,7 +286,7 @@
   """
 
   def __init__(self, *args, **kwargs):
-    super(ChromeHtml, self).__init__(*args, **kwargs)
+    super().__init__(*args, **kwargs)
     self.allow_external_script_ = False
     self.flatten_html_ = False
     self.preprocess_only_ = False
diff --git a/tools/grit/grit/gather/chrome_html_unittest.py b/tools/grit/grit/gather/chrome_html_unittest.py
index 15a52fa..954a2cf 100755
--- a/tools/grit/grit/gather/chrome_html_unittest.py
+++ b/tools/grit/grit/gather/chrome_html_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.gather.chrome_html'''
 
-from __future__ import print_function
 
 import os
 import re
@@ -65,7 +64,7 @@
     html.SetDefines({'scale_factors': '1.4x,1.8x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       <!DOCTYPE HTML>
       <html>
@@ -106,7 +105,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       <!DOCTYPE HTML>
       <html>
@@ -138,7 +137,7 @@
     html.SetDefines({'scale_factors': '1.4x,1.8x'})
     html.SetAttributes({'flattenhtml': 'false'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url('test.png') 1x, url('1.4x/test.png') 1.4x, url('1.8x/test.png') 1.8x);
@@ -167,7 +166,7 @@
     html.SetDefines({'scale_factors': '1.4x,1.8x'})
     html.SetAttributes({'flattenhtml': 'false'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url('sub/test.png') 1x, url('sub/1.4x/test.png') 1.4x, url('sub/1.8x/test.png') 1.8x);
@@ -197,7 +196,7 @@
     html.SetDefines({'scale_factors': '1.4x,1.8x'})
     html.SetAttributes({'flattenhtml': 'false', 'preprocess': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url('test.png') 1x, url('1.4x/test.png') 1.4x, url('1.8x/test.png') 1.8x);
@@ -224,7 +223,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url("data:image/png;base64,UE5HIERBVEE=") 1x, url("data:image/png;base64,MnggUE5HIERBVEE=") 2x);
@@ -251,7 +250,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
@@ -278,7 +277,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url('data:image/png;base64,UE5HIERBVEE=') 1x, url('data:image/png;base64,MnggUE5HIERBVEE=') 2x);
@@ -315,7 +314,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       <!DOCTYPE HTML>
       <html>
@@ -352,7 +351,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x), -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
@@ -380,7 +379,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x),
@@ -411,7 +410,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x),
@@ -440,7 +439,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       .image {
         background: -webkit-image-set(url(data:image/png;base64,UE5HIERBVEE=) 1x, url(data:image/png;base64,MnggUE5HIERBVEE=) 2x);
@@ -476,7 +475,7 @@
     html.SetDefines({'scale_factors': '2x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       <!DOCTYPE HTML>
       <html>
@@ -530,7 +529,7 @@
     html.SetDefines({'scale_factors': '1.8x'})
     html.SetAttributes({'flattenhtml': 'true'})
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       <!DOCTYPE HTML>
       <html>
@@ -587,7 +586,7 @@
     html.SetAttributes({'flattenhtml': 'true'})
     html.SetFilenameExpansionFunction(replacer('WHICH', '1'));
     html.Parse()
-    self.failUnlessEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
+    self.assertEqual(StandardizeHtml(html.GetData('en', 'utf-8')),
                          StandardizeHtml('''
       <!DOCTYPE HTML>
       <html>
diff --git a/tools/grit/grit/gather/chrome_scaled_image.py b/tools/grit/grit/gather/chrome_scaled_image.py
index a948527..c3f72c0 100644
--- a/tools/grit/grit/gather/chrome_scaled_image.py
+++ b/tools/grit/grit/gather/chrome_scaled_image.py
@@ -5,7 +5,6 @@
 '''Gatherer for <structure type="chrome_scaled_image">.
 '''
 
-from __future__ import print_function
 
 import os
 import struct
diff --git a/tools/grit/grit/gather/chrome_scaled_image_unittest.py b/tools/grit/grit/gather/chrome_scaled_image_unittest.py
index 9725bbb7..6e6a0bb3 100755
--- a/tools/grit/grit/gather/chrome_scaled_image_unittest.py
+++ b/tools/grit/grit/gather/chrome_scaled_image_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for ChromeScaledImage.'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -63,9 +62,9 @@
   '''Get a set of the files that were actually included in the .rc output.
   '''
   data = util.ReadFile(rcname, util.BINARY).decode('utf-16')
-  contents = dict((tmp_dir.GetPath(k), v) for k, v in contents.items())
-  return set(contents[os.path.normpath(m.group(1))]
-             for m in re.finditer(r'(?m)^\w+\s+BINDATA\s+"([^"]+)"$', data))
+  contents = {tmp_dir.GetPath(k): v for k, v in contents.items()}
+  return {contents[os.path.normpath(m.group(1))]
+             for m in re.finditer(r'(?m)^\w+\s+BINDATA\s+"([^"]+)"$', data)}
 
 
 def _MakeFallbackAttr(fallback):
@@ -111,7 +110,7 @@
   for pngpath, pngdata in inputs.items():
     normpath = os.path.normpath('in/' + pngpath)
     infiles[normpath] = pngdata
-  class Options(object):
+  class Options:
     pass
 
   with util.TempDir(infiles, mode='wb') as tmp_dir:
@@ -122,10 +121,10 @@
       options.extra_verbose = False
       build.RcBuilder().Run(options, [])
     for context, expected_data in expected_outputs.items():
-      self.assertEquals(expected_data,
+      self.assertEqual(expected_data,
                         _GetFilesInPak(tmp_dir.GetPath('out/%s.pak' % context)))
       if not skip_rc:
-        self.assertEquals(expected_data,
+        self.assertEqual(expected_data,
                           _GetFilesInRc(tmp_dir.GetPath('out/%s.rc' % context),
                                         tmp_dir, infiles))
 
@@ -144,8 +143,8 @@
          'tactile_123_percent/a.png': t123a,
          'default_123_percent/b.png': d123b,
         },
-        {'default_123_percent': set([d123a, d123b]),
-         'tactile_123_percent': set([t123a, d123b]),
+        {'default_123_percent': {d123a, d123b},
+         'tactile_123_percent': {t123a, d123b},
         })
 
   def testNormalFallbackFailure(self):
@@ -170,7 +169,7 @@
                 _Structure('IDR_A', 'a.png', inner),
             ),
             {'default_100_percent/a.png': png},
-            {'tactile_200_percent': set([png_with_csCl])})
+            {'tactile_200_percent': {png_with_csCl}})
         if inner or (inner is None and outer):
           # should fall back to 100%
           _RunBuildTest(*args, skip_rc=True)
@@ -200,8 +199,8 @@
          'tactile_123_percent/a.png': t123a,
          'default_123_percent/b.png': d123b,
         },
-        {'default_123_percent': set([d123a, d123b]),
-         'tactile_123_percent': set([t123a]),
+        {'default_123_percent': {d123a, d123b},
+         'tactile_123_percent': {t123a},
         },
         layout_fallback=' fallback_to_default_layout="false"')
 
diff --git a/tools/grit/grit/gather/interface.py b/tools/grit/grit/gather/interface.py
index 25e3195..e5b91fb 100644
--- a/tools/grit/grit/gather/interface.py
+++ b/tools/grit/grit/gather/interface.py
@@ -11,7 +11,7 @@
 from grit import util
 
 
-class GathererBase(object):
+class GathererBase:
   '''Interface for all gatherer implementations.  Subclasses must implement
   all methods that raise NotImplemented.'''
 
diff --git a/tools/grit/grit/gather/json_loader.py b/tools/grit/grit/gather/json_loader.py
index 4cfb549..f2d03e2 100644
--- a/tools/grit/grit/gather/json_loader.py
+++ b/tools/grit/grit/gather/json_loader.py
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from __future__ import print_function
 
 from grit.gather import interface
 
diff --git a/tools/grit/grit/gather/policy_json_unittest.py b/tools/grit/grit/gather/policy_json_unittest.py
index 896df64..a194c1c 100755
--- a/tools/grit/grit/gather/policy_json_unittest.py
+++ b/tools/grit/grit/gather/policy_json_unittest.py
@@ -33,8 +33,8 @@
     }
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 0)
-    self.failUnless(json.dumps(original) == json.dumps(
+    self.assertTrue(len(gatherer.GetCliques()) == 0)
+    self.assertTrue(json.dumps(original) == json.dumps(
             json.loads(gatherer.Translate('en'))))
 
   def testGeneralPolicy(self):
@@ -64,9 +64,9 @@
     }
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 4)
+    self.assertTrue(len(gatherer.GetCliques()) == 4)
     expected = self.GetExpectedOutput(original)
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
 
   def testEnum(self):
     original = {
@@ -85,9 +85,9 @@
     }
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 1)
+    self.assertTrue(len(gatherer.GetCliques()) == 1)
     expected = self.GetExpectedOutput(original)
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
 
   def testSchema(self):
     original = {
@@ -124,9 +124,9 @@
     }
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 4)
+    self.assertTrue(len(gatherer.GetCliques()) == 4)
     expected = self.GetExpectedOutput(original)
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
 
   def testValidationSchema(self):
     original = {
@@ -148,9 +148,9 @@
     }
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 1)
+    self.assertTrue(len(gatherer.GetCliques()) == 1)
     expected = self.GetExpectedOutput(original)
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
 
   def testDescriptionSchema(self):
     original = {
@@ -172,9 +172,9 @@
     }
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 1)
+    self.assertTrue(len(gatherer.GetCliques()) == 1)
     expected = self.GetExpectedOutput(original)
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
 
   # Keeping for backwards compatibility.
   def testSubPolicyOldFormat(self):
@@ -193,9 +193,9 @@
     }
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 1)
+    self.assertTrue(len(gatherer.GetCliques()) == 1)
     expected = self.GetExpectedOutput(original)
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
 
   def testSubPolicyNewFormat(self):
     original = {
@@ -212,9 +212,9 @@
     }
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 1)
+    self.assertTrue(len(gatherer.GetCliques()) == 1)
     expected = self.GetExpectedOutput(original)
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
 
   def testEscapingAndLineBreaks(self):
     original = {
@@ -256,9 +256,9 @@
     }
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 6)
+    self.assertTrue(len(gatherer.GetCliques()) == 6)
     expected = self.GetExpectedOutput(original)
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
 
   def testPlaceholdersChromium(self):
     original = {
@@ -274,16 +274,16 @@
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.SetDefines({'_chromium': True})
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 1)
+    self.assertTrue(len(gatherer.GetCliques()) == 1)
     expected = json.loads(re.sub('<ph.*ph>', 'Chromium', json.dumps(original)))
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
-    self.failUnless(gatherer.GetCliques()[0].translateable)
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(gatherer.GetCliques()[0].translateable)
     msg = gatherer.GetCliques()[0].GetMessage()
-    self.failUnless(len(msg.GetPlaceholders()) == 1)
+    self.assertTrue(len(msg.GetPlaceholders()) == 1)
     ph = msg.GetPlaceholders()[0]
-    self.failUnless(ph.GetOriginal() == 'Chromium')
-    self.failUnless(ph.GetPresentation() == 'PRODUCT_NAME')
-    self.failUnless(ph.GetExample() == 'Google Chrome')
+    self.assertTrue(ph.GetOriginal() == 'Chromium')
+    self.assertTrue(ph.GetPresentation() == 'PRODUCT_NAME')
+    self.assertTrue(ph.GetExample() == 'Google Chrome')
 
   def testPlaceholdersChrome(self):
     original = {
@@ -299,34 +299,34 @@
     gatherer = policy_json.PolicyJson(io.StringIO(json.dumps(original)))
     gatherer.SetDefines({'_google_chrome': True})
     gatherer.Parse()
-    self.failUnless(len(gatherer.GetCliques()) == 1)
+    self.assertTrue(len(gatherer.GetCliques()) == 1)
     expected = json.loads(
         re.sub('<ph.*ph>', 'Google Chrome', json.dumps(original)))
-    self.failUnless(expected == json.loads(gatherer.Translate('en')))
-    self.failUnless(gatherer.GetCliques()[0].translateable)
+    self.assertTrue(expected == json.loads(gatherer.Translate('en')))
+    self.assertTrue(gatherer.GetCliques()[0].translateable)
     msg = gatherer.GetCliques()[0].GetMessage()
-    self.failUnless(len(msg.GetPlaceholders()) == 1)
+    self.assertTrue(len(msg.GetPlaceholders()) == 1)
     ph = msg.GetPlaceholders()[0]
-    self.failUnless(ph.GetOriginal() == 'Google Chrome')
-    self.failUnless(ph.GetPresentation() == 'PRODUCT_NAME')
-    self.failUnless(ph.GetExample() == 'Google Chrome')
+    self.assertTrue(ph.GetOriginal() == 'Google Chrome')
+    self.assertTrue(ph.GetPresentation() == 'PRODUCT_NAME')
+    self.assertTrue(ph.GetExample() == 'Google Chrome')
 
   def testGetDescription(self):
     gatherer = policy_json.PolicyJson({})
     gatherer.SetDefines({'_google_chrome': True})
-    self.assertEquals(
+    self.assertEqual(
         gatherer._GetDescription({'name': 'Policy1', 'owners': ['a@b']},
                                  'policy', None, 'desc'),
         'Description of the policy named Policy1 [owner(s): a@b]')
-    self.assertEquals(
+    self.assertEqual(
         gatherer._GetDescription({'name': 'Plcy2', 'owners': ['a@b', 'c@d']},
                                  'policy', None, 'caption'),
         'Caption of the policy named Plcy2 [owner(s): a@b,c@d]')
-    self.assertEquals(
+    self.assertEqual(
         gatherer._GetDescription({'name': 'Plcy3', 'owners': ['a@b']},
                                  'policy', None, 'label'),
         'Label of the policy named Plcy3 [owner(s): a@b]')
-    self.assertEquals(
+    self.assertEqual(
         gatherer._GetDescription({'name': 'Item'}, 'enum_item',
                                  {'name': 'Plcy', 'owners': ['a@b']}, 'caption'),
         'Caption of the option named Item in policy Plcy [owner(s): a@b]')
diff --git a/tools/grit/grit/gather/rc.py b/tools/grit/grit/gather/rc.py
index 56489fe..fbf42702 100644
--- a/tools/grit/grit/gather/rc.py
+++ b/tools/grit/grit/gather/rc.py
@@ -5,7 +5,6 @@
 '''Support for gathering resources from RC files.
 '''
 
-from __future__ import print_function
 
 import re
 
@@ -36,7 +35,7 @@
 }
 
 # How to unescape certain strings
-_UNESCAPE_CHARS = dict([[value, key] for key, value in _ESCAPE_CHARS.items()])
+_UNESCAPE_CHARS = {value: key for key, value in _ESCAPE_CHARS.items()}
 
 
 
@@ -62,7 +61,7 @@
     '''Overrides _RegExpParse to add shortcut group handling.  Otherwise
     the same.
     '''
-    super(Section, self)._RegExpParse(rexp, text_to_parse)
+    super()._RegExpParse(rexp, text_to_parse)
 
     if not self.is_skeleton and len(self.GetTextualIds()) > 0:
       group_name = self.GetTextualIds()[0]
diff --git a/tools/grit/grit/gather/rc_unittest.py b/tools/grit/grit/gather/rc_unittest.py
index 70444ad..ff8df10f 100755
--- a/tools/grit/grit/gather/rc_unittest.py
+++ b/tools/grit/grit/gather/rc_unittest.py
@@ -43,7 +43,7 @@
 
     out = rc.Section(f, 'IDC_KLONKACC')
     out.ReadSection()
-    self.failUnless(out.GetText() == self.part_we_want)
+    self.assertTrue(out.GetText() == self.part_we_want)
 
     out = rc.Section(util.PathFromRoot(r'grit/testdata/klonk.rc'),
                      'IDC_KLONKACC',
@@ -52,7 +52,7 @@
     out_text = out.GetText().replace('\t', '')
     out_text = out_text.replace(' ', '')
     self.part_we_want = self.part_we_want.replace(' ', '')
-    self.failUnless(out_text.strip() == self.part_we_want.strip())
+    self.assertTrue(out_text.strip() == self.part_we_want.strip())
 
 
   def testDialog(self):
@@ -75,13 +75,13 @@
 END
 '''), 'IDD_ABOUTBOX')
     dlg.Parse()
-    self.failUnless(len(dlg.GetTextualIds()) == 7)
-    self.failUnless(len(dlg.GetCliques()) == 6)
-    self.failUnless(dlg.GetCliques()[1].GetMessage().GetRealContent() ==
+    self.assertTrue(len(dlg.GetTextualIds()) == 7)
+    self.assertTrue(len(dlg.GetCliques()) == 6)
+    self.assertTrue(dlg.GetCliques()[1].GetMessage().GetRealContent() ==
                     'klonk Version "yibbee" 1.0')
 
     transl = dlg.Translate('en')
-    self.failUnless(transl.strip() == dlg.GetText().strip())
+    self.assertTrue(transl.strip() == dlg.GetText().strip())
 
   def testAlternateSkeleton(self):
     dlg = rc.Dialog(
@@ -109,9 +109,9 @@
     alt_dlg.Parse()
 
     transl = dlg.Translate('en', skeleton_gatherer=alt_dlg)
-    self.failUnless(transl.count('040704') and
+    self.assertTrue(transl.count('040704') and
                     transl.count('110978'))
-    self.failUnless(transl.count('Yipee skippy'))
+    self.assertTrue(transl.count('Yipee skippy'))
 
   def testMenu(self):
     menu = rc.Menu(
@@ -135,13 +135,13 @@
 END'''), 'IDC_KLONK')
 
     menu.Parse()
-    self.failUnless(len(menu.GetTextualIds()) == 6)
-    self.failUnless(len(menu.GetCliques()) == 1)
-    self.failUnless(len(menu.GetCliques()[0].GetMessage().GetPlaceholders()) ==
+    self.assertTrue(len(menu.GetTextualIds()) == 6)
+    self.assertTrue(len(menu.GetCliques()) == 1)
+    self.assertTrue(len(menu.GetCliques()[0].GetMessage().GetPlaceholders()) ==
                     9)
 
     transl = menu.Translate('en')
-    self.failUnless(transl.strip() == menu.GetText().strip())
+    self.assertTrue(transl.strip() == menu.GetText().strip())
 
   def testVersion(self):
     version = rc.Version(
@@ -180,11 +180,11 @@
 END
 '''.strip()), 'VS_VERSION_INFO')
     version.Parse()
-    self.failUnless(len(version.GetTextualIds()) == 1)
-    self.failUnless(len(version.GetCliques()) == 4)
+    self.assertTrue(len(version.GetTextualIds()) == 1)
+    self.assertTrue(len(version.GetCliques()) == 4)
 
     transl = version.Translate('en')
-    self.failUnless(transl.strip() == version.GetText().strip())
+    self.assertTrue(transl.strip() == version.GetText().strip())
 
 
   def testRegressionDialogBox(self):
@@ -209,7 +209,7 @@
                     BS_AUTORADIOBUTTON,57,144,38,10
 END'''.strip()), 'IDD_SIDEBAR_WEATHER_PANEL_PROPPAGE')
     dialog.Parse()
-    self.failUnless(len(dialog.GetTextualIds()) == 10)
+    self.assertTrue(len(dialog.GetTextualIds()) == 10)
 
 
   def testRegressionDialogBox2(self):
@@ -230,7 +230,7 @@
                     IDC_STATIC,16,18,234,18
 END'''.strip()), 'IDD_SIDEBAR_EMAIL_PANEL_PROPPAGE')
     dialog.Parse()
-    self.failUnless('IDC_SIDEBAR_EMAIL_HIDDEN' in dialog.GetTextualIds())
+    self.assertTrue('IDC_SIDEBAR_EMAIL_HIDDEN' in dialog.GetTextualIds())
 
 
   def testRegressionMenuId(self):
@@ -244,7 +244,7 @@
     END
 END'''.strip()), 'IDR_HYPERMENU_FOLDER')
     menu.Parse()
-    self.failUnless(len(menu.GetTextualIds()) == 2)
+    self.assertTrue(len(menu.GetTextualIds()) == 2)
 
   def testRegressionNewlines(self):
     menu = rc.Menu(
@@ -259,7 +259,7 @@
     menu.Parse()
     transl = menu.Translate('en')
     # Shouldn't find \\n (the \n shouldn't be changed to \\n)
-    self.failUnless(transl.find('\\\\n') == -1)
+    self.assertTrue(transl.find('\\\\n') == -1)
 
   def testRegressionTabs(self):
     menu = rc.Menu(
@@ -274,19 +274,19 @@
     menu.Parse()
     transl = menu.Translate('en')
     # Shouldn't find \\t (the \t shouldn't be changed to \\t)
-    self.failUnless(transl.find('\\\\t') == -1)
+    self.assertTrue(transl.find('\\\\t') == -1)
 
   def testEscapeUnescape(self):
     original = 'Hello "bingo"\n How\\are\\you\\n?'
     escaped = rc.Section.Escape(original)
-    self.failUnless(escaped == 'Hello ""bingo""\\n How\\\\are\\\\you\\\\n?')
+    self.assertTrue(escaped == 'Hello ""bingo""\\n How\\\\are\\\\you\\\\n?')
     unescaped = rc.Section.UnEscape(escaped)
-    self.failUnless(unescaped == original)
+    self.assertTrue(unescaped == original)
 
   def testRegressionPathsWithSlashN(self):
     original = '..\\\\..\\\\trs\\\\res\\\\nav_first.gif'
     unescaped = rc.Section.UnEscape(original)
-    self.failUnless(unescaped == '..\\..\\trs\\res\\nav_first.gif')
+    self.assertTrue(unescaped == '..\\..\\trs\\res\\nav_first.gif')
 
   def testRegressionDialogItemsTextOnly(self):
     dialog = rc.Dialog(
@@ -307,8 +307,8 @@
     dialog.Parse()
     translateables = [c.GetMessage().GetRealContent()
                       for c in dialog.GetCliques()]
-    self.failUnless('Select search buttons and options' in translateables)
-    self.failUnless('Use Google site:' in translateables)
+    self.assertTrue('Select search buttons and options' in translateables)
+    self.assertTrue('Use Google site:' in translateables)
 
   def testAccelerators(self):
     acc = rc.Accelerators(
@@ -321,11 +321,11 @@
 END
 '''), 'IDR_ACCELERATOR1')
     acc.Parse()
-    self.failUnless(len(acc.GetTextualIds()) == 4)
-    self.failUnless(len(acc.GetCliques()) == 0)
+    self.assertTrue(len(acc.GetTextualIds()) == 4)
+    self.assertTrue(len(acc.GetCliques()) == 0)
 
     transl = acc.Translate('en')
-    self.failUnless(transl.strip() == acc.GetText().strip())
+    self.assertTrue(transl.strip() == acc.GetText().strip())
 
 
   def testRegressionEmptyString(self):
@@ -349,8 +349,8 @@
     dlg.Parse()
 
     def Check():
-      self.failUnless(transl.count('IDC_ENABLE_GD_AUTOSTART'))
-      self.failUnless(transl.count('END'))
+      self.assertTrue(transl.count('IDC_ENABLE_GD_AUTOSTART'))
+      self.assertTrue(transl.count('END'))
 
     transl = dlg.Translate('de', pseudo_if_not_available=True,
                            fallback_to_english=True)
diff --git a/tools/grit/grit/gather/regexp.py b/tools/grit/grit/gather/regexp.py
index 97e7e99..bf2c92ff 100644
--- a/tools/grit/grit/gather/regexp.py
+++ b/tools/grit/grit/gather/regexp.py
@@ -5,7 +5,6 @@
 '''A baseclass for simple gatherers based on regular expressions.
 '''
 
-from __future__ import print_function
 
 from grit.gather import skeleton_gatherer
 
diff --git a/tools/grit/grit/gather/skeleton_gatherer.py b/tools/grit/grit/gather/skeleton_gatherer.py
index c61e2aa1..e155440 100644
--- a/tools/grit/grit/gather/skeleton_gatherer.py
+++ b/tools/grit/grit/gather/skeleton_gatherer.py
@@ -18,7 +18,7 @@
   '''
 
   def __init__(self, *args, **kwargs):
-    super(SkeletonGatherer, self).__init__(*args, **kwargs)
+    super().__init__(*args, **kwargs)
     # List of parts of the document. Translateable parts are
     # clique.MessageClique objects, nontranslateable parts are plain strings.
     # Translated messages are inserted back into the skeleton using the quoting
diff --git a/tools/grit/grit/gather/tr_html.py b/tools/grit/grit/gather/tr_html.py
index 06adbea..c6f73ef 100644
--- a/tools/grit/grit/gather/tr_html.py
+++ b/tools/grit/grit/gather/tr_html.py
@@ -210,7 +210,7 @@
     print(text.encode('utf-8'))
 
 
-class HtmlChunks(object):
+class HtmlChunks:
   '''A parser that knows how to break an HTML-like document into a list of
   chunks, where each chunk is either translateable or non-translateable.
   The chunks are unmodified sections of the original document, so concatenating
@@ -599,7 +599,7 @@
   Total Recall for HTML documents.'''
 
   def __init__(self, *args, **kwargs):
-    super(TrHtml, self).__init__(*args, **kwargs)
+    super().__init__(*args, **kwargs)
     self.have_parsed_ = False
     self.skeleton_ = []  # list of strings and MessageClique objects
     self.fold_whitespace_ = False
@@ -676,7 +676,7 @@
     text = self._LoadInputFile()
 
     # Ignore the BOM character if the document starts with one.
-    if text.startswith(u'\ufeff'):
+    if text.startswith('\ufeff'):
       text = text[1:]
 
     self.text_ = text
diff --git a/tools/grit/grit/gather/tr_html_unittest.py b/tools/grit/grit/gather/tr_html_unittest.py
index a2f711a..3a9241d 100755
--- a/tools/grit/grit/gather/tr_html_unittest.py
+++ b/tools/grit/grit/gather/tr_html_unittest.py
@@ -46,19 +46,19 @@
     p = tr_html.HtmlChunks()
     chunks = p.Parse('<p>Hello <b>dear</b> how <i>are</i>you?<p>Fine!',
                      fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (False, '<p>', ''), (True, 'Hello <b>dear</b> how <i>are</i>you?', ''),
       (False, '<p>', ''), (True, 'Fine!', '')])
 
     chunks = p.Parse('<p> Hello <b>dear</b> how <i>are</i>you? <p>Fine!',
                      fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (False, '<p> ', ''), (True, 'Hello <b>dear</b> how <i>are</i>you?', ''),
       (False, ' <p>', ''), (True, 'Fine!', '')])
 
     chunks = p.Parse('<p> Hello <b>dear how <i>are you? <p> Fine!',
                      fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (False, '<p> ', ''), (True, 'Hello <b>dear how <i>are you?', ''),
       (False, ' <p> ', ''), (True, 'Fine!', '')])
 
@@ -66,7 +66,7 @@
     # the starting inline tag.
     chunks = p.Parse('<b>Hello!</b> how are you?<p><i>I am fine.</i>',
                      fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (True, '<b>Hello!</b> how are you?', ''), (False, '<p>', ''),
       (True, '<i>I am fine.</i>', '')])
 
@@ -74,7 +74,7 @@
     # the ending inline tag.
     chunks = p.Parse("Hello! How are <b>you?</b><p><i>I'm fine!</i>",
                      fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (True, 'Hello! How are <b>you?</b>', ''), (False, '<p>', ''),
       (True, "<i>I'm fine!</i>", '')])
 
@@ -83,18 +83,18 @@
     # Check capitals and explicit descriptions
     chunks = p.Parse('<!-- desc=bingo! --><B>Hello!</B> how are you?<P>'
                      '<I>I am fine.</I>', fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (True, '<B>Hello!</B> how are you?', 'bingo!'), (False, '<P>', ''),
       (True, '<I>I am fine.</I>', '')])
     chunks = p.Parse('<B><!-- desc=bingo! -->Hello!</B> how are you?<P>'
                      '<I>I am fine.</I>', fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (True, '<B>Hello!</B> how are you?', 'bingo!'), (False, '<P>', ''),
       (True, '<I>I am fine.</I>', '')])
     # Linebreaks get handled by the tclib message.
     chunks = p.Parse('<B>Hello!</B> <!-- desc=bi\nngo\n! -->how are you?<P>'
                      '<I>I am fine.</I>', fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (True, '<B>Hello!</B> how are you?', 'bi\nngo\n!'), (False, '<P>', ''),
       (True, '<I>I am fine.</I>', '')])
 
@@ -102,7 +102,7 @@
     # translateable, it will actually apply to the second translateable.
     chunks = p.Parse('<B>Hello!</B> how are you?<!-- desc=bingo! --><P>'
                      '<I>I am fine.</I>', fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (True, '<B>Hello!</B> how are you?', ''), (False, '<P>', ''),
       (True, '<I>I am fine.</I>', 'bingo!')])
 
@@ -112,7 +112,7 @@
     p = tr_html.HtmlChunks()
     chunks = p.Parse('<b>Hello!</b> how are you?<p [BINGO] [$~BONGO~$]>'
                      '<i>I am fine.</i>', fold_whitespace)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (True, '<b>Hello!</b> how are you?', ''),
       (False, '<p [BINGO] [$~BONGO~$]>', ''),
       (True, '<i>I am fine.</i>', '')])
@@ -122,12 +122,12 @@
     p = tr_html.HtmlChunks()
     chunks = p.Parse('<textarea>Hello\nthere\nhow\nare\nyou?</textarea>',
                      fold_whitespace)
-    self.failUnlessEqual(chunks, [(False, '<textarea>', ''),
+    self.assertEqual(chunks, [(False, '<textarea>', ''),
       (True, 'Hello\nthere\nhow\nare\nyou?', ''), (False, '</textarea>', '')])
 
     # ...and that other tags' line breaks are converted to spaces
     chunks = p.Parse('<p>Hello\nthere\nhow\nare\nyou?</p>', fold_whitespace)
-    self.failUnlessEqual(chunks, [(False, '<p>', ''),
+    self.assertEqual(chunks, [(False, '<p>', ''),
       (True, 'Hello there how are you?', ''), (False, '</p>', '')])
 
   def VerifyChunkingMessageBreak(self, fold_whitespace):
@@ -135,7 +135,7 @@
     # Make sure that message-break comments work properly.
     chunks = p.Parse('Break<!-- message-break --> apart '
                      '<!--message-break-->messages', fold_whitespace)
-    self.failUnlessEqual(chunks, [(True, 'Break', ''),
+    self.assertEqual(chunks, [(True, 'Break', ''),
                                   (False, ' ', ''),
                                   (True, 'apart', ''),
                                   (False, ' ', ''),
@@ -144,7 +144,7 @@
     # Make sure message-break comments work in an inline tag.
     chunks = p.Parse('<a href=\'google.com\'><!-- message-break -->Google'
                      '<!--message-break--></a>', fold_whitespace)
-    self.failUnlessEqual(chunks, [(False, '<a href=\'google.com\'>', ''),
+    self.assertEqual(chunks, [(False, '<a href=\'google.com\'>', ''),
                                   (True, 'Google', ''),
                                   (False, '</a>', '')])
 
@@ -153,12 +153,12 @@
     # Make sure that message-no-break comments work properly.
     chunks = p.Parse('Please <!-- message-no-break --> <br />don\'t break',
                      fold_whitespace)
-    self.failUnlessEqual(chunks, [(True, 'Please <!-- message-no-break --> '
+    self.assertEqual(chunks, [(True, 'Please <!-- message-no-break --> '
                          '<br />don\'t break', '')])
 
     chunks = p.Parse('Please <br /> break. <!-- message-no-break --> <br /> '
                      'But not this time.', fold_whitespace)
-    self.failUnlessEqual(chunks, [(True, 'Please', ''),
+    self.assertEqual(chunks, [(True, 'Please', ''),
                                   (False, ' <br /> ', ''),
                                   (True, 'break. <!-- message-no-break --> '
                                          '<br /> But not this time.', '')])
@@ -172,7 +172,7 @@
                      '<input type=submit value="hello">'
                      '<input type="button" value="hello">'
                      '<input type=\'text\' value=\'Howdie\'>', False)
-    self.failUnlessEqual(chunks, [
+    self.assertEqual(chunks, [
       (False, '<img src=bingo.jpg alt="', ''), (True, 'hello there', ''),
       (False, '"><input type=submit value="', ''), (True, 'hello', ''),
       (False, '"><input type="button" value="', ''), (True, 'hello', ''),
@@ -184,13 +184,13 @@
     msg = tr_html.HtmlToMessage(
       'Hello <b>[USERNAME]</b>, &lt;how&gt;&nbsp;<i>are</i> you?')
     pres = msg.GetPresentableContent()
-    self.failUnless(pres ==
+    self.assertTrue(pres ==
                     'Hello BEGIN_BOLDX_USERNAME_XEND_BOLD, '
                     '<how>&nbsp;BEGIN_ITALICareEND_ITALIC you?')
 
     msg = tr_html.HtmlToMessage('<b>Hello</b><I>Hello</I><b>Hello</b>')
     pres = msg.GetPresentableContent()
-    self.failUnless(pres ==
+    self.assertTrue(pres ==
                     'BEGIN_BOLD_1HelloEND_BOLD_1BEGIN_ITALICHelloEND_ITALIC'
                     'BEGIN_BOLD_2HelloEND_BOLD_2')
 
@@ -202,34 +202,34 @@
       '''New Features</a>: Now search PDFs, MP3s, Firefox web history, and '''
       '''more</font>''')
     pres = msg.GetPresentableContent()
-    self.failUnless(pres ==
+    self.assertTrue(pres ==
                     'BEGIN_FONT_1BEGIN_FONT_2Update!END_FONT_2 BEGIN_LINK'
                     'New FeaturesEND_LINK: Now search PDFs, MP3s, Firefox '
                     'web history, and moreEND_FONT_1')
 
     msg = tr_html.HtmlToMessage('''<a href='[$~URL~$]'><b>[NUM][CAT]</b></a>''')
     pres = msg.GetPresentableContent()
-    self.failUnless(pres == 'BEGIN_LINKBEGIN_BOLDX_NUM_XX_CAT_XEND_BOLDEND_LINK')
+    self.assertTrue(pres == 'BEGIN_LINKBEGIN_BOLDX_NUM_XX_CAT_XEND_BOLDEND_LINK')
 
     msg = tr_html.HtmlToMessage(
       '''<font size=-1><a class=q onClick='return window.qs?qs(this):1' '''
       '''href='http://[WEBSERVER][SEARCH_URI]'>Desktop</a></font>&nbsp;&nbsp;'''
       '''&nbsp;&nbsp;''')
     pres = msg.GetPresentableContent()
-    self.failUnless(pres ==
+    self.assertTrue(pres ==
                     '''BEGIN_FONTBEGIN_LINKDesktopEND_LINKEND_FONTSPACE''')
 
     msg = tr_html.HtmlToMessage(
       '''<br><br><center><font size=-2>&copy;2005 Google </font></center>''', 1)
     pres = msg.GetPresentableContent()
-    self.failUnless(pres ==
-                    u'BEGIN_BREAK_1BEGIN_BREAK_2BEGIN_CENTERBEGIN_FONT\xa92005'
-                    u' Google END_FONTEND_CENTER')
+    self.assertTrue(pres ==
+                    'BEGIN_BREAK_1BEGIN_BREAK_2BEGIN_CENTERBEGIN_FONT\xa92005'
+                    ' Google END_FONTEND_CENTER')
 
     msg = tr_html.HtmlToMessage(
       '''&nbsp;-&nbsp;<a class=c href=[$~CACHE~$]>Cached</a>''')
     pres = msg.GetPresentableContent()
-    self.failUnless(pres ==
+    self.assertTrue(pres ==
                     '&nbsp;-&nbsp;BEGIN_LINKCachedEND_LINK')
 
     # Check that upper-case tags are handled correctly.
@@ -238,7 +238,7 @@
       '''html?hl=[LANG_CODE]'>Privacy Policy</A> and <A HREF='http://desktop'''
       '''.google.com/privacyfaq.html?hl=[LANG_CODE]'>Privacy FAQ</A> online.''')
     pres = msg.GetPresentableContent()
-    self.failUnless(pres ==
+    self.assertTrue(pres ==
                     'You can read the BEGIN_LINK_1Privacy PolicyEND_LINK_1 and '
                     'BEGIN_LINK_2Privacy FAQEND_LINK_2 online.')
 
@@ -249,38 +249,38 @@
 <A HREF='http://desktop.google.com/privacypolicy.html?hl=[LANG_CODE]'>Privacy Policy</A>
 and <A HREF='http://desktop.google.com/privacyfaq.html?hl=[LANG_CODE]'>Privacy FAQ</A> online.''')
     pres = msg.GetPresentableContent()
-    self.failUnless(pres == '''You can read the
+    self.assertTrue(pres == '''You can read the
 BEGIN_LINK_1Privacy PolicyEND_LINK_1
 and BEGIN_LINK_2Privacy FAQEND_LINK_2 online.''')
 
     # Check that message-no-break comments are handled correctly.
     msg = tr_html.HtmlToMessage('''Please <!-- message-no-break --><br /> don't break''')
     pres = msg.GetPresentableContent()
-    self.failUnlessEqual(pres, '''Please BREAK don't break''')
+    self.assertEqual(pres, '''Please BREAK don't break''')
 
 class TrHtmlUnittest(unittest.TestCase):
   def testSetAttributes(self):
     html = tr_html.TrHtml(io.StringIO(''))
-    self.failUnlessEqual(html.fold_whitespace_, False)
+    self.assertEqual(html.fold_whitespace_, False)
     html.SetAttributes({})
-    self.failUnlessEqual(html.fold_whitespace_, False)
+    self.assertEqual(html.fold_whitespace_, False)
     html.SetAttributes({'fold_whitespace': 'false'})
-    self.failUnlessEqual(html.fold_whitespace_, False)
+    self.assertEqual(html.fold_whitespace_, False)
     html.SetAttributes({'fold_whitespace': 'true'})
-    self.failUnlessEqual(html.fold_whitespace_, True)
+    self.assertEqual(html.fold_whitespace_, True)
 
   def testFoldWhitespace(self):
     text = '<td>   Test     Message   </td>'
 
     html = tr_html.TrHtml(io.StringIO(text))
     html.Parse()
-    self.failUnlessEqual(html.skeleton_[1].GetMessage().GetPresentableContent(),
+    self.assertEqual(html.skeleton_[1].GetMessage().GetPresentableContent(),
                          'Test  Message')
 
     html = tr_html.TrHtml(io.StringIO(text))
     html.fold_whitespace_ = True
     html.Parse()
-    self.failUnlessEqual(html.skeleton_[1].GetMessage().GetPresentableContent(),
+    self.assertEqual(html.skeleton_[1].GetMessage().GetPresentableContent(),
                          'Test Message')
 
   def testTable(self):
@@ -292,7 +292,7 @@
 </td>
 </tr></table>'''))
     html.Parse()
-    self.failUnless(html.skeleton_[3].GetMessage().GetPresentableContent() ==
+    self.assertTrue(html.skeleton_[3].GetMessage().GetPresentableContent() ==
                     'BEGIN_LINKPreferences&nbsp;HelpEND_LINK')
 
   def testSubmitAttribute(self):
@@ -302,7 +302,7 @@
 name=submit2></td>
 </tr></table>'''))
     html.Parse()
-    self.failUnless(html.skeleton_[1].GetMessage().GetPresentableContent() ==
+    self.assertTrue(html.skeleton_[1].GetMessage().GetPresentableContent() ==
                     'Save Preferences')
 
   def testWhitespaceAfterInlineTag(self):
@@ -312,7 +312,7 @@
     html = tr_html.TrHtml(
         io.StringIO('''<label for=DISPLAYNONE><font size=-1> Hello</font>'''))
     html.Parse()
-    self.failUnless(html.skeleton_[1].GetMessage().GetRealContent() ==
+    self.assertTrue(html.skeleton_[1].GetMessage().GetRealContent() ==
                     '<font size=-1> Hello</font>')
 
   def testSillyHeader(self):
@@ -326,11 +326,11 @@
 <p>Other stuff</p>'''))
     html.Parse()
     content = html.skeleton_[1].GetMessage().GetRealContent()
-    self.failUnless(content == 'Hello')
-    self.failUnless(html.skeleton_[-1] == '</p>')
+    self.assertTrue(content == 'Hello')
+    self.assertTrue(html.skeleton_[-1] == '</p>')
     # Right after the translateable the nontranslateable should start with
     # a linebreak (this catches a bug we had).
-    self.failUnless(html.skeleton_[2][0] == '\n')
+    self.assertTrue(html.skeleton_[2][0] == '\n')
 
 
   def testExplicitDescriptions(self):
@@ -339,16 +339,16 @@
                     '<input type="button">Go!</input>'))
     html.Parse()
     msg = html.GetCliques()[1].GetMessage()
-    self.failUnlessEqual(msg.GetDescription(), 'explicit')
-    self.failUnlessEqual(msg.GetRealContent(), 'Go!')
+    self.assertEqual(msg.GetDescription(), 'explicit')
+    self.assertEqual(msg.GetRealContent(), 'Go!')
 
     html = tr_html.TrHtml(
         io.StringIO('Hello [USER]<br/><!-- desc=explicit\nmultiline -->'
                     '<input type="button">Go!</input>'))
     html.Parse()
     msg = html.GetCliques()[1].GetMessage()
-    self.failUnlessEqual(msg.GetDescription(), 'explicit multiline')
-    self.failUnlessEqual(msg.GetRealContent(), 'Go!')
+    self.assertEqual(msg.GetDescription(), 'explicit multiline')
+    self.assertEqual(msg.GetRealContent(), 'Go!')
 
 
   def testRegressionInToolbarAbout(self):
@@ -358,7 +358,7 @@
     for cl in cliques:
       content = cl.GetMessage().GetRealContent()
       if content.count('De parvis grandis acervus erit'):
-        self.failIf(content.count('$/translate'))
+        self.assertFalse(content.count('$/translate'))
 
 
   def HtmlFromFileWithManualCheck(self, f):
@@ -380,10 +380,10 @@
     html = self.HtmlFromFileWithManualCheck(
       util.PathFromRoot(r'grit/testdata/privacy.html'))
 
-    self.failUnless(html.skeleton_[1].GetMessage().GetRealContent() ==
+    self.assertTrue(html.skeleton_[1].GetMessage().GetRealContent() ==
                     'Privacy and Google Desktop Search')
-    self.failUnless(html.skeleton_[3].startswith('<'))
-    self.failUnless(len(html.skeleton_) > 10)
+    self.assertTrue(html.skeleton_[3].startswith('<'))
+    self.assertTrue(len(html.skeleton_) > 10)
 
 
   def testPreferencesHtml(self):
@@ -400,7 +400,7 @@
           item.GetMessage().GetRealContent() == '[ADDIN-DO] [ADDIN-OPTIONS]'):
         self.fail()
 
-    self.failUnless(len(html.skeleton_) > 100)
+    self.assertTrue(len(html.skeleton_) > 100)
 
   def AssertNumberOfTranslateables(self, files, num):
     '''Fails if any of the files in files don't have exactly
@@ -413,7 +413,7 @@
     for f in files:
       f = util.PathFromRoot(r'grit/testdata/%s' % f)
       html = self.HtmlFromFileWithManualCheck(f)
-      self.failUnless(len(html.GetCliques()) == num)
+      self.assertTrue(len(html.GetCliques()) == num)
 
   def testFewTranslateables(self):
     self.AssertNumberOfTranslateables(['browser.html', 'email_thread.html',
@@ -481,29 +481,29 @@
     msg = tr_html.HtmlToMessage(
       'Hello<p>Howdie<img alt="bingo" src="image.gif">', True)
     result = msg.GetPresentableContent()
-    self.failUnless(
+    self.assertTrue(
       result == 'HelloBEGIN_PARAGRAPHHowdieBEGIN_BLOCKbingoEND_BLOCK')
 
     msg = tr_html.HtmlToMessage(
       'Hello<p>Howdie<input type="button" value="bingo">', True)
     result = msg.GetPresentableContent()
-    self.failUnless(
+    self.assertTrue(
       result == 'HelloBEGIN_PARAGRAPHHowdieBEGIN_BLOCKbingoEND_BLOCK')
 
 
   def testHtmlToMessageRegressions(self):
     msg = tr_html.HtmlToMessage(' - ', True)
     result = msg.GetPresentableContent()
-    self.failUnless(result == ' - ')
+    self.assertTrue(result == ' - ')
 
 
   def testEscapeUnescaped(self):
     text = '&copy;&nbsp; & &quot;&lt;hello&gt;&quot;'
     unescaped = util.UnescapeHtml(text)
-    self.failUnless(unescaped == u'\u00a9\u00a0 & "<hello>"')
+    self.assertTrue(unescaped == '\u00a9\u00a0 & "<hello>"')
     escaped_unescaped = util.EscapeHtml(unescaped, True)
-    self.failUnless(escaped_unescaped ==
-                    u'\u00a9\u00a0 &amp; &quot;&lt;hello&gt;&quot;')
+    self.assertTrue(escaped_unescaped ==
+                    '\u00a9\u00a0 &amp; &quot;&lt;hello&gt;&quot;')
 
   def testRegressionCjkHtmlFile(self):
     # TODO(joi) Fix this problem where unquoted attributes that
@@ -512,7 +512,7 @@
     if False:
       html = self.HtmlFromFileWithManualCheck(util.PathFromRoot(
         r'grit/testdata/ko_oem_enable_bug.html'))
-      self.failUnless(True)
+      self.assertTrue(True)
 
   def testRegressionCpuHang(self):
     # If this regression occurs, the unit test will never return
diff --git a/tools/grit/grit/gather/txt.py b/tools/grit/grit/gather/txt.py
index 3459a77..94a77dd 100644
--- a/tools/grit/grit/gather/txt.py
+++ b/tools/grit/grit/gather/txt.py
@@ -5,7 +5,6 @@
 '''Supports making amessage from a text file.
 '''
 
-from __future__ import print_function
 
 from grit.gather import interface
 from grit import tclib
diff --git a/tools/grit/grit/gather/txt_unittest.py b/tools/grit/grit/gather/txt_unittest.py
index 75dc502..fd34be2 100755
--- a/tools/grit/grit/gather/txt_unittest.py
+++ b/tools/grit/grit/gather/txt_unittest.py
@@ -21,9 +21,9 @@
     input = io.StringIO('Hello there\nHow are you?')
     gatherer = txt.TxtFile(input)
     gatherer.Parse()
-    self.failUnless(gatherer.GetText() == input.getvalue())
-    self.failUnless(len(gatherer.GetCliques()) == 1)
-    self.failUnless(gatherer.GetCliques()[0].GetMessage().GetRealContent() ==
+    self.assertTrue(gatherer.GetText() == input.getvalue())
+    self.assertTrue(len(gatherer.GetCliques()) == 1)
+    self.assertTrue(gatherer.GetCliques()[0].GetMessage().GetRealContent() ==
                     input.getvalue())
 
 
diff --git a/tools/grit/grit/grd_reader.py b/tools/grit/grit/grd_reader.py
index 3bb61f7..5cc3702d 100755
--- a/tools/grit/grit/grd_reader.py
+++ b/tools/grit/grit/grd_reader.py
@@ -6,7 +6,6 @@
 '''Class for reading GRD files into memory, without processing them.
 '''
 
-from __future__ import print_function
 
 import os.path
 import sys
@@ -200,7 +199,7 @@
     grit.exception.Parsing
   '''
 
-  if isinstance(filename_or_stream, six.string_types):
+  if isinstance(filename_or_stream, str):
     source = filename_or_stream
     if dir is None:
       dir = util.dirname(filename_or_stream)
@@ -252,4 +251,4 @@
 
 if __name__ == '__main__':
   util.ChangeStdoutEncoding()
-  print(six.text_type(Parse(sys.argv[1])))
+  print(str(Parse(sys.argv[1])))
diff --git a/tools/grit/grit/grd_reader_unittest.py b/tools/grit/grit/grd_reader_unittest.py
index f5fbb4c9..99d4a3c 100755
--- a/tools/grit/grit/grd_reader_unittest.py
+++ b/tools/grit/grit/grd_reader_unittest.py
@@ -23,7 +23,7 @@
 
 class GrdReaderUnittest(unittest.TestCase):
   def testParsingAndXmlOutput(self):
-    input = u'''<?xml version="1.0" encoding="UTF-8"?>
+    input = '''<?xml version="1.0" encoding="UTF-8"?>
 <grit base_dir="." current_release="3" latest_public_release="2" source_lang_id="en-US">
   <release seq="3">
     <includes>
@@ -54,13 +54,13 @@
     pseudo_file = io.StringIO(input)
     tree = grd_reader.Parse(pseudo_file, '.')
     output = str(tree)
-    expected_output = input.replace(u' base_dir="."', u'')
+    expected_output = input.replace(' base_dir="."', '')
     self.assertEqual(expected_output, output)
-    self.failUnless(tree.GetNodeById('IDS_GREETING'))
+    self.assertTrue(tree.GetNodeById('IDS_GREETING'))
 
 
   def testStopAfter(self):
-    input = u'''<?xml version="1.0" encoding="UTF-8"?>
+    input = '''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
   <outputs>
     <output filename="resource.h" type="rc_header" />
@@ -75,11 +75,11 @@
     pseudo_file = io.StringIO(input)
     tree = grd_reader.Parse(pseudo_file, '.', stop_after='outputs')
     # only an <outputs> child
-    self.failUnless(len(tree.children) == 1)
-    self.failUnless(tree.children[0].name == 'outputs')
+    self.assertTrue(len(tree.children) == 1)
+    self.assertTrue(tree.children[0].name == 'outputs')
 
   def testLongLinesWithComments(self):
-    input = u'''<?xml version="1.0" encoding="UTF-8"?>
+    input = '''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
   <release seq="3">
     <messages>
@@ -95,12 +95,12 @@
     tree = grd_reader.Parse(pseudo_file, '.')
 
     greeting = tree.GetNodeById('IDS_GREETING')
-    self.failUnless(greeting.GetCliques()[0].GetMessage().GetRealContent() ==
+    self.assertTrue(greeting.GetCliques()[0].GetMessage().GetRealContent() ==
                     'This is a very long line with no linebreaks yes yes it '
                     'stretches on and on and on!')
 
   def doTestAssignFirstIds(self, first_ids_path):
-    input = u'''<?xml version="1.0" encoding="UTF-8"?>
+    input = '''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
       base_dir="." first_ids_file="%s">
   <release seq="3">
@@ -119,8 +119,8 @@
     root = grd_reader.Parse(pseudo_file, os.path.split(fake_input_path)[0])
     root.AssignFirstIds(fake_input_path, {})
     messages_node = root.children[0].children[0]
-    self.failUnless(isinstance(messages_node, empty.MessagesNode))
-    self.failUnless(messages_node.attrs["first_id"] !=
+    self.assertTrue(isinstance(messages_node, empty.MessagesNode))
+    self.assertTrue(messages_node.attrs["first_id"] !=
         empty.MessagesNode().DefaultAttributes()["first_id"])
 
   def testAssignFirstIds(self):
@@ -132,7 +132,7 @@
   def testAssignFirstIdsMultipleMessages(self):
     """If there are multiple messages sections, the resource_ids file
     needs to list multiple first_id values."""
-    input = u'''<?xml version="1.0" encoding="UTF-8"?>
+    input = '''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
       base_dir="." first_ids_file="resource_ids">
   <release seq="3">
@@ -163,7 +163,7 @@
     self.assertEqual('10000', messages_node.attrs["first_id"])
 
   def testUseNameForIdAndPpIfdef(self):
-    input = u'''<?xml version="1.0" encoding="UTF-8"?>
+    input = '''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
   <release seq="3">
     <messages>
@@ -181,10 +181,10 @@
     # Check if the ID is set to the name. In the past, there was a bug
     # that caused the ID to be a generated number.
     hello = root.GetNodeById('IDS_HELLO')
-    self.failUnless(hello.GetCliques()[0].GetId() == 'IDS_HELLO')
+    self.assertTrue(hello.GetCliques()[0].GetId() == 'IDS_HELLO')
 
   def testUseNameForIdWithIfElse(self):
-    input = u'''<?xml version="1.0" encoding="UTF-8"?>
+    input = '''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
   <release seq="3">
     <messages>
@@ -209,16 +209,16 @@
     # Check if the ID is set to the name. In the past, there was a bug
     # that caused the ID to be a generated number.
     hello = root.GetNodeById('IDS_HELLO')
-    self.failUnless(hello.GetCliques()[0].GetId() == 'IDS_HELLO')
+    self.assertTrue(hello.GetCliques()[0].GetId() == 'IDS_HELLO')
 
   def testPartInclusionAndCorrectSource(self):
-    arbitrary_path_grd = u'''\
+    arbitrary_path_grd = '''\
         <grit-part>
           <message name="IDS_TEST5" desc="test5">test5</message>
         </grit-part>'''
     tmp_dir = util.TempDir({'arbitrary_path.grp': arbitrary_path_grd})
     arbitrary_path_grd_file = tmp_dir.GetPath('arbitrary_path.grp')
-    top_grd = u'''\
+    top_grd = '''\
         <grit latest_public_release="2" current_release="3">
           <release seq="3">
             <messages>
@@ -230,17 +230,17 @@
             </messages>
           </release>
         </grit>''' % arbitrary_path_grd_file
-    sub_grd = u'''\
+    sub_grd = '''\
         <grit-part>
           <message name="IDS_TEST2" desc="test2">test2</message>
           <part file="subsub.grp" />
           <message name="IDS_TEST3" desc="test3">test3</message>
         </grit-part>'''
-    subsub_grd = u'''\
+    subsub_grd = '''\
         <grit-part>
           <message name="IDS_TEST4" desc="test4">test4</message>
         </grit-part>'''
-    expected_output = u'''\
+    expected_output = '''\
         <grit current_release="3" latest_public_release="2">
           <release seq="3">
             <messages>
@@ -288,7 +288,7 @@
     tmp_dir.CleanUp()
 
   def testPartInclusionFailure(self):
-    template = u'''
+    template = '''
       <grit latest_public_release="2" current_release="3">
         <outputs>
           %s
@@ -296,28 +296,28 @@
       </grit>'''
 
     part_failures = [
-        (exception.UnexpectedContent, u'<part file="x">fnord</part>'),
+        (exception.UnexpectedContent, '<part file="x">fnord</part>'),
         (exception.UnexpectedChild,
-         u'<part file="x"><output filename="x" type="y" /></part>'),
-        (exception.FileNotFound, u'<part file="yet_created_x" />'),
+         '<part file="x"><output filename="x" type="y" /></part>'),
+        (exception.FileNotFound, '<part file="yet_created_x" />'),
     ]
     for raises, data in part_failures:
       data = io.StringIO(template % data)
       self.assertRaises(raises, grd_reader.Parse, data, '.')
 
     gritpart_failures = [
-        (exception.UnexpectedAttribute, u'<grit-part file="xyz"></grit-part>'),
-        (exception.MissingElement, u'<output filename="x" type="y" />'),
+        (exception.UnexpectedAttribute, '<grit-part file="xyz"></grit-part>'),
+        (exception.MissingElement, '<output filename="x" type="y" />'),
     ]
     for raises, data in gritpart_failures:
-      top_grd = io.StringIO(template % u'<part file="bad.grp" />')
+      top_grd = io.StringIO(template % '<part file="bad.grp" />')
       with util.TempDir({'bad.grp': data}) as temp_dir:
         self.assertRaises(raises, grd_reader.Parse, top_grd, temp_dir.GetPath())
 
   def testEarlyEnoughPlatformSpecification(self):
     # This is a regression test for issue
     # https://code.google.com/p/grit-i18n/issues/detail?id=23
-    grd_text = u'''<?xml version="1.0" encoding="UTF-8"?>
+    grd_text = '''<?xml version="1.0" encoding="UTF-8"?>
       <grit latest_public_release="1" current_release="1">
         <release seq="1">
           <messages>
@@ -339,7 +339,7 @@
                        target_platform='android')
 
   def testSkipValidationChecks(self):
-    input = u'''<?xml version="1.0" encoding="UTF-8"?>
+    input = '''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
   <release seq="3">
     <messages>
diff --git a/tools/grit/grit/grit_runner.py b/tools/grit/grit/grit_runner.py
index 77c37023..8c8d637 100755
--- a/tools/grit/grit/grit_runner.py
+++ b/tools/grit/grit/grit_runner.py
@@ -7,7 +7,6 @@
 GRIT tools.
 """
 
-from __future__ import print_function
 
 import os
 import sys
@@ -196,7 +195,7 @@
 """ % (tool_list))
 
 
-class Options(object):
+class Options:
   """Option storage and parsing."""
 
   def __init__(self):
diff --git a/tools/grit/grit/grit_runner_unittest.py b/tools/grit/grit/grit_runner_unittest.py
index c499bab..889dbd03 100755
--- a/tools/grit/grit/grit_runner_unittest.py
+++ b/tools/grit/grit/grit_runner_unittest.py
@@ -30,9 +30,9 @@
                            util.PathFromRoot('grit/testdata/simple-input.xml'),
                            'test', 'bla', 'voff', 'ga'])
     output = self.buf.getvalue()
-    self.failUnless(output.count("'test'") == 0)  # tool name doesn't occur
-    self.failUnless(output.count('bla'))
-    self.failUnless(output.count('simple-input.xml'))
+    self.assertTrue(output.count("'test'") == 0)  # tool name doesn't occur
+    self.assertTrue(output.count('bla'))
+    self.assertTrue(output.count('simple-input.xml'))
 
 
 if __name__ == '__main__':
diff --git a/tools/grit/grit/lazy_re.py b/tools/grit/grit/lazy_re.py
index c06517c..2b17685 100644
--- a/tools/grit/grit/lazy_re.py
+++ b/tools/grit/grit/lazy_re.py
@@ -8,12 +8,11 @@
 time in some cases.
 '''
 
-from __future__ import print_function
 
 import re
 
 
-class LazyRegexObject(object):
+class LazyRegexObject:
   '''This object creates a RegexObject with the arguments passed in
   its constructor, the first time any attribute except the several on
   the class itself is accessed.  This accomplishes lazy compilation of
diff --git a/tools/grit/grit/lazy_re_unittest.py b/tools/grit/grit/lazy_re_unittest.py
index 85a271c..21fddfa8 100755
--- a/tools/grit/grit/lazy_re_unittest.py
+++ b/tools/grit/grit/lazy_re_unittest.py
@@ -6,7 +6,6 @@
 '''Unit test for lazy_re.
 '''
 
-from __future__ import print_function
 
 import os
 import sys
diff --git a/tools/grit/grit/node/base.py b/tools/grit/grit/node/base.py
index b526a7c8..3f14a9949 100644
--- a/tools/grit/grit/node/base.py
+++ b/tools/grit/grit/node/base.py
@@ -19,7 +19,7 @@
 import grit.format.gzip_string
 
 
-class Node(object):
+class Node:
   '''An item in the tree that has children.'''
 
   # Valid content types that can be returned by _ContentType()
@@ -49,7 +49,7 @@
     self.mixed_content = []   # A list of u'' and/or child elements (this
     # duplicates 'children' but
     # is needed to preserve markup-type content).
-    self.name = u''           # The name of this element
+    self.name = ''           # The name of this element
     self.attrs = {}           # The set of attributes (keys to values)
     self.parent = None        # Our parent unless we are the root element.
     self.uberclique = None    # Allows overriding uberclique for parts of tree
@@ -63,7 +63,7 @@
 
   def __exit__(self, exc_type, exc_value, traceback):
     if exc_type is not None:
-      print(u'Error processing node %s: %s' % (str(self), exc_value))
+      print('Error processing node %s: %s' % (str(self), exc_value))
 
   def __iter__(self):
     '''A preorder iteration through the tree that this node is the root of.'''
@@ -74,8 +74,7 @@
     any child nodes.'''
     yield self
     for child in self.children:
-      for iterchild in child.Preorder():
-        yield iterchild
+      yield from child.Preorder()
 
   def ActiveChildren(self):
     '''Returns the children of this node that should be included in the current
@@ -87,8 +86,7 @@
     the current configuration, in preorder.'''
     yield self
     for child in self.ActiveChildren():
-      for descendant in child.ActiveDescendants():
-        yield descendant
+      yield from child.ActiveDescendants()
 
   def GetRoot(self):
     '''Returns the root Node in the tree this Node belongs to.'''
@@ -257,13 +255,13 @@
   def __str__(self):
     '''Returns this node and all nodes below it as an XML document in a Unicode
     string.'''
-    header = u'<?xml version="1.0" encoding="UTF-8"?>\n'
+    header = '<?xml version="1.0" encoding="UTF-8"?>\n'
     return header + self.FormatXml()
 
   # Some Python 2 glue.
   __unicode__ = __str__
 
-  def FormatXml(self, indent = u'', one_line = False):
+  def FormatXml(self, indent = '', one_line = False):
     '''Returns this node and all nodes below it as an XML
     element in a Unicode string.  This differs from __unicode__ in that it does
     not include the <?xml> stuff at the top of the string.  If one_line is true,
@@ -277,30 +275,30 @@
     inside_content = self.ContentsAsXml(indent, content_one_line)
 
     # Then the attributes for this node.
-    attribs = u''
+    attribs = ''
     default_attribs = self.DefaultAttributes()
     for attrib, value in sorted(self.attrs.items()):
       # Only print an attribute if it is other than the default value.
       if attrib not in default_attribs or value != default_attribs[attrib]:
-        attribs += u' %s=%s' % (attrib, saxutils.quoteattr(value))
+        attribs += ' %s=%s' % (attrib, saxutils.quoteattr(value))
 
     # Finally build the XML for our node and return it
     if len(inside_content) > 0:
       if one_line:
-        return u'<%s%s>%s</%s>' % (self.name, attribs, inside_content,
+        return '<%s%s>%s</%s>' % (self.name, attribs, inside_content,
                                    self.name)
       elif content_one_line:
-        return u'%s<%s%s>\n%s  %s\n%s</%s>' % (
+        return '%s<%s%s>\n%s  %s\n%s</%s>' % (
           indent, self.name, attribs,
           indent, inside_content,
           indent, self.name)
       else:
-        return u'%s<%s%s>\n%s\n%s</%s>' % (
+        return '%s<%s%s>\n%s\n%s</%s>' % (
           indent, self.name, attribs,
           inside_content,
           indent, self.name)
     else:
-      return u'%s<%s%s />' % (indent, self.name, attribs)
+      return '%s<%s%s />' % (indent, self.name, attribs)
 
   def ContentsAsXml(self, indent, one_line):
     '''Returns the contents of this node (CDATA and child elements) in XML
@@ -312,15 +310,15 @@
     last_item = None
     for mixed_item in self.mixed_content:
       if isinstance(mixed_item, Node):
-        inside_parts.append(mixed_item.FormatXml(indent + u'  ', one_line))
+        inside_parts.append(mixed_item.FormatXml(indent + '  ', one_line))
         if not one_line:
-          inside_parts.append(u'\n')
+          inside_parts.append('\n')
       else:
         message = mixed_item
         # If this is the first item and it starts with whitespace, we add
         # the ''' delimiter.
         if not last_item and message.lstrip() != message:
-          message = u"'''" + message
+          message = "'''" + message
         inside_parts.append(util.EncodeCdata(message))
       last_item = mixed_item
 
@@ -332,9 +330,9 @@
     # If the last item is a string (not a node) and ends with whitespace,
     # we need to add the ''' delimiter.
     if (isinstance(last_item, str) and last_item.rstrip() != last_item):
-      inside_parts[-1] = inside_parts[-1] + u"'''"
+      inside_parts[-1] = inside_parts[-1] + "'''"
 
-    return u''.join(inside_parts)
+    return ''.join(inside_parts)
 
   def SubstituteMessages(self, substituter):
     '''Applies substitutions to all messages in the tree.
diff --git a/tools/grit/grit/node/base_unittest.py b/tools/grit/grit/node/base_unittest.py
index 732d6e2..963d749 100755
--- a/tools/grit/grit/node/base_unittest.py
+++ b/tools/grit/grit/node/base_unittest.py
@@ -21,9 +21,9 @@
 
 def MakePlaceholder(phname='BINGO'):
   ph = message.PhNode()
-  ph.StartParsing(u'ph', None)
-  ph.HandleAttribute(u'name', phname)
-  ph.AppendContent(u'bongo')
+  ph.StartParsing('ph', None)
+  ph.HandleAttribute('name', phname)
+  ph.AppendContent('bongo')
   ph.EndParsing()
   return ph
 
@@ -32,49 +32,49 @@
   def testWhitespaceHandling(self):
     # We test using the Message node type.
     node = message.MessageNode()
-    node.StartParsing(u'hello', None)
-    node.HandleAttribute(u'name', u'bla')
-    node.AppendContent(u" '''  two spaces  ")
+    node.StartParsing('hello', None)
+    node.HandleAttribute('name', 'bla')
+    node.AppendContent(" '''  two spaces  ")
     node.EndParsing()
-    self.failUnless(node.GetCdata() == u'  two spaces')
+    self.assertTrue(node.GetCdata() == '  two spaces')
 
     node = message.MessageNode()
-    node.StartParsing(u'message', None)
-    node.HandleAttribute(u'name', u'bla')
-    node.AppendContent(u"  two spaces  '''  ")
+    node.StartParsing('message', None)
+    node.HandleAttribute('name', 'bla')
+    node.AppendContent("  two spaces  '''  ")
     node.EndParsing()
-    self.failUnless(node.GetCdata() == u'two spaces  ')
+    self.assertTrue(node.GetCdata() == 'two spaces  ')
 
   def testWhitespaceHandlingWithChildren(self):
     # We test using the Message node type.
     node = message.MessageNode()
-    node.StartParsing(u'message', None)
-    node.HandleAttribute(u'name', u'bla')
-    node.AppendContent(u" '''  two spaces  ")
+    node.StartParsing('message', None)
+    node.HandleAttribute('name', 'bla')
+    node.AppendContent(" '''  two spaces  ")
     node.AddChild(MakePlaceholder())
-    node.AppendContent(u' space before and after ')
+    node.AppendContent(' space before and after ')
     node.AddChild(MakePlaceholder('BONGO'))
-    node.AppendContent(u" space before two after  '''")
+    node.AppendContent(" space before two after  '''")
     node.EndParsing()
-    self.failUnless(node.mixed_content[0] == u'  two spaces  ')
-    self.failUnless(node.mixed_content[2] == u' space before and after ')
-    self.failUnless(node.mixed_content[-1] == u' space before two after  ')
+    self.assertTrue(node.mixed_content[0] == '  two spaces  ')
+    self.assertTrue(node.mixed_content[2] == ' space before and after ')
+    self.assertTrue(node.mixed_content[-1] == ' space before two after  ')
 
   def testXmlFormatMixedContent(self):
     # Again test using the Message node type, because it is the only mixed
     # content node.
     node = message.MessageNode()
-    node.StartParsing(u'message', None)
-    node.HandleAttribute(u'name', u'name')
-    node.AppendContent(u'Hello <young> ')
+    node.StartParsing('message', None)
+    node.HandleAttribute('name', 'name')
+    node.AppendContent('Hello <young> ')
 
     ph = message.PhNode()
-    ph.StartParsing(u'ph', None)
-    ph.HandleAttribute(u'name', u'USERNAME')
-    ph.AppendContent(u'$1')
+    ph.StartParsing('ph', None)
+    ph.HandleAttribute('name', 'USERNAME')
+    ph.AppendContent('$1')
     ex = message.ExNode()
-    ex.StartParsing(u'ex', None)
-    ex.AppendContent(u'Joi')
+    ex.StartParsing('ex', None)
+    ex.AppendContent('Joi')
     ex.EndParsing()
     ph.AddChild(ex)
     ph.EndParsing()
@@ -83,51 +83,51 @@
     node.EndParsing()
 
     non_indented_xml = node.FormatXml()
-    self.failUnless(non_indented_xml == u'<message name="name">\n  Hello '
-                    u'&lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
-                    u'\n</message>')
+    self.assertTrue(non_indented_xml == '<message name="name">\n  Hello '
+                    '&lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
+                    '\n</message>')
 
-    indented_xml = node.FormatXml(u'  ')
-    self.failUnless(indented_xml == u'  <message name="name">\n    Hello '
-                    u'&lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
-                    u'\n  </message>')
+    indented_xml = node.FormatXml('  ')
+    self.assertTrue(indented_xml == '  <message name="name">\n    Hello '
+                    '&lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
+                    '\n  </message>')
 
   def testXmlFormatMixedContentWithLeadingWhitespace(self):
     # Again test using the Message node type, because it is the only mixed
     # content node.
     node = message.MessageNode()
-    node.StartParsing(u'message', None)
-    node.HandleAttribute(u'name', u'name')
-    node.AppendContent(u"'''   Hello <young> ")
+    node.StartParsing('message', None)
+    node.HandleAttribute('name', 'name')
+    node.AppendContent("'''   Hello <young> ")
 
     ph = message.PhNode()
-    ph.StartParsing(u'ph', None)
-    ph.HandleAttribute(u'name', u'USERNAME')
-    ph.AppendContent(u'$1')
+    ph.StartParsing('ph', None)
+    ph.HandleAttribute('name', 'USERNAME')
+    ph.AppendContent('$1')
     ex = message.ExNode()
-    ex.StartParsing(u'ex', None)
-    ex.AppendContent(u'Joi')
+    ex.StartParsing('ex', None)
+    ex.AppendContent('Joi')
     ex.EndParsing()
     ph.AddChild(ex)
     ph.EndParsing()
 
     node.AddChild(ph)
-    node.AppendContent(u" yessiree '''")
+    node.AppendContent(" yessiree '''")
     node.EndParsing()
 
     non_indented_xml = node.FormatXml()
-    self.failUnless(non_indented_xml ==
-                    u"<message name=\"name\">\n  '''   Hello"
-                    u' &lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
-                    u" yessiree '''\n</message>")
+    self.assertTrue(non_indented_xml ==
+                    "<message name=\"name\">\n  '''   Hello"
+                    ' &lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
+                    " yessiree '''\n</message>")
 
-    indented_xml = node.FormatXml(u'  ')
-    self.failUnless(indented_xml ==
-                    u"  <message name=\"name\">\n    '''   Hello"
-                    u' &lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
-                    u" yessiree '''\n  </message>")
+    indented_xml = node.FormatXml('  ')
+    self.assertTrue(indented_xml ==
+                    "  <message name=\"name\">\n    '''   Hello"
+                    ' &lt;young&gt; <ph name="USERNAME">$1<ex>Joi</ex></ph>'
+                    " yessiree '''\n  </message>")
 
-    self.failUnless(node.GetNodeById('name'))
+    self.assertTrue(node.GetNodeById('name'))
 
   def testXmlFormatContentWithEntities(self):
     '''Tests a bug where &nbsp; would not be escaped correctly.'''
@@ -140,31 +140,31 @@
         tclib.Placeholder('END_BOLD', '</b>', 'bla')]),
                                              'BINGOBONGO')
     xml = msg_node.FormatXml()
-    self.failUnless(xml.find('&nbsp;') == -1, 'should have no entities')
+    self.assertTrue(xml.find('&nbsp;') == -1, 'should have no entities')
 
   def testIter(self):
     # First build a little tree of message and ph nodes.
     node = message.MessageNode()
-    node.StartParsing(u'message', None)
-    node.HandleAttribute(u'name', u'bla')
-    node.AppendContent(u" '''  two spaces  ")
-    node.AppendContent(u' space before and after ')
+    node.StartParsing('message', None)
+    node.HandleAttribute('name', 'bla')
+    node.AppendContent(" '''  two spaces  ")
+    node.AppendContent(' space before and after ')
     ph = message.PhNode()
-    ph.StartParsing(u'ph', None)
+    ph.StartParsing('ph', None)
     ph.AddChild(message.ExNode())
-    ph.HandleAttribute(u'name', u'BINGO')
-    ph.AppendContent(u'bongo')
+    ph.HandleAttribute('name', 'BINGO')
+    ph.AppendContent('bongo')
     node.AddChild(ph)
     node.AddChild(message.PhNode())
-    node.AppendContent(u" space before two after  '''")
+    node.AppendContent(" space before two after  '''")
 
     order = [
         message.MessageNode, message.PhNode, message.ExNode, message.PhNode
     ]
     for n in node:
-      self.failUnless(type(n) == order[0])
+      self.assertTrue(type(n) == order[0])
       order = order[1:]
-    self.failUnless(len(order) == 0)
+    self.assertTrue(len(order) == 0)
 
   def testGetChildrenOfType(self):
     xml = '''<?xml version="1.0" encoding="UTF-8"?>
@@ -189,14 +189,14 @@
                            util.PathFromRoot('grit/test/data'))
     from grit.node import node_io
     output_nodes = grd.GetChildrenOfType(node_io.OutputNode)
-    self.failUnlessEqual(len(output_nodes), 3)
-    self.failUnlessEqual(output_nodes[2].attrs['filename'],
+    self.assertEqual(len(output_nodes), 3)
+    self.assertEqual(output_nodes[2].attrs['filename'],
                          'de/generated_resources.rc')
 
   def testEvaluateExpression(self):
     def AssertExpr(expected_value, expr, defs, target_platform,
                    extra_variables):
-      self.failUnlessEqual(expected_value, base.Node.EvaluateExpression(
+      self.assertEqual(expected_value, base.Node.EvaluateExpression(
           expr, defs, target_platform, extra_variables))
 
     AssertExpr(True, "True", {}, 'linux', {})
diff --git a/tools/grit/grit/node/custom/filename.py b/tools/grit/grit/node/custom/filename.py
index e8045b36..0eafd22 100644
--- a/tools/grit/grit/node/custom/filename.py
+++ b/tools/grit/grit/node/custom/filename.py
@@ -4,7 +4,6 @@
 
 '''A CustomType for filenames.'''
 
-from __future__ import print_function
 
 from grit import clique
 from grit import lazy_re
diff --git a/tools/grit/grit/node/custom/filename_unittest.py b/tools/grit/grit/node/custom/filename_unittest.py
index 6c273a6..c12bcff 100755
--- a/tools/grit/grit/node/custom/filename_unittest.py
+++ b/tools/grit/grit/node/custom/filename_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.node.custom.filename'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -27,7 +26,7 @@
     c.SetCustomType(filename.WindowsFilename())
     translation = tclib.Translation(id=msg.GetId(), text='Bilingo bolongo:')
     c.AddTranslation(translation, 'fr')
-    self.failUnless(c.MessageForLanguage('fr').GetRealContent() == 'Bilingo bolongo ')
+    self.assertTrue(c.MessageForLanguage('fr').GetRealContent() == 'Bilingo bolongo ')
 
 
 if __name__ == '__main__':
diff --git a/tools/grit/grit/node/empty.py b/tools/grit/grit/node/empty.py
index 13f5ee5..ac5cd13e 100644
--- a/tools/grit/grit/node/empty.py
+++ b/tools/grit/grit/node/empty.py
@@ -5,7 +5,6 @@
 '''Container nodes that don't have any logic.
 '''
 
-from __future__ import print_function
 
 from grit.node import base
 from grit.node import include
diff --git a/tools/grit/grit/node/include.py b/tools/grit/grit/node/include.py
index c5ab2be..04d490ea 100644
--- a/tools/grit/grit/node/include.py
+++ b/tools/grit/grit/node/include.py
@@ -5,7 +5,6 @@
 """Handling of the <include> element.
 """
 
-from __future__ import print_function
 
 import os
 
@@ -19,7 +18,7 @@
   """An <include> element."""
 
   def __init__(self):
-    super(IncludeNode, self).__init__()
+    super().__init__()
 
     # Cache flattened data so that we don't flatten the same file
     # multiple times.
diff --git a/tools/grit/grit/node/include_unittest.py b/tools/grit/grit/node/include_unittest.py
index ad4edb4..6352678 100755
--- a/tools/grit/grit/node/include_unittest.py
+++ b/tools/grit/grit/node/include_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for include.IncludeNode'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -41,20 +40,20 @@
 class IncludeNodeUnittest(unittest.TestCase):
   def testGetPath(self):
     root = misc.GritNode()
-    root.StartParsing(u'grit', None)
-    root.HandleAttribute(u'latest_public_release', u'0')
-    root.HandleAttribute(u'current_release', u'1')
-    root.HandleAttribute(u'base_dir', r'..\resource')
+    root.StartParsing('grit', None)
+    root.HandleAttribute('latest_public_release', '0')
+    root.HandleAttribute('current_release', '1')
+    root.HandleAttribute('base_dir', r'..\resource')
     release = misc.ReleaseNode()
-    release.StartParsing(u'release', root)
-    release.HandleAttribute(u'seq', u'1')
+    release.StartParsing('release', root)
+    release.HandleAttribute('seq', '1')
     root.AddChild(release)
     includes = empty.IncludesNode()
-    includes.StartParsing(u'includes', release)
+    includes.StartParsing('includes', release)
     release.AddChild(includes)
     include_node = include.IncludeNode()
-    include_node.StartParsing(u'include', includes)
-    include_node.HandleAttribute(u'file', r'flugel\kugel.pdf')
+    include_node.StartParsing('include', includes)
+    include_node.HandleAttribute('file', r'flugel\kugel.pdf')
     includes.AddChild(include_node)
     root.EndParsing()
 
@@ -64,27 +63,27 @@
 
   def testGetPathNoBasedir(self):
     root = misc.GritNode()
-    root.StartParsing(u'grit', None)
-    root.HandleAttribute(u'latest_public_release', u'0')
-    root.HandleAttribute(u'current_release', u'1')
-    root.HandleAttribute(u'base_dir', r'..\resource')
+    root.StartParsing('grit', None)
+    root.HandleAttribute('latest_public_release', '0')
+    root.HandleAttribute('current_release', '1')
+    root.HandleAttribute('base_dir', r'..\resource')
     release = misc.ReleaseNode()
-    release.StartParsing(u'release', root)
-    release.HandleAttribute(u'seq', u'1')
+    release.StartParsing('release', root)
+    release.HandleAttribute('seq', '1')
     root.AddChild(release)
     includes = empty.IncludesNode()
-    includes.StartParsing(u'includes', release)
+    includes.StartParsing('includes', release)
     release.AddChild(includes)
     include_node = include.IncludeNode()
-    include_node.StartParsing(u'include', includes)
-    include_node.HandleAttribute(u'file', r'flugel\kugel.pdf')
-    include_node.HandleAttribute(u'use_base_dir', u'false')
+    include_node.StartParsing('include', includes)
+    include_node.HandleAttribute('file', r'flugel\kugel.pdf')
+    include_node.HandleAttribute('use_base_dir', 'false')
     includes.AddChild(include_node)
     root.EndParsing()
 
     last_dir = os.path.basename(os.getcwd())
     expected_path = util.normpath(os.path.join(
-        u'..', last_dir, u'flugel/kugel.pdf'))
+        '..', last_dir, 'flugel/kugel.pdf'))
     self.assertEqual(root.ToRealPath(include_node.GetInputPath()),
                      expected_path)
 
diff --git a/tools/grit/grit/node/mapping.py b/tools/grit/grit/node/mapping.py
index 9c250ee..2fef690 100644
--- a/tools/grit/grit/node/mapping.py
+++ b/tools/grit/grit/node/mapping.py
@@ -6,7 +6,6 @@
 When adding a new node type, you add to this mapping.
 '''
 
-from __future__ import print_function
 
 from grit import exception
 
diff --git a/tools/grit/grit/node/message.py b/tools/grit/grit/node/message.py
index 5f59557c..399c86f 100644
--- a/tools/grit/grit/node/message.py
+++ b/tools/grit/grit/node/message.py
@@ -18,7 +18,7 @@
 
 # Matches exactly three dots ending a line or followed by whitespace.
 _ELLIPSIS_PATTERN = lazy_re.compile(r'(?<!\.)\.\.\.(?=$|\s)')
-_ELLIPSIS_SYMBOL = u'\u2026'  # Ellipsis
+_ELLIPSIS_SYMBOL = '\u2026'  # Ellipsis
 
 # Finds whitespace at the start and end of a string which can be multiline.
 _WHITESPACE = lazy_re.compile(r'(?P<start>\s*)(?P<body>.+?)(?P<end>\s*)\Z',
@@ -58,7 +58,7 @@
   _SPLIT_RE = lazy_re.compile(r'\s*,\s*|\s+')
 
   def __init__(self):
-    super(MessageNode, self).__init__()
+    super().__init__()
     # Valid after EndParsing, this is the MessageClique that contains the
     # source message and any translations of it that have been loaded.
     self.clique = None
@@ -139,7 +139,7 @@
     superclass' implementation
     '''
     if 'offset' not in self.attrs:
-      return super(MessageNode, self).GetTextualIds()
+      return super().GetTextualIds()
 
     # we search for the first grouping node in the parents' list
     # to take care of the case where the first parent is an <if> node
@@ -156,7 +156,7 @@
     return self.attrs['translateable'] == 'true'
 
   def EndParsing(self):
-    super(MessageNode, self).EndParsing()
+    super().EndParsing()
 
     # Make the text (including placeholder references) and list of placeholders,
     # verify placeholder formats, then strip and store leading and trailing
@@ -268,7 +268,7 @@
     if self._replace_ellipsis:
       msg = _ELLIPSIS_PATTERN.sub(_ELLIPSIS_SYMBOL, msg)
     # Always remove all byte order marks (\uFEFF) https://crbug.com/1033305
-    msg = msg.replace(u'\uFEFF','')
+    msg = msg.replace('\uFEFF','')
     return msg.replace('[GRITLANGCODE]', lang)
 
   def NameOrOffset(self):
@@ -343,7 +343,7 @@
     return ['name']
 
   def EndParsing(self):
-    super(PhNode, self).EndParsing()
+    super().EndParsing()
     # We only allow a single example for each placeholder
     if len(self.children) > 1:
       raise exception.TooManyExamples()
diff --git a/tools/grit/grit/node/message_unittest.py b/tools/grit/grit/node/message_unittest.py
index a796f9e..8ee03c0 100755
--- a/tools/grit/grit/node/message_unittest.py
+++ b/tools/grit/grit/node/message_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.node.message'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -31,7 +30,7 @@
     msg, = root.GetChildrenOfType(message.MessageNode)
     cliques = msg.GetCliques()
     content = cliques[0].GetMessage().GetPresentableContent()
-    self.failUnless(content == 'Hello USERNAME, how are you doing today?')
+    self.assertTrue(content == 'Hello USERNAME, how are you doing today?')
 
   def testMessageWithWhitespace(self):
     root = util.ParseGrdForUnittest("""\
@@ -42,28 +41,28 @@
         </messages>""")
     msg, = root.GetChildrenOfType(message.MessageNode)
     content = msg.GetCliques()[0].GetMessage().GetPresentableContent()
-    self.failUnless(content == 'Hello there USERNAME')
-    self.failUnless(msg.ws_at_start == '  ')
-    self.failUnless(msg.ws_at_end == '   ')
+    self.assertTrue(content == 'Hello there USERNAME')
+    self.assertTrue(msg.ws_at_start == '  ')
+    self.assertTrue(msg.ws_at_end == '   ')
 
   def testConstruct(self):
     msg = tclib.Message(text="   Hello USERNAME, how are you?   BINGO\t\t",
                         placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi'),
                                       tclib.Placeholder('BINGO', '%d', '11')])
     msg_node = message.MessageNode.Construct(None, msg, 'BINGOBONGO')
-    self.failUnless(msg_node.children[0].name == 'ph')
-    self.failUnless(msg_node.children[0].children[0].name == 'ex')
-    self.failUnless(msg_node.children[0].children[0].GetCdata() == 'Joi')
-    self.failUnless(msg_node.children[1].children[0].GetCdata() == '11')
-    self.failUnless(msg_node.ws_at_start == '   ')
-    self.failUnless(msg_node.ws_at_end == '\t\t')
+    self.assertTrue(msg_node.children[0].name == 'ph')
+    self.assertTrue(msg_node.children[0].children[0].name == 'ex')
+    self.assertTrue(msg_node.children[0].children[0].GetCdata() == 'Joi')
+    self.assertTrue(msg_node.children[1].children[0].GetCdata() == '11')
+    self.assertTrue(msg_node.ws_at_start == '   ')
+    self.assertTrue(msg_node.ws_at_end == '\t\t')
 
   def testUnicodeConstruct(self):
-    text = u'Howdie \u00fe'
+    text = 'Howdie \u00fe'
     msg = tclib.Message(text=text)
     msg_node = message.MessageNode.Construct(None, msg, 'BINGOBONGO')
     msg_from_node = msg_node.GetCdata()
-    self.failUnless(msg_from_node == text)
+    self.assertTrue(msg_from_node == text)
 
   def testFormatterData(self):
     root = util.ParseGrdForUnittest("""\
@@ -80,10 +79,10 @@
 
     # Can't use assertDictEqual, not available in Python 2.6, so do it
     # by hand.
-    self.failUnlessEqual(len(expected_formatter_data),
+    self.assertEqual(len(expected_formatter_data),
                          len(msg.formatter_data))
     for key in expected_formatter_data:
-      self.failUnlessEqual(expected_formatter_data[key],
+      self.assertEqual(expected_formatter_data[key],
                            msg.formatter_data[key])
 
   def testReplaceEllipsis(self):
@@ -96,10 +95,10 @@
     msg, = root.GetChildrenOfType(message.MessageNode)
     msg.SetReplaceEllipsis(True)
     content = msg.Translate('en')
-    self.failUnlessEqual(u'A...B.... %s\u2026 B\u2026 C\u2026', content)
+    self.assertEqual('A...B.... %s\u2026 B\u2026 C\u2026', content)
 
   def testRemoveByteOrderMark(self):
-    root = util.ParseGrdForUnittest(u'''
+    root = util.ParseGrdForUnittest('''
         <messages>
         <message name="IDS_HAS_BOM" desc="">
         \uFEFFThis\uFEFF i\uFEFFs OK\uFEFF
@@ -107,7 +106,7 @@
         </messages>''')
     msg, = root.GetChildrenOfType(message.MessageNode)
     content = msg.Translate('en')
-    self.failUnlessEqual(u'This is OK', content)
+    self.assertEqual('This is OK', content)
 
   def testPlaceholderHasTooManyExamples(self):
     try:
@@ -250,7 +249,7 @@
     msg, = root.GetChildrenOfType(message.MessageNode)
     cliques = msg.GetCliques()
     content = cliques[0].GetMessage().GetPresentableContent()
-    self.failUnless(content == 'ERROR_COUNT error, WARNING_COUNT warning')
+    self.assertTrue(content == 'ERROR_COUNT error, WARNING_COUNT warning')
 
   def testMultipleFormattersAreInsidePhNodes(self):
     failed = True
diff --git a/tools/grit/grit/node/misc.py b/tools/grit/grit/node/misc.py
index 07d9205..617c1a8 100644
--- a/tools/grit/grit/node/misc.py
+++ b/tools/grit/grit/node/misc.py
@@ -272,7 +272,7 @@
 
   def _IsValidChild(self, child):
     return (isinstance(child, (ThenNode, ElseNode)) or
-            super(IfNode, self)._IsValidChild(child))
+            super()._IsValidChild(child))
 
   def EndParsing(self):
     children = self.children
@@ -290,7 +290,7 @@
       return self.children[0 if cond else 1].ActiveChildren()
     else:
       # Equivalent to having all children inside <then> with an empty <else>
-      return super(IfNode, self).ActiveChildren() if cond else []
+      return super().ActiveChildren() if cond else []
 
 
 class ThenNode(SplicingNode):
@@ -308,14 +308,14 @@
   """
 
   def __init__(self):
-    super(PartNode, self).__init__()
+    super().__init__()
     self.started_inclusion = False
 
   def MandatoryAttributes(self):
     return ['file']
 
   def _IsValidChild(self, child):
-    return self.started_inclusion and super(PartNode, self)._IsValidChild(child)
+    return self.started_inclusion and super()._IsValidChild(child)
 
 
 class ReleaseNode(base.Node):
@@ -343,7 +343,7 @@
   """The <grit> root element."""
 
   def __init__(self):
-    super(GritNode, self).__init__()
+    super().__init__()
     self.output_language = ''
     self.defines = {}
     self.substituter = None
@@ -386,7 +386,7 @@
     }
 
   def EndParsing(self):
-    super(GritNode, self).EndParsing()
+    super().EndParsing()
     if (int(self.attrs['latest_public_release'])
         > int(self.attrs['current_release'])):
       raise exception.Parsing('latest_public_release cannot have a greater '
@@ -556,8 +556,8 @@
     """Returns the distinct (language, context, fallback_to_default_layout)
     triples from the output nodes.
     """
-    return set((n.GetLanguage(), n.GetContext(), n.GetFallbackToDefaultLayout())
-               for n in self.GetOutputFiles())
+    return {(n.GetLanguage(), n.GetContext(), n.GetFallbackToDefaultLayout())
+               for n in self.GetOutputFiles()}
 
   def GetSubstitutionMessages(self):
     """Returns the list of <message sub_variable="true"> nodes."""
@@ -727,7 +727,7 @@
 
   def EndParsing(self):
     """Handles system identifiers."""
-    super(IdentifierNode, self).EndParsing()
+    super().EndParsing()
     if self.attrs['systemid'] == 'true':
       util.SetupSystemIdentifiers((self.attrs['name'],))
 
diff --git a/tools/grit/grit/node/misc_unittest.py b/tools/grit/grit/node/misc_unittest.py
index 37814fd..258a7b2 100755
--- a/tools/grit/grit/node/misc_unittest.py
+++ b/tools/grit/grit/node/misc_unittest.py
@@ -110,7 +110,7 @@
               path in grd.GetInputFiles()]
     # Convert path separator for Windows paths.
     actual = [path.replace('\\', '/') for path in actual]
-    self.assertEquals(expected, actual)
+    self.assertEqual(expected, actual)
 
   # Verifies that GetInputFiles() returns the correct list of files
   # when files include other files.
@@ -136,7 +136,7 @@
               path in grd.GetInputFiles()]
     # Convert path separator for Windows paths.
     actual = [path.replace('\\', '/') for path in actual]
-    self.assertEquals(expected, actual)
+    self.assertEqual(expected, actual)
 
   def testNonDefaultEntry(self):
     grd = util.ParseGrdForUnittest('''
@@ -151,7 +151,7 @@
     self.assertIn('#define IDS_A 2378\n#define IDS_B 2379', output)
 
   def testMaxIds(self):
-    xml = u'''<?xml version="1.0" encoding="UTF-8"?>
+    xml = '''<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
       base_dir="." first_ids_file="%s">
   <release seq="3">
@@ -280,33 +280,33 @@
     grd.SetOutputLanguage('fr')
     grd.SetDefines({'hello': '1'})
     active = set(grd.ActiveDescendants())
-    self.failUnless(bingo_message not in active)
-    self.failUnless(hello_message in active)
-    self.failUnless(french_message in active)
+    self.assertTrue(bingo_message not in active)
+    self.assertTrue(hello_message in active)
+    self.assertTrue(french_message in active)
 
     grd.SetOutputLanguage('en')
     grd.SetDefines({'bingo': 1})
     active = set(grd.ActiveDescendants())
-    self.failUnless(bingo_message in active)
-    self.failUnless(hello_message not in active)
-    self.failUnless(french_message not in active)
+    self.assertTrue(bingo_message in active)
+    self.assertTrue(hello_message not in active)
+    self.assertTrue(french_message not in active)
 
     grd.SetOutputLanguage('en')
     grd.SetDefines({'FORCE_FRENCH': '1', 'bingo': '1'})
     active = set(grd.ActiveDescendants())
-    self.failUnless(bingo_message in active)
-    self.failUnless(hello_message not in active)
-    self.failUnless(french_message in active)
+    self.assertTrue(bingo_message in active)
+    self.assertTrue(hello_message not in active)
+    self.assertTrue(french_message in active)
 
     grd.SetOutputLanguage('en')
     grd.SetDefines({})
-    self.failUnless(grd.target_platform == sys.platform)
+    self.assertTrue(grd.target_platform == sys.platform)
     grd.SetTargetPlatform('darwin')
     active = set(grd.ActiveDescendants())
-    self.failUnless(is_win_message not in active)
+    self.assertTrue(is_win_message not in active)
     grd.SetTargetPlatform('win32')
     active = set(grd.ActiveDescendants())
-    self.failUnless(is_win_message in active)
+    self.assertTrue(is_win_message in active)
 
   def testElsiness(self):
     grd = util.ParseGrdForUnittest('''
@@ -383,7 +383,7 @@
     grd.SetOutputLanguage('ru')
     grd.SetDefines({'hello': '1'})
     outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-    self.assertEquals(
+    self.assertEqual(
         outputs,
         ['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'doc.html',
          'uncond2.adm', 'iftest.h'])
@@ -391,14 +391,14 @@
     grd.SetOutputLanguage('ru')
     grd.SetDefines({'bingo': '2'})
     outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-    self.assertEquals(
+    self.assertEqual(
         outputs,
         ['uncond1.rc', 'doc.html', 'uncond2.adm', 'iftest.h'])
 
     grd.SetOutputLanguage('fr')
     grd.SetDefines({'hello': '1'})
     outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-    self.assertEquals(
+    self.assertEqual(
         outputs,
         ['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'uncond2.adm',
          'iftest.h'])
@@ -406,12 +406,12 @@
     grd.SetOutputLanguage('en')
     grd.SetDefines({'bingo': '1'})
     outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-    self.assertEquals(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
+    self.assertEqual(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
 
     grd.SetOutputLanguage('fr')
     grd.SetDefines({'bingo': '1'})
     outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
-    self.assertNotEquals(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
+    self.assertNotEqual(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
 
   def testChildrenAccepted(self):
     grd_reader.Parse(io.StringIO(r'''<?xml version="1.0"?>
@@ -596,10 +596,10 @@
     menu = grd.GetNodeById('IDC_KLONKMENU')
 
     for node in [hello, aboutbox]:
-      self.failUnless(not node.PseudoIsAllowed())
+      self.assertTrue(not node.PseudoIsAllowed())
 
     for node in [bingo, menu]:
-      self.failUnless(node.PseudoIsAllowed())
+      self.assertTrue(node.PseudoIsAllowed())
 
     # TODO(benrg): There was a test here that formatting hello and aboutbox with
     # a pseudo language should fail, but they do not fail and the test was
diff --git a/tools/grit/grit/node/node_io.py b/tools/grit/grit/node/node_io.py
index 8432f4d4..3c16220 100644
--- a/tools/grit/grit/node/node_io.py
+++ b/tools/grit/grit/node/node_io.py
@@ -5,7 +5,6 @@
 '''The <output> and <file> elements.
 '''
 
-from __future__ import print_function
 
 import os
 
@@ -17,7 +16,7 @@
   '''A <file> element.'''
 
   def __init__(self):
-    super(FileNode, self).__init__()
+    super().__init__()
     self.re = None
     self.should_load_ = True
 
diff --git a/tools/grit/grit/node/node_io_unittest.py b/tools/grit/grit/node/node_io_unittest.py
index 8900896..b1fc7b2c 100755
--- a/tools/grit/grit/node/node_io_unittest.py
+++ b/tools/grit/grit/node/node_io_unittest.py
@@ -31,20 +31,20 @@
 class FileNodeUnittest(unittest.TestCase):
   def testGetPath(self):
     root = misc.GritNode()
-    root.StartParsing(u'grit', None)
-    root.HandleAttribute(u'latest_public_release', u'0')
-    root.HandleAttribute(u'current_release', u'1')
-    root.HandleAttribute(u'base_dir', r'..\resource')
+    root.StartParsing('grit', None)
+    root.HandleAttribute('latest_public_release', '0')
+    root.HandleAttribute('current_release', '1')
+    root.HandleAttribute('base_dir', r'..\resource')
     translations = empty.TranslationsNode()
-    translations.StartParsing(u'translations', root)
+    translations.StartParsing('translations', root)
     root.AddChild(translations)
     file_node = node_io.FileNode()
-    file_node.StartParsing(u'file', translations)
-    file_node.HandleAttribute(u'path', r'flugel\kugel.pdf')
+    file_node.StartParsing('file', translations)
+    file_node.HandleAttribute('path', r'flugel\kugel.pdf')
     translations.AddChild(file_node)
     root.EndParsing()
 
-    self.failUnless(root.ToRealPath(file_node.GetInputPath()) ==
+    self.assertTrue(root.ToRealPath(file_node.GetInputPath()) ==
                     util.normpath(
                       os.path.join(r'../resource', r'flugel/kugel.pdf')))
 
@@ -149,12 +149,12 @@
     grd.RunGatherers()
     outputs = grd.GetChildrenOfType(node_io.OutputNode)
     active = set(grd.ActiveDescendants())
-    self.failUnless(outputs[0] in active)
-    self.failUnless(outputs[0].GetType() == 'rc_header')
-    self.failUnless(outputs[1] in active)
-    self.failUnless(outputs[1].GetType() == 'rc_all')
-    self.failUnless(outputs[2] not in active)
-    self.failUnless(outputs[2].GetType() == 'rc_all')
+    self.assertTrue(outputs[0] in active)
+    self.assertTrue(outputs[0].GetType() == 'rc_header')
+    self.assertTrue(outputs[1] in active)
+    self.assertTrue(outputs[1].GetType() == 'rc_all')
+    self.assertTrue(outputs[2] not in active)
+    self.assertTrue(outputs[2].GetType() == 'rc_all')
 
   # Verify that 'iw' and 'no' language codes in xtb files are mapped to 'he' and
   # 'nb'.
diff --git a/tools/grit/grit/node/structure.py b/tools/grit/grit/node/structure.py
index 99f3d365..ba6608eb 100644
--- a/tools/grit/grit/node/structure.py
+++ b/tools/grit/grit/node/structure.py
@@ -5,7 +5,6 @@
 '''The <structure> element.
 '''
 
-from __future__ import print_function
 
 import os
 import platform
@@ -58,7 +57,7 @@
   variable_pattern = re.compile(r'([^,=\s]+)=((?:,,|[^,])*)')
 
   def __init__(self):
-    super(StructureNode, self).__init__()
+    super().__init__()
 
     # Keep track of the last filename we flattened to, so we can
     # avoid doing it more than once.
@@ -74,10 +73,10 @@
   def _ParseVariables(self, variables):
     '''Parse a variable string into a dictionary.'''
     matches = StructureNode.variable_pattern.findall(variables)
-    return dict((name, value.replace(',,', ',')) for name, value in matches)
+    return {name: value.replace(',,', ',') for name, value in matches}
 
   def EndParsing(self):
-    super(StructureNode, self).EndParsing()
+    super().EndParsing()
 
     # Now that we have attributes and children, instantiate the gatherers.
     gathertype = _GATHERERS[self.attrs['type']]
diff --git a/tools/grit/grit/node/structure_unittest.py b/tools/grit/grit/node/structure_unittest.py
index 6b43dfc..2bc12eb 100755
--- a/tools/grit/grit/node/structure_unittest.py
+++ b/tools/grit/grit/node/structure_unittest.py
@@ -6,7 +6,6 @@
 '''Unit tests for <structure> nodes.
 '''
 
-from __future__ import print_function
 
 import os
 import os.path
@@ -55,18 +54,18 @@
     grd.SetOutputLanguage('fr')
     grd.RunGatherers()
     transl = ''.join(rc.Format(grd, 'fr', '.'))
-    self.failUnless(transl.count('040704') and transl.count('110978'))
-    self.failUnless(transl.count('2005",IDC_STATIC'))
+    self.assertTrue(transl.count('040704') and transl.count('110978'))
+    self.assertTrue(transl.count('2005",IDC_STATIC'))
 
   def testRunCommandOnCurrentPlatform(self):
     node = structure.StructureNode()
     node.attrs = node.DefaultAttributes()
-    self.failUnless(node.RunCommandOnCurrentPlatform())
+    self.assertTrue(node.RunCommandOnCurrentPlatform())
     node.attrs['run_command_on_platforms'] = 'Nosuch'
-    self.failIf(node.RunCommandOnCurrentPlatform())
+    self.assertFalse(node.RunCommandOnCurrentPlatform())
     node.attrs['run_command_on_platforms'] = (
         'Nosuch,%s,Othernot' % platform.system())
-    self.failUnless(node.RunCommandOnCurrentPlatform())
+    self.assertTrue(node.RunCommandOnCurrentPlatform())
 
   def testVariables(self):
     grd = util.ParseGrdForUnittest('''
@@ -80,7 +79,7 @@
     filepath = os.path.join(tempfile.gettempdir(), filename)
     with open(filepath) as f:
       result = f.read()
-      self.failUnlessEqual(('<h1>Hello!</h1>\n'
+      self.assertEqual(('<h1>Hello!</h1>\n'
                             'Some cool things are foo, bar, baz.\n'
                             'Did you know that 2+2==4?\n'
                             '<p>\n'
@@ -199,7 +198,7 @@
     node.RunPreSubstitutionGatherer()
     data = node.GetDataPackValue(lang='en', encoding=util.BINARY)
 
-    self.assertEqual('LOTTIE'.encode('utf-8'), data[0:6])
+    self.assertEqual(b'LOTTIE', data[0:6])
     self.assertEqual(
         util.ReadFile(os.path.join(test_data_root, 'test_json.json'),
                       util.BINARY),
@@ -221,7 +220,7 @@
     ])
     data = node.GetDataPackValue(lang='en', encoding=util.BINARY)
 
-    self.assertEqual('LOTTIE'.encode('utf-8'), data[0:6])
+    self.assertEqual(b'LOTTIE', data[0:6])
     self.assertEqual(constants.BROTLI_CONST, data[6:8])
     self.assertEqual(
         len(
diff --git a/tools/grit/grit/node/variant.py b/tools/grit/grit/node/variant.py
index 8ef887f8..b2329405 100644
--- a/tools/grit/grit/node/variant.py
+++ b/tools/grit/grit/node/variant.py
@@ -5,7 +5,6 @@
 '''The <skeleton> element.
 '''
 
-from __future__ import print_function
 
 from grit.node import base
 
diff --git a/tools/grit/grit/pseudo.py b/tools/grit/grit/pseudo.py
index ecd71bd5..c169661 100644
--- a/tools/grit/grit/pseudo.py
+++ b/tools/grit/grit/pseudo.py
@@ -21,7 +21,6 @@
 the latin-1 character set which will stress character encoding bugs.
 '''
 
-from __future__ import print_function
 
 from grit import lazy_re
 from grit import tclib
@@ -36,22 +35,22 @@
 # a better solution, i.e. one that introduces a non-latin1 character into the
 # pseudotranslation.
 #_QOF = u'\u05e7'
-_QOF = u'P'
+_QOF = 'P'
 
 # How we map each vowel.
 _VOWELS = {
-  u'a' : u'\u00e5',  # a with ring
-  u'e' : u'\u00e9',  # e acute
-  u'i' : u'\u00ef',  # i diaresis
-  u'o' : u'\u00f4',  # o circumflex
-  u'u' : u'\u00fc',  # u diaresis
-  u'y' : u'\u00fd',  # y acute
-  u'A' : u'\u00c5',  # A with ring
-  u'E' : u'\u00c9',  # E acute
-  u'I' : u'\u00cf',  # I diaresis
-  u'O' : u'\u00d4',  # O circumflex
-  u'U' : u'\u00dc',  # U diaresis
-  u'Y' : u'\u00dd',  # Y acute
+  'a' : '\u00e5',  # a with ring
+  'e' : '\u00e9',  # e acute
+  'i' : '\u00ef',  # i diaresis
+  'o' : '\u00f4',  # o circumflex
+  'u' : '\u00fc',  # u diaresis
+  'y' : '\u00fd',  # y acute
+  'A' : '\u00c5',  # A with ring
+  'E' : '\u00c9',  # E acute
+  'I' : '\u00cf',  # I diaresis
+  'O' : '\u00d4',  # O circumflex
+  'U' : '\u00dc',  # U diaresis
+  'Y' : '\u00dd',  # Y acute
 }
 _VOWELS_KEYS = set(_VOWELS.keys())
 
@@ -87,7 +86,7 @@
   if str in _existing_translations:
     return _existing_translations[str]
 
-  outstr = u''
+  outstr = ''
   ix = 0
   while ix < len(str):
     if str[ix] not in _VOWELS_KEYS:
@@ -96,7 +95,7 @@
     else:
       # We want to treat consecutive vowels as one composite vowel.  This is not
       # always accurate e.g. in composite words but good enough.
-      consecutive_vowels = u''
+      consecutive_vowels = ''
       while ix < len(str) and str[ix] in _VOWELS_KEYS:
         consecutive_vowels += str[ix]
         ix += 1
diff --git a/tools/grit/grit/pseudo_unittest.py b/tools/grit/grit/pseudo_unittest.py
index a33a249d..2a3d44b 100755
--- a/tools/grit/grit/pseudo_unittest.py
+++ b/tools/grit/grit/pseudo_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.pseudo'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -20,24 +19,24 @@
 
 class PseudoUnittest(unittest.TestCase):
   def testVowelMapping(self):
-    self.failUnless(pseudo.MapVowels('abebibobuby') ==
-                    u'\u00e5b\u00e9b\u00efb\u00f4b\u00fcb\u00fd')
-    self.failUnless(pseudo.MapVowels('ABEBIBOBUBY') ==
-                    u'\u00c5B\u00c9B\u00cfB\u00d4B\u00dcB\u00dd')
+    self.assertTrue(pseudo.MapVowels('abebibobuby') ==
+                    '\u00e5b\u00e9b\u00efb\u00f4b\u00fcb\u00fd')
+    self.assertTrue(pseudo.MapVowels('ABEBIBOBUBY') ==
+                    '\u00c5B\u00c9B\u00cfB\u00d4B\u00dcB\u00dd')
 
   def testPseudoString(self):
     out = pseudo.PseudoString('hello')
-    self.failUnless(out == pseudo.MapVowels(u'hePelloPo', True))
+    self.assertTrue(out == pseudo.MapVowels('hePelloPo', True))
 
   def testConsecutiveVowels(self):
     out = pseudo.PseudoString("beautiful weather, ain't it?")
-    self.failUnless(out == pseudo.MapVowels(
-      u"beauPeautiPifuPul weaPeathePer, aiPain't iPit?", 1))
+    self.assertTrue(out == pseudo.MapVowels(
+      "beauPeautiPifuPul weaPeathePer, aiPain't iPit?", 1))
 
   def testCapitals(self):
     out = pseudo.PseudoString("HOWDIE DOODIE, DR. JONES")
-    self.failUnless(out == pseudo.MapVowels(
-      u"HOPOWDIEPIE DOOPOODIEPIE, DR. JOPONEPES", 1))
+    self.assertTrue(out == pseudo.MapVowels(
+      "HOPOWDIEPIE DOOPOODIEPIE, DR. JOPONEPES", 1))
 
   def testPseudoMessage(self):
     msg = tclib.Message(text='Hello USERNAME, how are you?',
@@ -46,9 +45,9 @@
     trans = pseudo.PseudoMessage(msg)
     # TODO(joi) It would be nicer if 'you' -> 'youPou' instead of
     # 'you' -> 'youPyou' and if we handled the silent e in 'are'
-    self.failUnless(trans.GetPresentableContent() ==
+    self.assertTrue(trans.GetPresentableContent() ==
                     pseudo.MapVowels(
-                      u'HePelloPo USERNAME, hoPow aParePe youPyou?', 1))
+                      'HePelloPo USERNAME, hoPow aParePe youPyou?', 1))
 
 
 if __name__ == '__main__':
diff --git a/tools/grit/grit/pseudolocales.py b/tools/grit/grit/pseudolocales.py
index 5f1e6109..fb07f66a 100644
--- a/tools/grit/grit/pseudolocales.py
+++ b/tools/grit/grit/pseudolocales.py
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 """Pseudolocale translations for chrome."""
 
-from __future__ import print_function
 
 import re
 import string
@@ -13,49 +12,49 @@
 from grit import tclib
 
 ACCENTED_STRINGS = {
-    '!': u'\u00a1',
-    '$': u'\u20ac',
-    '?': u'\u00bf',
-    'A': u'\u00c5',
-    'C': u'\u00c7',
-    'D': u'\u00d0',
-    'E': u'\u00c9',
-    'G': u'\u011c',
-    'H': u'\u0124',
-    'I': u'\u00ce',
-    'J': u'\u0134',
-    'K': u'\u0136',
-    'L': u'\u013b',
-    'N': u'\u00d1',
-    'O': u'\u00d6',
-    'P': u'\u00de',
-    'R': u'\u00ae',
-    'S': u'\u0160',
-    'T': u'\u0162',
-    'U': u'\u00db',
-    'W': u'\u0174',
-    'Y': u'\u00dd',
-    'Z': u'\u017d',
-    'a': u'\u00e5',
-    'c': u'\u00e7',
-    'd': u'\u00f0',
-    'e': u'\u00e9',
-    'f': u'\u0192',
-    'g': u'\u011d',
-    'h': u'\u0125',
-    'i': u'\u00ee',
-    'j': u'\u0135',
-    'k': u'\u0137',
-    'l': u'\u013c',
-    'n': u'\u00f1',
-    'o': u'\u00f6',
-    'p': u'\u00fe',
-    's': u'\u0161',
-    't': u'\u0163',
-    'u': u'\u00fb',
-    'w': u'\u0175',
-    'y': u'\u00fd',
-    'z': u'\u017e',
+    '!': '\u00a1',
+    '$': '\u20ac',
+    '?': '\u00bf',
+    'A': '\u00c5',
+    'C': '\u00c7',
+    'D': '\u00d0',
+    'E': '\u00c9',
+    'G': '\u011c',
+    'H': '\u0124',
+    'I': '\u00ce',
+    'J': '\u0134',
+    'K': '\u0136',
+    'L': '\u013b',
+    'N': '\u00d1',
+    'O': '\u00d6',
+    'P': '\u00de',
+    'R': '\u00ae',
+    'S': '\u0160',
+    'T': '\u0162',
+    'U': '\u00db',
+    'W': '\u0174',
+    'Y': '\u00dd',
+    'Z': '\u017d',
+    'a': '\u00e5',
+    'c': '\u00e7',
+    'd': '\u00f0',
+    'e': '\u00e9',
+    'f': '\u0192',
+    'g': '\u011d',
+    'h': '\u0125',
+    'i': '\u00ee',
+    'j': '\u0135',
+    'k': '\u0137',
+    'l': '\u013c',
+    'n': '\u00f1',
+    'o': '\u00f6',
+    'p': '\u00fe',
+    's': '\u0161',
+    't': '\u0163',
+    'u': '\u00fb',
+    'w': '\u0175',
+    'y': '\u00fd',
+    'z': '\u017e',
 }
 
 NUMBERS = [
@@ -67,11 +66,11 @@
 WORD = lazy_re.compile(r'\b\S+\b')
 
 # RTL modifiers for letters
-RLO = u'\u202e'
-PDF = u'\u202c'
+RLO = '\u202e'
+PDF = '\u202c'
 
 
-class Node(object):
+class Node:
   """A node in the syntax tree representing a message to be translated."""
 
   translatable = False
@@ -100,7 +99,7 @@
     translation from.
     """
     children = ''.join(c.ToString() for c in self.children)
-    return u'%s%s%s' % (self.text, children, self.after)
+    return '%s%s%s' % (self.text, children, self.after)
 
   def __repr__(self):
     # For debugging
@@ -231,7 +230,7 @@
   child_types = [HtmlTag, BasicVariable, Plural, RawText]
 
   def __init__(self, children):
-    super(NodeSequence, self).__init__('', children)
+    super().__init__('', children)
 
   @classmethod
   def Parse(cls, text):
diff --git a/tools/grit/grit/pseudolocales_unittest.py b/tools/grit/grit/pseudolocales_unittest.py
index adf33a0..17799f0 100755
--- a/tools/grit/grit/pseudolocales_unittest.py
+++ b/tools/grit/grit/pseudolocales_unittest.py
@@ -177,8 +177,8 @@
         '{ATTEMPTS_LEFT, plural,\n  =1 {hello}\nother {world}\n}')
 
   def testBuildAndUnbuildTree(self):
-    p1 = tclib.Placeholder(u'USERNAME', '%s', 'foo')
-    p2 = tclib.Placeholder(u'EMAIL', '%s', 'bar')
+    p1 = tclib.Placeholder('USERNAME', '%s', 'foo')
+    p2 = tclib.Placeholder('EMAIL', '%s', 'bar')
 
     msg = tclib.Message()
     msg.AppendText('hello')
@@ -199,8 +199,8 @@
     self.assertEqual(transl.GetContent(), ['hello', p1, p2, 'world'])
 
   def testPseudolocales(self):
-    p1 = tclib.Placeholder(u'USERNAME', '%s', 'foo')
-    p2 = tclib.Placeholder(u'EMAIL', '%s', 'bar')
+    p1 = tclib.Placeholder('USERNAME', '%s', 'foo')
+    p2 = tclib.Placeholder('EMAIL', '%s', 'bar')
     msg = tclib.Message()
     msg.AppendText('h_')
     msg.AppendPlaceholder(p1)
@@ -209,23 +209,23 @@
 
     self.assertEqual(
         pl.PseudoLongStringMessage(msg).GetContent(),
-        [u'\u0125_', p1, p2, u'\u0175', ' - one two three four'])
+        ['\u0125_', p1, p2, '\u0175', ' - one two three four'])
 
     msg.AppendText('hello world')
     self.assertEqual(
         pl.PseudoRTLMessage(msg).GetContent(),
-        [u'\u202eh\u202c_', p1, p2, u'\u202ewhello\u202c \u202eworld\u202c'])
+        ['\u202eh\u202c_', p1, p2, '\u202ewhello\u202c \u202eworld\u202c'])
 
   # If it fails to translate with prod messages, add the failure to here to
   # make sure it doesn't happen again.
   def testProdFailures(self):
-    p1 = tclib.Placeholder(u'USERNAME', '%s', 'foo')
+    p1 = tclib.Placeholder('USERNAME', '%s', 'foo')
 
     msg = tclib.Message()
-    msg.AppendText(u'{LINE_COUNT, plural,\n      =1 {<1 line not shown>}\n'
+    msg.AppendText('{LINE_COUNT, plural,\n      =1 {<1 line not shown>}\n'
                    '      other {<')
     msg.AppendPlaceholder(p1)
-    msg.AppendText(u' lines not shown>}\n}')
+    msg.AppendText(' lines not shown>}\n}')
     tree, _ = pl.BuildTreeFromMessage(msg)
     self.assertTreesEqual(
         tree,
@@ -238,11 +238,11 @@
         ]))
 
     msg = tclib.Message()
-    msg.AppendText(u'{1, plural,\n   \n             =1 {Rated ')
+    msg.AppendText('{1, plural,\n   \n             =1 {Rated ')
     msg.AppendPlaceholder(p1)
-    msg.AppendText(u' by one user.}\n      other{Rated ')
+    msg.AppendText(' by one user.}\n      other{Rated ')
     msg.AppendPlaceholder(p1)
-    msg.AppendText(u' by # users.}}')
+    msg.AppendText(' by # users.}}')
     tree, _ = pl.BuildTreeFromMessage(msg)
     self.assertTreesEqual(
         tree,
diff --git a/tools/grit/grit/shortcuts.py b/tools/grit/grit/shortcuts.py
index b0a9062..366ad18 100644
--- a/tools/grit/grit/shortcuts.py
+++ b/tools/grit/grit/shortcuts.py
@@ -5,12 +5,11 @@
 '''Stuff to prevent conflicting shortcuts.
 '''
 
-from __future__ import print_function
 
 from grit import lazy_re
 
 
-class ShortcutGroup(object):
+class ShortcutGroup:
   '''Manages a list of cliques that belong together in a single shortcut
   group.  Knows how to detect conflicting shortcut keys.
   '''
diff --git a/tools/grit/grit/shortcuts_unittest.py b/tools/grit/grit/shortcuts_unittest.py
index 6455b10..a1398c2 100755
--- a/tools/grit/grit/shortcuts_unittest.py
+++ b/tools/grit/grit/shortcuts_unittest.py
@@ -31,7 +31,7 @@
     c.AddToShortcutGroup('group_name')
 
     warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
-    self.failUnless(warnings)
+    self.assertTrue(warnings)
 
   def testAmpersandEscaping(self):
     c = self.uq.MakeClique(tclib.Message(text="Hello &there"))
@@ -40,7 +40,7 @@
     c.AddToShortcutGroup('group_name')
 
     warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
-    self.failUnless(len(warnings) == 0)
+    self.assertTrue(len(warnings) == 0)
 
   def testDialog(self):
     dlg = rc.Dialog(
@@ -74,4 +74,4 @@
     dlg.Parse()
 
     warnings = shortcuts.GenerateDuplicateShortcutsWarnings(self.uq, 'PROJECT')
-    self.failUnless(len(warnings) == 0)
+    self.assertTrue(len(warnings) == 0)
diff --git a/tools/grit/grit/tclib.py b/tools/grit/grit/tclib.py
index 0f21d5f..cb23764 100644
--- a/tools/grit/grit/tclib.py
+++ b/tools/grit/grit/tclib.py
@@ -5,7 +5,6 @@
 '''Adaptation of the extern.tclib classes for our needs.
 '''
 
-from __future__ import print_function
 
 import functools
 import re
@@ -29,7 +28,7 @@
   return i
 
 
-class BaseMessage(object):
+class BaseMessage:
   '''Base class with methods shared by Message and Translation.
   '''
 
@@ -87,7 +86,7 @@
     '''
     bits = []
     for item in self.parts:
-      if isinstance(item, six.string_types):
+      if isinstance(item, str):
         bits.append(escaping_function(item))
       else:
         bits.append(item.GetOriginal())
@@ -122,7 +121,7 @@
     self.dirty = True
 
   def AppendText(self, text):
-    assert isinstance(text, six.string_types)
+    assert isinstance(text, str)
     assert text != ''
 
     self.parts.append(text)
@@ -180,7 +179,7 @@
 
   def __init__(self, text='', placeholders=[], description='', meaning='',
                assigned_id=None):
-    super(Message, self).__init__(text, placeholders, description, meaning)
+    super().__init__(text, placeholders, description, meaning)
     self.assigned_id = assigned_id
 
   def ToTclibMessage(self):
@@ -193,7 +192,7 @@
     if self.assigned_id:
       return self.assigned_id
 
-    return super(Message, self).GetId()
+    return super().GetId()
 
   def HasAssignedId(self):
     '''Returns True if this message has an assigned id.'''
@@ -204,7 +203,7 @@
   '''A translation.'''
 
   def __init__(self, text='', id='', placeholders=[], description='', meaning=''):
-    super(Translation, self).__init__(text, placeholders, description, meaning)
+    super().__init__(text, placeholders, description, meaning)
     self.id = id
 
   def GetId(self):
diff --git a/tools/grit/grit/tclib_unittest.py b/tools/grit/grit/tclib_unittest.py
index 510a2f5..360cdb515 100755
--- a/tools/grit/grit/tclib_unittest.py
+++ b/tools/grit/grit/tclib_unittest.py
@@ -20,41 +20,41 @@
 
 class TclibUnittest(unittest.TestCase):
   def testInit(self):
-    msg = tclib.Message(text=u'Hello Earthlings',
+    msg = tclib.Message(text='Hello Earthlings',
                         description='Greetings\n\t      message')
-    self.failUnlessEqual(msg.GetPresentableContent(), 'Hello Earthlings')
-    self.failUnless(isinstance(msg.GetPresentableContent(), str))
-    self.failUnlessEqual(msg.GetDescription(), 'Greetings message')
+    self.assertEqual(msg.GetPresentableContent(), 'Hello Earthlings')
+    self.assertTrue(isinstance(msg.GetPresentableContent(), str))
+    self.assertEqual(msg.GetDescription(), 'Greetings message')
 
   def testGetAttr(self):
     msg = tclib.Message()
-    msg.AppendText(u'Hello')  # Tests __getattr__
-    self.failUnless(msg.GetPresentableContent() == 'Hello')
-    self.failUnless(isinstance(msg.GetPresentableContent(), str))
+    msg.AppendText('Hello')  # Tests __getattr__
+    self.assertTrue(msg.GetPresentableContent() == 'Hello')
+    self.assertTrue(isinstance(msg.GetPresentableContent(), str))
 
   def testAll(self):
-    text = u'Howdie USERNAME'
-    phs = [tclib.Placeholder(u'USERNAME', u'%s', 'Joi')]
+    text = 'Howdie USERNAME'
+    phs = [tclib.Placeholder('USERNAME', '%s', 'Joi')]
     msg = tclib.Message(text=text, placeholders=phs)
-    self.failUnless(msg.GetPresentableContent() == 'Howdie USERNAME')
+    self.assertTrue(msg.GetPresentableContent() == 'Howdie USERNAME')
 
     trans = tclib.Translation(text=text, placeholders=phs)
-    self.failUnless(trans.GetPresentableContent() == 'Howdie USERNAME')
-    self.failUnless(isinstance(trans.GetPresentableContent(), str))
+    self.assertTrue(trans.GetPresentableContent() == 'Howdie USERNAME')
+    self.assertTrue(isinstance(trans.GetPresentableContent(), str))
 
   def testUnicodeReturn(self):
-    text = u'\u00fe'
+    text = '\u00fe'
     msg = tclib.Message(text=text)
-    self.failUnless(msg.GetPresentableContent() == text)
+    self.assertTrue(msg.GetPresentableContent() == text)
     from_list = msg.GetContent()[0]
-    self.failUnless(from_list == text)
+    self.assertTrue(from_list == text)
 
   def testRegressionTranslationInherited(self):
     '''Regression tests a bug that was caused by grit.tclib.Translation
     inheriting from the translation console's Translation object
     instead of only owning an instance of it.
     '''
-    msg = tclib.Message(text=u"BLA1\r\nFrom: BLA2 \u00fe BLA3",
+    msg = tclib.Message(text="BLA1\r\nFrom: BLA2 \u00fe BLA3",
                         placeholders=[
                           tclib.Placeholder('BLA1', '%s', '%s'),
                           tclib.Placeholder('BLA2', '%s', '%s'),
@@ -62,7 +62,7 @@
     transl = tclib.Translation(text=msg.GetPresentableContent(),
                                placeholders=msg.GetPlaceholders())
     content = transl.GetContent()
-    self.failUnless(isinstance(content[3], str))
+    self.assertTrue(isinstance(content[3], str))
 
   def testFingerprint(self):
     # This has Windows line endings.  That is on purpose.
@@ -167,7 +167,7 @@
     phs = [tclib.Placeholder(word[:i], str(i), str(i)) for i in range(1, 11)]
     try:
       msg = tclib.Message(text=text, placeholders=phs)
-      self.failUnless(msg.GetRealContent() == '1 2 3 4 5 6 7 8 9 10')
+      self.assertTrue(msg.GetRealContent() == '1 2 3 4 5 6 7 8 9 10')
     except:
       self.fail('tclib.Message() should handle placeholders that are '
                 'substrings of each other')
diff --git a/tools/grit/grit/test_suite_all.py b/tools/grit/grit/test_suite_all.py
index 3cb9e2b..a4270ca 100755
--- a/tools/grit/grit/test_suite_all.py
+++ b/tools/grit/grit/test_suite_all.py
@@ -5,7 +5,6 @@
 
 '''Unit test suite that collects all test cases for GRIT.'''
 
-from __future__ import print_function
 
 import os
 import sys
diff --git a/tools/grit/grit/tool/android2grd.py b/tools/grit/grit/tool/android2grd.py
index 026b2e2..8526a7ef 100644
--- a/tools/grit/grit/tool/android2grd.py
+++ b/tools/grit/grit/tool/android2grd.py
@@ -4,7 +4,6 @@
 
 """The 'grit android2grd' tool."""
 
-from __future__ import print_function
 
 import getopt
 import os.path
@@ -13,7 +12,7 @@
 import xml.dom.minidom
 
 import six
-from six import StringIO
+from io import StringIO
 
 import grit.node.empty
 from grit.node import node_io
@@ -176,7 +175,7 @@
 
     # Do the hard work -- convert the Android dom to grd file contents.
     grd_dom = self.AndroidDomToGrdDom(android_dom)
-    grd_string = six.text_type(grd_dom)
+    grd_string = str(grd_dom)
 
     # Write the grd string to a file in grd_dir.
     grd_filename = self.name + '.grd'
@@ -406,7 +405,7 @@
     xtb_file = os.path.normpath(os.path.join(
         self.xtb_dir, '%s_%s.xtb' % (self.name, lang)))
     fnode = node_io.FileNode()
-    fnode.StartParsing(u'file', translations_node)
+    fnode.StartParsing('file', translations_node)
     fnode.HandleAttribute('path', xtb_file)
     fnode.HandleAttribute('lang', lang)
     fnode.EndParsing()
@@ -417,11 +416,11 @@
     """Creates the <output> element corresponding to the generated c header."""
     header_file_name = os.path.join(header_dir, self.name + '.h')
     header_node = node_io.OutputNode()
-    header_node.StartParsing(u'output', outputs_node)
+    header_node.StartParsing('output', outputs_node)
     header_node.HandleAttribute('filename', header_file_name)
     header_node.HandleAttribute('type', 'rc_header')
     emit_node = node_io.EmitNode()
-    emit_node.StartParsing(u'emit', header_node)
+    emit_node.StartParsing('emit', header_node)
     emit_node.HandleAttribute('emit_type', 'prepend')
     emit_node.EndParsing()
     header_node.AddChild(emit_node)
@@ -434,7 +433,7 @@
     rc_file_name = self.name + '_' + lang + ".rc"
     rc_path = os.path.join(rc_dir, rc_file_name)
     node = node_io.OutputNode()
-    node.StartParsing(u'output', outputs_node)
+    node.StartParsing('output', outputs_node)
     node.HandleAttribute('filename', rc_path)
     node.HandleAttribute('lang', lang)
     node.HandleAttribute('type', 'rc_all')
@@ -462,7 +461,7 @@
         xml_res_dir, values, 'strings.xml'))
 
     node = node_io.OutputNode()
-    node.StartParsing(u'output', outputs_node)
+    node.StartParsing('output', outputs_node)
     node.HandleAttribute('filename', xml_path)
     node.HandleAttribute('lang', locale)
     node.HandleAttribute('type', 'android')
diff --git a/tools/grit/grit/tool/android2grd_unittest.py b/tools/grit/grit/tool/android2grd_unittest.py
index 7abb3e6..d25bbf59 100755
--- a/tools/grit/grit/tool/android2grd_unittest.py
+++ b/tools/grit/grit/tool/android2grd_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.tool.android2grd'''
 
-from __future__ import print_function
 
 import os
 import sys
diff --git a/tools/grit/grit/tool/build.py b/tools/grit/grit/tool/build.py
index c6a9e8d7..664b9f6 100644
--- a/tools/grit/grit/tool/build.py
+++ b/tools/grit/grit/tool/build.py
@@ -5,7 +5,6 @@
 '''The 'grit build' tool.
 '''
 
-from __future__ import print_function
 
 import collections
 import codecs
@@ -340,7 +339,7 @@
     # inefficient to call write once per character/byte.  Handle all of this
     # ourselves by calling write directly on strings/bytes before falling back
     # to writelines.
-    if isinstance(formatted, (six.string_types, six.binary_type)):
+    if isinstance(formatted, ((str,), bytes)):
       outfile.write(formatted)
     else:
       outfile.writelines(formatted)
diff --git a/tools/grit/grit/tool/build_unittest.py b/tools/grit/grit/tool/build_unittest.py
index 04bdaa3..4ad7c9f 100755
--- a/tools/grit/grit/tool/build_unittest.py
+++ b/tools/grit/grit/tool/build_unittest.py
@@ -6,7 +6,6 @@
 '''Unit tests for the 'grit build' tool.
 '''
 
-from __future__ import print_function
 
 import codecs
 import os
@@ -37,7 +36,7 @@
     # another <message>.
     output_dir = util.TempDir({})
     builder = build.RcBuilder()
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = util.PathFromRoot('grit/testdata/substitute.grd')
         self.verbose = False
@@ -48,7 +47,7 @@
   def testGenerateDepFile(self):
     output_dir = util.TempDir({})
     builder = build.RcBuilder()
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = util.PathFromRoot('grit/testdata/depfile.grd')
         self.verbose = False
@@ -58,14 +57,14 @@
                               '--depdir', output_dir.GetPath(),
                               '--depfile', expected_dep_file])
 
-    self.failUnless(os.path.isfile(expected_dep_file))
+    self.assertTrue(os.path.isfile(expected_dep_file))
     with open(expected_dep_file) as f:
       line = f.readline()
       (dep_output_file, deps_string) = line.split(': ')
       deps = deps_string.split(' ')
 
-      self.failUnlessEqual("default_100_percent.pak", dep_output_file)
-      self.failUnlessEqual(deps, [
+      self.assertEqual("default_100_percent.pak", dep_output_file)
+      self.assertEqual(deps, [
           util.PathFromRoot('grit/testdata/default_100_percent/a.png'),
           util.PathFromRoot('grit/testdata/grit_part.grdp'),
           util.PathFromRoot('grit/testdata/special_100_percent/a.png'),
@@ -75,7 +74,7 @@
   def testGenerateDepFileWithResourceIds(self):
     output_dir = util.TempDir({})
     builder = build.RcBuilder()
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = util.PathFromRoot('grit/testdata/substitute_no_ids.grd')
         self.verbose = False
@@ -87,23 +86,23 @@
          '--depdir', output_dir.GetPath(),
          '--depfile', expected_dep_file])
 
-    self.failUnless(os.path.isfile(expected_dep_file))
+    self.assertTrue(os.path.isfile(expected_dep_file))
     with open(expected_dep_file) as f:
       line = f.readline()
       (dep_output_file, deps_string) = line.split(': ')
       deps = deps_string.split(' ')
 
-      self.failUnlessEqual("resource.h", dep_output_file)
-      self.failUnlessEqual(2, len(deps))
-      self.failUnlessEqual(deps[0],
+      self.assertEqual("resource.h", dep_output_file)
+      self.assertEqual(2, len(deps))
+      self.assertEqual(deps[0],
           util.PathFromRoot('grit/testdata/substitute.xmb'))
-      self.failUnlessEqual(deps[1],
+      self.assertEqual(deps[1],
           util.PathFromRoot('grit/testdata/resource_ids'))
     output_dir.CleanUp()
 
   def testAssertOutputs(self):
     output_dir = util.TempDir({})
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = util.PathFromRoot('grit/testdata/substitute.grd')
         self.verbose = False
@@ -111,7 +110,7 @@
 
     # Incomplete output file list should fail.
     builder_fail = build.RcBuilder()
-    self.failUnlessEqual(2,
+    self.assertEqual(2,
         builder_fail.Run(DummyOpts(), [
             '-o', output_dir.GetPath(),
             '-a', os.path.abspath(
@@ -119,7 +118,7 @@
 
     # Complete output file list should succeed.
     builder_ok = build.RcBuilder()
-    self.failUnlessEqual(0,
+    self.assertEqual(0,
         builder_ok.Run(DummyOpts(), [
             '-o', output_dir.GetPath(),
             '-a', os.path.abspath(
@@ -131,7 +130,7 @@
 
   def testAssertTemplateOutputs(self):
     output_dir = util.TempDir({})
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = util.PathFromRoot('grit/testdata/substitute_tmpl.grd')
         self.verbose = False
@@ -139,7 +138,7 @@
 
     # Incomplete output file list should fail.
     builder_fail = build.RcBuilder()
-    self.failUnlessEqual(2,
+    self.assertEqual(2,
         builder_fail.Run(DummyOpts(), [
             '-o', output_dir.GetPath(),
             '-E', 'name=foo',
@@ -147,7 +146,7 @@
 
     # Complete output file list should succeed.
     builder_ok = build.RcBuilder()
-    self.failUnlessEqual(0,
+    self.assertEqual(0,
         builder_ok.Run(DummyOpts(), [
             '-o', output_dir.GetPath(),
             '-E', 'name=foo',
@@ -161,7 +160,7 @@
                                allowlisted_ids,
                                non_allowlisted_ids,
                                encoding='utf8'):
-    self.failUnless(os.path.exists(filename))
+    self.assertTrue(os.path.exists(filename))
     allowlisted_ids_found = []
     non_allowlisted_ids_found = []
     with codecs.open(filename, encoding=encoding) as f:
@@ -189,7 +188,7 @@
   def testAllowlistStrings(self):
     output_dir = util.TempDir({})
     builder = build.RcBuilder()
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = util.PathFromRoot('grit/testdata/allowlist_strings.grd')
         self.verbose = False
@@ -216,7 +215,7 @@
   def testAllowlistResources(self):
     output_dir = util.TempDir({})
     builder = build.RcBuilder()
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = util.PathFromRoot('grit/testdata/allowlist_resources.grd')
         self.verbose = False
@@ -231,8 +230,8 @@
 
     # Ensure the resource map header and .pak files exist, but don't verify
     # their content.
-    self.failUnless(os.path.exists(map_h))
-    self.failUnless(os.path.exists(pak))
+    self.assertTrue(os.path.exists(map_h))
+    self.assertTrue(os.path.exists(pak))
 
     allowlisted_ids = [
         'IDR_STRUCTURE_ALLOWLISTED',
@@ -257,7 +256,7 @@
   def testWriteOnlyNew(self):
     output_dir = util.TempDir({})
     builder = build.RcBuilder()
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = util.PathFromRoot('grit/testdata/substitute.grd')
         self.verbose = False
@@ -266,19 +265,19 @@
     header = output_dir.GetPath('resource.h')
 
     builder.Run(DummyOpts(), ['-o', output_dir.GetPath()])
-    self.failUnless(os.path.exists(header))
+    self.assertTrue(os.path.exists(header))
     first_mtime = os.stat(header).st_mtime
 
     os.utime(header, (UNCHANGED, UNCHANGED))
     builder.Run(DummyOpts(),
                 ['-o', output_dir.GetPath(), '--write-only-new', '0'])
-    self.failUnless(os.path.exists(header))
+    self.assertTrue(os.path.exists(header))
     second_mtime = os.stat(header).st_mtime
 
     os.utime(header, (UNCHANGED, UNCHANGED))
     builder.Run(DummyOpts(),
                 ['-o', output_dir.GetPath(), '--write-only-new', '1'])
-    self.failUnless(os.path.exists(header))
+    self.assertTrue(os.path.exists(header))
     third_mtime = os.stat(header).st_mtime
 
     self.assertTrue(abs(second_mtime - UNCHANGED) > 5)
@@ -288,7 +287,7 @@
   def testGenerateDepFileWithDependOnStamp(self):
     output_dir = util.TempDir({})
     builder = build.RcBuilder()
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = util.PathFromRoot('grit/testdata/substitute.grd')
         self.verbose = False
@@ -303,7 +302,7 @@
                               '--depdir', output_dir.GetPath(),
                               '--depfile', expected_dep_file,
                               '--depend-on-stamp'])
-    self.failUnless(os.path.isfile(expected_stamp_file))
+    self.assertTrue(os.path.isfile(expected_stamp_file))
     first_mtime = os.stat(expected_stamp_file).st_mtime
 
     # Reset mtime to very old.
@@ -314,21 +313,21 @@
                               '--depdir', output_dir.GetPath(),
                               '--depfile', expected_dep_file,
                               '--depend-on-stamp'])
-    self.failUnless(os.path.isfile(expected_stamp_file))
+    self.assertTrue(os.path.isfile(expected_stamp_file))
     second_mtime = os.stat(expected_stamp_file).st_mtime
 
     # Some OS have a 2s stat resolution window, so can't do a direct comparison.
     self.assertTrue((second_mtime - OLDTIME) > 5)
     self.assertTrue(abs(second_mtime - first_mtime) < 5)
 
-    self.failUnless(os.path.isfile(expected_dep_file))
+    self.assertTrue(os.path.isfile(expected_dep_file))
     with open(expected_dep_file) as f:
       line = f.readline()
       (dep_output_file, deps_string) = line.split(': ')
       deps = deps_string.split(' ')
 
-      self.failUnlessEqual(expected_stamp_file_name, dep_output_file)
-      self.failUnlessEqual(deps, [
+      self.assertEqual(expected_stamp_file_name, dep_output_file)
+      self.assertEqual(deps, [
           util.PathFromRoot('grit/testdata/substitute.xmb'),
       ])
     output_dir.CleanUp()
diff --git a/tools/grit/grit/tool/buildinfo.py b/tools/grit/grit/tool/buildinfo.py
index 74fbb38..143e8835 100644
--- a/tools/grit/grit/tool/buildinfo.py
+++ b/tools/grit/grit/tool/buildinfo.py
@@ -5,7 +5,6 @@
 """Output the list of files to be generated by GRIT from an input.
 """
 
-from __future__ import print_function
 
 import getopt
 import os
diff --git a/tools/grit/grit/tool/buildinfo_unittest.py b/tools/grit/grit/tool/buildinfo_unittest.py
index 991244d..d2b7f9b 100755
--- a/tools/grit/grit/tool/buildinfo_unittest.py
+++ b/tools/grit/grit/tool/buildinfo_unittest.py
@@ -37,7 +37,7 @@
     """Find all of the inputs and outputs for a GRD file."""
     info_object = buildinfo.DetermineBuildInfo()
 
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = '../grit/testdata/buildinfo.grd'
         self.print_header = False
@@ -45,23 +45,23 @@
         self.extra_verbose = False
     info_object.Run(DummyOpts(), [])
     output = self.buf.getvalue().replace('\\', '/')
-    self.failUnless(output.count(r'rc_all|sv_sidebar_loading.html'))
-    self.failUnless(output.count(r'rc_header|resource.h'))
-    self.failUnless(output.count(r'rc_all|en_generated_resources.rc'))
-    self.failUnless(output.count(r'rc_all|sv_generated_resources.rc'))
-    self.failUnless(output.count(r'input|../grit/testdata/substitute.xmb'))
-    self.failUnless(output.count(r'input|../grit/testdata/pr.bmp'))
-    self.failUnless(output.count(r'input|../grit/testdata/pr2.bmp'))
-    self.failUnless(
+    self.assertTrue(output.count(r'rc_all|sv_sidebar_loading.html'))
+    self.assertTrue(output.count(r'rc_header|resource.h'))
+    self.assertTrue(output.count(r'rc_all|en_generated_resources.rc'))
+    self.assertTrue(output.count(r'rc_all|sv_generated_resources.rc'))
+    self.assertTrue(output.count(r'input|../grit/testdata/substitute.xmb'))
+    self.assertTrue(output.count(r'input|../grit/testdata/pr.bmp'))
+    self.assertTrue(output.count(r'input|../grit/testdata/pr2.bmp'))
+    self.assertTrue(
         output.count(r'input|../grit/testdata/sidebar_loading.html'))
-    self.failUnless(output.count(r'input|../grit/testdata/transl.rc'))
-    self.failUnless(output.count(r'input|../grit/testdata/transl1.rc'))
+    self.assertTrue(output.count(r'input|../grit/testdata/transl.rc'))
+    self.assertTrue(output.count(r'input|../grit/testdata/transl1.rc'))
 
   def testBuildOutputWithDir(self):
     """Find all the inputs and outputs for a GRD file with an output dir."""
     info_object = buildinfo.DetermineBuildInfo()
 
-    class DummyOpts(object):
+    class DummyOpts:
       def __init__(self):
         self.input = '../grit/testdata/buildinfo.grd'
         self.print_header = False
@@ -69,17 +69,17 @@
         self.extra_verbose = False
     info_object.Run(DummyOpts(), ['-o', '../grit/testdata'])
     output = self.buf.getvalue().replace('\\', '/')
-    self.failUnless(
+    self.assertTrue(
         output.count(r'rc_all|../grit/testdata/sv_sidebar_loading.html'))
-    self.failUnless(output.count(r'rc_header|../grit/testdata/resource.h'))
-    self.failUnless(
+    self.assertTrue(output.count(r'rc_header|../grit/testdata/resource.h'))
+    self.assertTrue(
         output.count(r'rc_all|../grit/testdata/en_generated_resources.rc'))
-    self.failUnless(
+    self.assertTrue(
         output.count(r'rc_all|../grit/testdata/sv_generated_resources.rc'))
-    self.failUnless(output.count(r'input|../grit/testdata/substitute.xmb'))
-    self.failUnlessEqual(0,
+    self.assertTrue(output.count(r'input|../grit/testdata/substitute.xmb'))
+    self.assertEqual(0,
         output.count(r'rc_all|../grit/testdata/sv_welcome_toast.html'))
-    self.failUnless(
+    self.assertTrue(
         output.count(r'rc_all|../grit/testdata/en_welcome_toast.html'))
 
 
diff --git a/tools/grit/grit/tool/count.py b/tools/grit/grit/tool/count.py
index 54a3224..2bba181 100644
--- a/tools/grit/grit/tool/count.py
+++ b/tools/grit/grit/tool/count.py
@@ -4,7 +4,6 @@
 
 '''Count number of occurrences of a given message ID.'''
 
-from __future__ import print_function
 
 import getopt
 import sys
diff --git a/tools/grit/grit/tool/diff_structures.py b/tools/grit/grit/tool/diff_structures.py
index 61b6edf..e2f49678 100644
--- a/tools/grit/grit/tool/diff_structures.py
+++ b/tools/grit/grit/tool/diff_structures.py
@@ -5,7 +5,6 @@
 '''The 'grit sdiff' tool.
 '''
 
-from __future__ import print_function
 
 import os
 import getopt
diff --git a/tools/grit/grit/tool/diff_structures_unittest.py b/tools/grit/grit/tool/diff_structures_unittest.py
index 8dfb4c0..3e99d14 100755
--- a/tools/grit/grit/tool/diff_structures_unittest.py
+++ b/tools/grit/grit/tool/diff_structures_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for the 'grit newgrd' tool.'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -17,7 +16,7 @@
 from grit.tool import diff_structures
 
 
-class DummyOpts(object):
+class DummyOpts:
   """Options needed by NewGrd."""
 
 
diff --git a/tools/grit/grit/tool/interface.py b/tools/grit/grit/tool/interface.py
index 8ed460d..1f46ab9 100644
--- a/tools/grit/grit/tool/interface.py
+++ b/tools/grit/grit/tool/interface.py
@@ -5,9 +5,8 @@
 '''Base class and interface for tools.
 '''
 
-from __future__ import print_function
 
-class Tool(object):
+class Tool:
   '''Base class for all tools.  Tools should use their docstring (i.e. the
   class-level docstring) for the help they want to have printed when they
   are invoked.'''
diff --git a/tools/grit/grit/tool/menu_from_parts.py b/tools/grit/grit/tool/menu_from_parts.py
index 269531c2..7e0466a 100644
--- a/tools/grit/grit/tool/menu_from_parts.py
+++ b/tools/grit/grit/tool/menu_from_parts.py
@@ -4,7 +4,6 @@
 
 '''The 'grit menufromparts' tool.'''
 
-from __future__ import print_function
 
 import six
 
@@ -61,7 +60,7 @@
 
         contents = message.GetContent()
         for part in contents:
-          if isinstance(part, six.string_types):
+          if isinstance(part, str):
             id = grit.extern.tclib.GenerateMessageId(part)
             if id not in xtb:
               print("WARNING didn't find all translations for menu %s" %
diff --git a/tools/grit/grit/tool/newgrd.py b/tools/grit/grit/tool/newgrd.py
index 68376a1..88cfbab 100644
--- a/tools/grit/grit/tool/newgrd.py
+++ b/tools/grit/grit/tool/newgrd.py
@@ -5,7 +5,6 @@
 '''Tool to create a new, empty .grd file with all the basic sections.
 '''
 
-from __future__ import print_function
 
 import getopt
 import sys
diff --git a/tools/grit/grit/tool/newgrd_unittest.py b/tools/grit/grit/tool/newgrd_unittest.py
index 2d6b25c..df51de5 100755
--- a/tools/grit/grit/tool/newgrd_unittest.py
+++ b/tools/grit/grit/tool/newgrd_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for the 'grit newgrd' tool.'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -18,7 +17,7 @@
 from grit.tool import newgrd
 
 
-class DummyOpts(object):
+class DummyOpts:
   """Options needed by NewGrd."""
 
 
diff --git a/tools/grit/grit/tool/postprocess_interface.py b/tools/grit/grit/tool/postprocess_interface.py
index f8ade49..a4fc678 100644
--- a/tools/grit/grit/tool/postprocess_interface.py
+++ b/tools/grit/grit/tool/postprocess_interface.py
@@ -5,9 +5,8 @@
 ''' Base class for postprocessing of RC files.
 '''
 
-from __future__ import print_function
 
-class PostProcessor(object):
+class PostProcessor:
   ''' Base class for postprocessing of the RC file data before being
   output through the RC2GRD tool. You should implement this class if
   you want GRIT to do specific things to the RC files after it has
diff --git a/tools/grit/grit/tool/postprocess_unittest.py b/tools/grit/grit/tool/postprocess_unittest.py
index 5556a826..ad4938fe 100755
--- a/tools/grit/grit/tool/postprocess_unittest.py
+++ b/tools/grit/grit/tool/postprocess_unittest.py
@@ -8,7 +8,6 @@
    modify the grd data tree, changing the message name attributes.
 '''
 
-from __future__ import print_function
 
 import os
 import re
@@ -33,16 +32,16 @@
 END
     '''
     tool = rc2grd.Rc2Grd()
-    class DummyOpts(object):
+    class DummyOpts:
       verbose = False
       extra_verbose = False
     tool.o = DummyOpts()
     tool.post_process = 'grit.tool.postprocess_unittest.DummyPostProcessor'
     result = tool.Process(rctext, '.\resource.rc')
 
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[0].attrs['name'] == 'SMART_STRING_1')
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[1].attrs['name'] == 'SMART_STRING_2')
 
 class DummyPostProcessor(grit.tool.postprocess_interface.PostProcessor):
diff --git a/tools/grit/grit/tool/preprocess_interface.py b/tools/grit/grit/tool/preprocess_interface.py
index 494fe792..4fef71c 100644
--- a/tools/grit/grit/tool/preprocess_interface.py
+++ b/tools/grit/grit/tool/preprocess_interface.py
@@ -5,9 +5,8 @@
 ''' Base class for preprocessing of RC files.
 '''
 
-from __future__ import print_function
 
-class PreProcessor(object):
+class PreProcessor:
   ''' Base class for preprocessing of the RC file data before being
   output through the RC2GRD tool. You should implement this class if
   you have specific constructs in your RC files that GRIT cannot handle.'''
diff --git a/tools/grit/grit/tool/preprocess_unittest.py b/tools/grit/grit/tool/preprocess_unittest.py
index 629ea0a..da0df3b 100755
--- a/tools/grit/grit/tool/preprocess_unittest.py
+++ b/tools/grit/grit/tool/preprocess_unittest.py
@@ -8,7 +8,6 @@
    provide the actual rctext data.
 '''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -25,14 +24,14 @@
 
   def testPreProcessing(self):
     tool = rc2grd.Rc2Grd()
-    class DummyOpts(object):
+    class DummyOpts:
       verbose = False
       extra_verbose = False
     tool.o = DummyOpts()
     tool.pre_process = 'grit.tool.preprocess_unittest.DummyPreProcessor'
     result = tool.Process('', '.\resource.rc')
 
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[0].attrs['name'] == 'DUMMY_STRING_1')
 
 class DummyPreProcessor(grit.tool.preprocess_interface.PreProcessor):
diff --git a/tools/grit/grit/tool/rc2grd.py b/tools/grit/grit/tool/rc2grd.py
index 3578fd7f..291f2e71 100644
--- a/tools/grit/grit/tool/rc2grd.py
+++ b/tools/grit/grit/tool/rc2grd.py
@@ -4,7 +4,6 @@
 
 '''The 'grit rc2grd' tool.'''
 
-from __future__ import print_function
 
 import os.path
 import getopt
@@ -12,7 +11,7 @@
 import sys
 
 import six
-from six import StringIO
+from io import StringIO
 
 import grit.node.empty
 from grit.node import include
@@ -201,7 +200,7 @@
                 os.path.splitext(os.path.basename(path))[0] + '.grd')
 
     rctext = util.ReadFile(path, self.input_encoding)
-    grd_text = six.text_type(self.Process(rctext, path))
+    grd_text = str(self.Process(rctext, path))
     with util.WrapOutputStream(open(out_path, 'wb'), 'utf-8') as outfile:
       outfile.write(grd_text)
 
@@ -341,7 +340,7 @@
         # Messages that contain only placeholders do not need translation.
         is_translateable = False
         for item in msg_obj.GetContent():
-          if isinstance(item, six.string_types):
+          if isinstance(item, str):
             if not _WHITESPACE_ONLY.match(item):
               is_translateable = True
 
@@ -389,7 +388,7 @@
       # TODO(joi) Allow use of non-TotalRecall flavors of HTML placeholderizing
       msg = tr_html.HtmlToMessage(text, True)
       for item in msg.GetContent():
-        if not isinstance(item, six.string_types):
+        if not isinstance(item, str):
           return msg  # Contained at least one placeholder, so we're done
 
       # HTML placeholderization didn't do anything, so try to find printf or
diff --git a/tools/grit/grit/tool/rc2grd_unittest.py b/tools/grit/grit/tool/rc2grd_unittest.py
index 5d7d972..ac8cf92a 100755
--- a/tools/grit/grit/tool/rc2grd_unittest.py
+++ b/tools/grit/grit/tool/rc2grd_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.tool.rc2grd'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -15,7 +14,7 @@
 import re
 import unittest
 
-from six import StringIO
+from io import StringIO
 
 from grit import grd_reader
 from grit import util
@@ -28,16 +27,16 @@
     tool = rc2grd.Rc2Grd()
     original = "Hello %s, how are you? I'm $1 years old!"
     msg = tool.Placeholderize(original)
-    self.failUnless(msg.GetPresentableContent() == "Hello TODO_0001, how are you? I'm TODO_0002 years old!")
-    self.failUnless(msg.GetRealContent() == original)
+    self.assertTrue(msg.GetPresentableContent() == "Hello TODO_0001, how are you? I'm TODO_0002 years old!")
+    self.assertTrue(msg.GetRealContent() == original)
 
   def testHtmlPlaceholderize(self):
     tool = rc2grd.Rc2Grd()
     original = "Hello <b>[USERNAME]</b>, how are you? I'm [AGE] years old!"
     msg = tool.Placeholderize(original)
-    self.failUnless(msg.GetPresentableContent() ==
+    self.assertTrue(msg.GetPresentableContent() ==
                     "Hello BEGIN_BOLDX_USERNAME_XEND_BOLD, how are you? I'm X_AGE_X years old!")
-    self.failUnless(msg.GetRealContent() == original)
+    self.assertTrue(msg.GetRealContent() == original)
 
   def testMenuWithoutWhitespaceRegression(self):
     # There was a problem in the original regular expression for parsing out
@@ -62,7 +61,7 @@
 END
 
 '''
-    self.failUnless(len(rc2grd._MENU.findall(two_menus)) == 2)
+    self.assertTrue(len(rc2grd._MENU.findall(two_menus)) == 2)
 
   def testRegressionScriptWithTranslateable(self):
     tool = rc2grd.Rc2Grd()
@@ -78,11 +77,11 @@
 
     rc_text = '''STRINGTABLE\nBEGIN\nID_BINGO "<SPAN id=hp style='BEHAVIOR: url(#default#homepage)'></SPAN><script>if (!hp.isHomePage('[$~HOMEPAGE~$]')) {document.write(""<a href=\\""[$~SETHOMEPAGEURL~$]\\"" >Set As Homepage</a> - "");}</script>"\nEND\n'''
     tool.AddMessages(rc_text, tool.o)
-    self.failUnless(tool.o.node.GetCdata().find('Set As Homepage') != -1)
+    self.assertTrue(tool.o.node.GetCdata().find('Set As Homepage') != -1)
 
     # TODO(joi) Improve the HTML parser to support translateables inside
     # <script> blocks?
-    self.failUnless(tool.o.node.attrs['translateable'] == 'false')
+    self.assertTrue(tool.o.node.attrs['translateable'] == 'false')
 
   def testRoleModel(self):
     rc_text = ('STRINGTABLE\n'
@@ -116,30 +115,30 @@
       </grit>'''), dir='.')
 
     # test rig
-    class DummyOpts(object):
+    class DummyOpts:
       verbose = False
       extra_verbose = False
     tool.o = DummyOpts()
     result = tool.Process(rc_text, '.\resource.rc')
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[0].attrs['desc'] == '')
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[0].children[0].attrs['name'] == 'USERNAME')
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[1].attrs['desc'] == 'The other description')
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[1].attrs['meaning'] == '')
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[1].children[0].attrs['name'] == 'USERNAME')
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[1].children[1].attrs['name'] == 'ADMINNAME')
-    self.failUnless(
+    self.assertTrue(
       result.children[2].children[2].children[2].children[0].attrs['name'] == 'LIST_OF_PROGRAMS')
 
   def testRunOutput(self):
     """Verify basic correct Run behavior."""
     tool = rc2grd.Rc2Grd()
-    class DummyOpts(object):
+    class DummyOpts:
       verbose = False
       extra_verbose = False
     with util.TempDir({}) as output_dir:
@@ -151,7 +150,7 @@
   def testMissingOutput(self):
     """Verify failure with no args."""
     tool = rc2grd.Rc2Grd()
-    class DummyOpts(object):
+    class DummyOpts:
       verbose = False
       extra_verbose = False
     ret = tool.Run(DummyOpts(), [])
diff --git a/tools/grit/grit/tool/resize.py b/tools/grit/grit/tool/resize.py
index cf6aa9d..58524010 100644
--- a/tools/grit/grit/tool/resize.py
+++ b/tools/grit/grit/tool/resize.py
@@ -5,7 +5,6 @@
 '''The 'grit resize' tool.
 '''
 
-from __future__ import print_function
 
 import getopt
 import os
diff --git a/tools/grit/grit/tool/test.py b/tools/grit/grit/tool/test.py
index 9f9d503..dc21de6 100644
--- a/tools/grit/grit/tool/test.py
+++ b/tools/grit/grit/tool/test.py
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from __future__ import print_function
 
 from grit.tool import interface
 
diff --git a/tools/grit/grit/tool/transl2tc.py b/tools/grit/grit/tool/transl2tc.py
index 78998d3..16092aab 100644
--- a/tools/grit/grit/tool/transl2tc.py
+++ b/tools/grit/grit/tool/transl2tc.py
@@ -5,7 +5,6 @@
 '''The 'grit transl2tc' tool.
 '''
 
-from __future__ import print_function
 
 from grit import grd_reader
 from grit import util
diff --git a/tools/grit/grit/tool/transl2tc_unittest.py b/tools/grit/grit/tool/transl2tc_unittest.py
index fecac46..7b978afd 100755
--- a/tools/grit/grit/tool/transl2tc_unittest.py
+++ b/tools/grit/grit/tool/transl2tc_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for the 'grit transl2tc' tool.'''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -14,7 +13,7 @@
 
 import unittest
 
-from six import StringIO
+from io import StringIO
 
 from grit.tool import transl2tc
 from grit import grd_reader
@@ -39,7 +38,7 @@
     ]
     tool.WriteTranslations(buf, translations)
     output = buf.getvalue()
-    self.failUnless(output.strip() == '''
+    self.assertTrue(output.strip() == '''
 1 Hello USERNAME, how are you?
 12 Howdie doodie!
 123 Hello
@@ -107,26 +106,26 @@
     values = list(translations.values())
     output = output_buf.getvalue()
 
-    self.failUnless('Ein' in values)
-    self.failUnless('NUMBIRDS Vogeln' in values)
-    self.failUnless('ITEM von COUNT' in values)
-    self.failUnless(values.count('Hallo') == 1)
-    self.failIf('Dass war die alte Version' in values)
-    self.failIf(':' in values)
-    self.failIf('Dokument FILENAME ist entfernt worden' in values)
-    self.failIf('Nicht verwendet' in values)
-    self.failUnless(('Howdie' in values or 'Hallo sagt man' in values) and not
+    self.assertTrue('Ein' in values)
+    self.assertTrue('NUMBIRDS Vogeln' in values)
+    self.assertTrue('ITEM von COUNT' in values)
+    self.assertTrue(values.count('Hallo') == 1)
+    self.assertFalse('Dass war die alte Version' in values)
+    self.assertFalse(':' in values)
+    self.assertFalse('Dokument FILENAME ist entfernt worden' in values)
+    self.assertFalse('Nicht verwendet' in values)
+    self.assertTrue(('Howdie' in values or 'Hallo sagt man' in values) and not
       ('Howdie' in values and 'Hallo sagt man' in values))
 
-    self.failUnless('XX01XX&SkraXX02XX&HaettaXX03XXThetta er "Klonk" sem eg fylaXX04XXgonkurinnXX05XXKlonk && er [good]XX06XX&HjalpXX07XX&Um...XX08XX' in values)
+    self.assertTrue('XX01XX&SkraXX02XX&HaettaXX03XXThetta er "Klonk" sem eg fylaXX04XXgonkurinnXX05XXKlonk && er [good]XX06XX&HjalpXX07XX&Um...XX08XX' in values)
 
-    self.failUnless('I lagi' in values)
+    self.assertTrue('I lagi' in values)
 
-    self.failUnless(output.count('Structure of message IDS_REORDERED_PLACEHOLDERS has changed'))
-    self.failUnless(output.count('Message IDS_CHANGED has changed'))
-    self.failUnless(output.count('Structure of message IDS_LONGER_TRANSLATED has changed'))
-    self.failUnless(output.count('Two different translations for "Howdie"'))
-    self.failUnless(output.count('IDD_DIFFERENT_LENGTH_IN_TRANSL has wrong # of cliques'))
+    self.assertTrue(output.count('Structure of message IDS_REORDERED_PLACEHOLDERS has changed'))
+    self.assertTrue(output.count('Message IDS_CHANGED has changed'))
+    self.assertTrue(output.count('Structure of message IDS_LONGER_TRANSLATED has changed'))
+    self.assertTrue(output.count('Two different translations for "Howdie"'))
+    self.assertTrue(output.count('IDD_DIFFERENT_LENGTH_IN_TRANSL has wrong # of cliques'))
 
 
 if __name__ == '__main__':
diff --git a/tools/grit/grit/tool/unit.py b/tools/grit/grit/tool/unit.py
index 7885081..316db8f8 100644
--- a/tools/grit/grit/tool/unit.py
+++ b/tools/grit/grit/tool/unit.py
@@ -4,7 +4,6 @@
 
 '''GRIT tool that runs the unit test suite for GRIT.'''
 
-from __future__ import print_function
 
 import getopt
 import sys
diff --git a/tools/grit/grit/tool/update_resource_ids/__init__.py b/tools/grit/grit/tool/update_resource_ids/__init__.py
index fe8c2ed..8aba863 100644
--- a/tools/grit/grit/tool/update_resource_ids/__init__.py
+++ b/tools/grit/grit/tool/update_resource_ids/__init__.py
@@ -64,7 +64,6 @@
 structure elements 1-3 stated above.
 """
 
-from __future__ import print_function
 
 import argparse
 import collections
@@ -82,7 +81,7 @@
     data = sys.stdin.read()
     file_dir = os.getcwd()
   else:
-    with open(input_file, 'rt') as f:
+    with open(input_file) as f:
       data = f.read()
     file_dir = os.path.dirname(input_file)
   return data, file_dir
@@ -178,7 +177,7 @@
 """
 
   def __init(self):
-    super(UpdateResourceIds, self).__init__()
+    super().__init__()
 
   def ShortDescription(self):
     return 'Updates a resource_ids file based on usage, preserving structure'
diff --git a/tools/grit/grit/tool/update_resource_ids/assigner.py b/tools/grit/grit/tool/update_resource_ids/assigner.py
index c69da5d..a3c5a76 100644
--- a/tools/grit/grit/tool/update_resource_ids/assigner.py
+++ b/tools/grit/grit/tool/update_resource_ids/assigner.py
@@ -55,7 +55,7 @@
       yield tag, cur_id
 
 
-class BaseCoarseIdAssigner(object):
+class BaseCoarseIdAssigner:
   """Base class for coarse assignment."""
 
   def __init__(self, item_list, align):
@@ -79,7 +79,7 @@
   """CoarseIdAssigner that assigns item with non-overlapping start IDs."""
 
   def __init__(self, item_list, align):
-    super(NaiveCoarseIdAssigner, self).__init__(item_list, align)
+    super().__init__(item_list, align)
     first_id = self._item_list[0].tags[0].id
     self._cur_id = common.AlignUp(first_id, self._align)
 
@@ -93,7 +93,7 @@
 
 
 class DagCoarseIdAssigner(BaseCoarseIdAssigner):
-  """CoarseIdAssigner that preserves existing structure.
+  r"""CoarseIdAssigner that preserves existing structure.
 
 Start ID assignment in resource_ids is structured a Series-Parallel Graph, which
 is a directed, acyclic multi-graph generated by the following:
@@ -217,7 +217,7 @@
       self.weight = None
 
   def __init__(self, item_list, align):
-    super(DagCoarseIdAssigner, self).__init__(item_list, align)
+    super().__init__(item_list, align)
     self._node_dict = {}  # Maps from |lo| to item.
 
   def GenStartIds(self):
diff --git a/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py b/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
index cb75738..c74e575e 100755
--- a/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
+++ b/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
@@ -3,7 +3,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from __future__ import print_function
 
 import os
 import sys
@@ -134,11 +133,11 @@
     for exp, spec in test_cases:
       try:
         actual, new_spec = _RunCoarseIdAssigner(spec)
-        self.failUnlessEqual(exp, actual)
+        self.assertEqual(exp, actual)
         # Test that assignment is idempotent.
         actual2, new_spec2 = _RunCoarseIdAssigner(new_spec)
-        self.failUnlessEqual(actual, actual2)
-        self.failUnlessEqual(new_spec, new_spec2)
+        self.assertEqual(actual, actual2)
+        self.assertEqual(new_spec, new_spec2)
       except Exception as e:
         print(common.Color.RED(traceback.format_exc().rstrip()))
         print('Failed spec: %s' % common.Color.CYAN(spec))
diff --git a/tools/grit/grit/tool/update_resource_ids/parser.py b/tools/grit/grit/tool/update_resource_ids/parser.py
index d9bc87c..aced611 100644
--- a/tools/grit/grit/tool/update_resource_ids/parser.py
+++ b/tools/grit/grit/tool/update_resource_ids/parser.py
@@ -8,7 +8,6 @@
 integers).
 """
 
-from __future__ import print_function
 
 _isWhitespace = lambda ch: ch in ' \t\n'
 _isNotNewline = lambda ch: ch != '\n'
diff --git a/tools/grit/grit/tool/update_resource_ids/reader.py b/tools/grit/grit/tool/update_resource_ids/reader.py
index 77360ce..314573e 100644
--- a/tools/grit/grit/tool/update_resource_ids/reader.py
+++ b/tools/grit/grit/tool/update_resource_ids/reader.py
@@ -9,7 +9,6 @@
 produces a conservative estimate of ID usages.
 """
 
-from __future__ import print_function
 
 import collections
 import os
@@ -18,7 +17,7 @@
 from grit import util
 from grit.tool.update_resource_ids import common
 
-TAGS_OF_INTEREST = set(['include', 'message', 'structure'])
+TAGS_OF_INTEREST = {'include', 'message', 'structure'}
 
 def _CountResourceUsage(grd, seen_files):
   tag_name_to_count = {tag: set() for tag in TAGS_OF_INTEREST}
@@ -55,7 +54,7 @@
       yield item, tag_name_to_usage
     return
   for item in item_list:
-    supported_tag_names = set(tag.name for tag in item.tags)
+    supported_tag_names = {tag.name for tag in item.tags}
     if item.meta and 'sizes' in item.meta:
       # If META has "sizes" field, use it instead of reading GRD.
       tag_name_to_usage = collections.Counter()
diff --git a/tools/grit/grit/tool/xmb.py b/tools/grit/grit/tool/xmb.py
index a4b49f9..1c27d8a 100644
--- a/tools/grit/grit/tool/xmb.py
+++ b/tools/grit/grit/tool/xmb.py
@@ -5,7 +5,6 @@
 """The 'grit xmb' tool.
 """
 
-from __future__ import print_function
 
 import getopt
 import os
@@ -29,8 +28,8 @@
 
 # See XmlEscape below.
 _XML_QUOTE_ESCAPES = {
-    u"'":  u'&apos;',
-    u'"':  u'&quot;',
+    "'":  '&apos;',
+    '"':  '&quot;',
 }
 
 def _XmlEscape(s):
@@ -38,7 +37,7 @@
   internal Translation Console tool.  May be used for attributes as
   well as for contents.
   """
-  return saxutils.escape(six.text_type(s), _XML_QUOTE_ESCAPES).encode('utf-8')
+  return saxutils.escape(str(s), _XML_QUOTE_ESCAPES).encode('utf-8')
 
 
 def _WriteAttribute(file, name, value):
@@ -56,11 +55,11 @@
 
 def _WriteMessage(file, message):
   presentable_content = message.GetPresentableContent()
-  assert (isinstance(presentable_content, six.string_types) or
+  assert (isinstance(presentable_content, str) or
           (len(message.parts) == 1 and
            type(message.parts[0] == tclib.Placeholder)))
   preserve_space = presentable_content != _WHITESPACES_REGEX.sub(
-      u' ', presentable_content.strip())
+      ' ', presentable_content.strip())
 
   file.write(b'<msg')
   _WriteAttribute(file, 'desc', message.GetDescription())
@@ -160,7 +159,7 @@
   FORMAT_IDS_ONLY = 1
 
   def __init__(self, defines=None):
-    super(OutputXmb, self).__init__()
+    super().__init__()
     self.format = self.FORMAT_XMB
     self.defines = defines or {}
 
@@ -178,7 +177,7 @@
     own_opts, args = getopt.getopt(args, 'l:D:ih', ('help',))
     for key, val in own_opts:
       if key == '-l':
-        limit_file = open(val, 'r')
+        limit_file = open(val)
         limit_file_dir = util.dirname(val)
         if not len(limit_file_dir):
           limit_file_dir = '.'
diff --git a/tools/grit/grit/tool/xmb_unittest.py b/tools/grit/grit/tool/xmb_unittest.py
index 79b3c5b6..987677d 100755
--- a/tools/grit/grit/tool/xmb_unittest.py
+++ b/tools/grit/grit/tool/xmb_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for 'grit xmb' tool.'''
 
-from __future__ import print_function
 
 import io
 import os
@@ -16,7 +15,7 @@
 import unittest
 import xml.sax
 
-from six import StringIO
+from io import StringIO
 
 from grit import grd_reader
 from grit import util
@@ -26,7 +25,7 @@
 class XmbUnittest(unittest.TestCase):
   def setUp(self):
     self.res_tree = grd_reader.Parse(
-        io.BytesIO(u'''<?xml version="1.0" encoding="UTF-8"?>
+        io.BytesIO('''<?xml version="1.0" encoding="UTF-8"?>
       <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
         <release seq="3">
           <includes>
@@ -50,23 +49,23 @@
             <structure type="dialog" name="IDD_SPACYBOX" encoding="utf-16" file="grit/testdata/klonk.rc" />
           </structures>
         </release>
-      </grit>'''.encode('utf-8')), '.')
+      </grit>'''.encode()), '.')
     self.xmb_file = io.BytesIO()
 
   def testNormalOutput(self):
     xmb.OutputXmb().Process(self.res_tree, self.xmb_file)
     output = self.xmb_file.getvalue().decode('utf-8')
-    self.failUnless(output.count('Joi'))
-    self.failUnless(output.count('Yibbee'))
-    self.failUnless(output.count(u'Ol\xe1, \u4eca\u65e5\u306f! \U0001F60A'))
+    self.assertTrue(output.count('Joi'))
+    self.assertTrue(output.count('Yibbee'))
+    self.assertTrue(output.count('Ol\xe1, \u4eca\u65e5\u306f! \U0001F60A'))
 
   def testLimitList(self):
     limit_file = StringIO(
       'IDS_BONGOBINGO\nIDS_DOES_NOT_EXIST\nIDS_ALSO_DOES_NOT_EXIST')
     xmb.OutputXmb().Process(self.res_tree, self.xmb_file, limit_file, False)
     output = self.xmb_file.getvalue().decode('utf-8')
-    self.failUnless(output.count('Yibbee'))
-    self.failUnless(not output.count('Joi'))
+    self.assertTrue(output.count('Yibbee'))
+    self.assertTrue(not output.count('Joi'))
 
   def testLimitGrd(self):
     limit_file = StringIO('''<?xml version="1.0" encoding="UTF-8"?>
@@ -80,13 +79,13 @@
         </release>
       </grit>''')
     tool = xmb.OutputXmb()
-    class DummyOpts(object):
+    class DummyOpts:
       extra_verbose = False
     tool.o = DummyOpts()
     tool.Process(self.res_tree, self.xmb_file, limit_file, True, dir='.')
     output = self.xmb_file.getvalue().decode('utf-8')
-    self.failUnless(output.count('Joi'))
-    self.failUnless(not output.count('Yibbee'))
+    self.assertTrue(output.count('Joi'))
+    self.assertTrue(not output.count('Yibbee'))
 
   def testSubstitution(self):
     self.res_tree.SetOutputLanguage('en')
@@ -94,7 +93,7 @@
     self.res_tree.RunGatherers()
     xmb.OutputXmb().Process(self.res_tree, self.xmb_file)
     output = self.xmb_file.getvalue().decode('utf-8')
-    self.failUnless(output.count(
+    self.assertTrue(output.count(
         '<ph name="GOOD_1"><ex>excellent</ex>[GOOD]</ph>'))
 
   def testLeadingTrailingWhitespace(self):
@@ -106,7 +105,7 @@
     self.res_tree.RunGatherers()
     xmb.OutputXmb().Process(self.res_tree, self.xmb_file)
     output = self.xmb_file.getvalue().decode('utf-8')
-    self.failUnless(output.count('OK ? </msg>'))
+    self.assertTrue(output.count('OK ? </msg>'))
 
   def testDisallowedChars(self):
     # Validate that the invalid unicode is not accepted. Since it's not valid,
diff --git a/tools/grit/grit/util.py b/tools/grit/grit/util.py
index a32e09a..bb722c2 100644
--- a/tools/grit/grit/util.py
+++ b/tools/grit/grit/util.py
@@ -207,7 +207,7 @@
   else:
     mode = 'r'
 
-  with io.open(filename, mode, encoding=encoding) as f:
+  with open(filename, mode, encoding=encoding) as f:
     return f.read()
 
 
@@ -527,7 +527,7 @@
   return (name, val)
 
 
-class Substituter(object):
+class Substituter:
   '''Finds and substitutes variable names in text strings.
 
   Given a dictionary of variable names and values, prepares to
@@ -644,7 +644,7 @@
       return msg
 
 
-class TempDir(object):
+class TempDir:
   '''Creates files with the specified contents in a temporary directory,
   for unit testing.
   '''
@@ -677,7 +677,7 @@
   def AsCurrentDir(self):
     return self._AsCurrentDirClass(self.GetPath())
 
-  class _AsCurrentDirClass(object):
+  class _AsCurrentDirClass:
     def __init__(self, path):
       self.path = path
     def __enter__(self):
diff --git a/tools/grit/grit/util_unittest.py b/tools/grit/grit/util_unittest.py
index a553251..342d7c2 100755
--- a/tools/grit/grit/util_unittest.py
+++ b/tools/grit/grit/util_unittest.py
@@ -6,7 +6,6 @@
 '''Unit test that checks some of util functions.
 '''
 
-from __future__ import print_function
 
 import os
 import sys
@@ -29,35 +28,35 @@
     # Should fail, it is not supported by the function now (as documented)
     cls = util.NewClassInstance('grit.util.TestClassToLoad',
                                 TestBaseClassToLoad)
-    self.failUnless(cls == None)
+    self.assertTrue(cls == None)
 
     # Test non existent class name
     cls = util.NewClassInstance('grit.util_unittest.NotExistingClass',
                                 TestBaseClassToLoad)
-    self.failUnless(cls == None)
+    self.assertTrue(cls == None)
 
     # Test valid class name and valid base class
     cls = util.NewClassInstance('grit.util_unittest.TestClassToLoad',
                                 TestBaseClassToLoad)
-    self.failUnless(isinstance(cls, TestBaseClassToLoad))
+    self.assertTrue(isinstance(cls, TestBaseClassToLoad))
 
     # Test valid class name with wrong hierarchy
     cls = util.NewClassInstance('grit.util_unittest.TestClassNoBase',
                                 TestBaseClassToLoad)
-    self.failUnless(cls == None)
+    self.assertTrue(cls == None)
 
   def testCanonicalLanguage(self):
-    self.failUnless(util.CanonicalLanguage('en') == 'en')
-    self.failUnless(util.CanonicalLanguage('pt_br') == 'pt-BR')
-    self.failUnless(util.CanonicalLanguage('pt-br') == 'pt-BR')
-    self.failUnless(util.CanonicalLanguage('pt-BR') == 'pt-BR')
-    self.failUnless(util.CanonicalLanguage('pt/br') == 'pt-BR')
-    self.failUnless(util.CanonicalLanguage('pt/BR') == 'pt-BR')
-    self.failUnless(util.CanonicalLanguage('no_no_bokmal') == 'no-NO-BOKMAL')
+    self.assertTrue(util.CanonicalLanguage('en') == 'en')
+    self.assertTrue(util.CanonicalLanguage('pt_br') == 'pt-BR')
+    self.assertTrue(util.CanonicalLanguage('pt-br') == 'pt-BR')
+    self.assertTrue(util.CanonicalLanguage('pt-BR') == 'pt-BR')
+    self.assertTrue(util.CanonicalLanguage('pt/br') == 'pt-BR')
+    self.assertTrue(util.CanonicalLanguage('pt/BR') == 'pt-BR')
+    self.assertTrue(util.CanonicalLanguage('no_no_bokmal') == 'no-NO-BOKMAL')
 
   def testUnescapeHtml(self):
-    self.failUnless(util.UnescapeHtml('&#1010;') == six.unichr(1010))
-    self.failUnless(util.UnescapeHtml('&#xABcd;') == six.unichr(43981))
+    self.assertTrue(util.UnescapeHtml('&#1010;') == chr(1010))
+    self.assertTrue(util.UnescapeHtml('&#xABcd;') == chr(43981))
 
   def testRelativePath(self):
     """ Verify that MakeRelativePath works in some tricky cases."""
@@ -69,7 +68,7 @@
       for path1 in [base_path, base_path + os.path.sep]:
         for path2 in [other_path, other_path + os.path.sep]:
           result = util.MakeRelativePath(path1, path2)
-          self.failUnless(result == expected_result)
+          self.assertTrue(result == expected_result)
 
     # set-up variables
     root_dir = 'c:%sa' % os.path.sep
@@ -104,13 +103,13 @@
         self.assertRaises(UnicodeDecodeError, Test, b'\x80', 'utf-8', None)
 
 
-class TestBaseClassToLoad(object):
+class TestBaseClassToLoad:
   pass
 
 class TestClassToLoad(TestBaseClassToLoad):
   pass
 
-class TestClassNoBase(object):
+class TestClassNoBase:
   pass
 
 
diff --git a/tools/grit/grit/xtb_reader.py b/tools/grit/grit/xtb_reader.py
index 3a3c0a3..2944e70 100644
--- a/tools/grit/grit/xtb_reader.py
+++ b/tools/grit/grit/xtb_reader.py
@@ -5,7 +5,6 @@
 '''Fast and efficient parser for XTB files.
 '''
 
-from __future__ import print_function
 
 import sys
 import xml.sax
diff --git a/tools/grit/grit/xtb_reader_unittest.py b/tools/grit/grit/xtb_reader_unittest.py
index bcd3619cc..b370a11 100755
--- a/tools/grit/grit/xtb_reader_unittest.py
+++ b/tools/grit/grit/xtb_reader_unittest.py
@@ -5,7 +5,6 @@
 
 '''Unit tests for grit.xtb_reader'''
 
-from __future__ import print_function
 
 import io
 import os
@@ -40,10 +39,10 @@
     def Callback(id, structure):
       messages.append((id, structure))
     xtb_reader.Parse(xtb_file, Callback)
-    self.failUnless(len(messages[0][1]) == 1)
-    self.failUnless(messages[3][1][0])  # PROBLEM_REPORT placeholder
-    self.failUnless(messages[4][0] == '7729135689895381486')
-    self.failUnless(messages[4][1][7][1] == 'and another after a blank line.')
+    self.assertTrue(len(messages[0][1]) == 1)
+    self.assertTrue(messages[3][1][0])  # PROBLEM_REPORT placeholder
+    self.assertTrue(messages[4][0] == '7729135689895381486')
+    self.assertTrue(messages[4][1][7][1] == 'and another after a blank line.')
 
   def testParsingIntoMessages(self):
     root = util.ParseGrdForUnittest('''
@@ -71,7 +70,7 @@
                      msgs.UberClique().GenerateXtbParserCallback('is'))
     self.assertEqual('Meirihattar!',
                      clique_mega.MessageForLanguage('is').GetRealContent())
-    self.failUnless('Saelir %s',
+    self.assertTrue('Saelir %s',
                     clique_hello_user.MessageForLanguage('is').GetRealContent())
 
   def testIfNodesWithUseNameForId(self):
diff --git a/tools/grit/grit_info.py b/tools/grit/grit_info.py
index 9f8c519..d59d6a6 100755
--- a/tools/grit/grit_info.py
+++ b/tools/grit/grit_info.py
@@ -6,7 +6,6 @@
 '''Tool to determine inputs and outputs of a grit file.
 '''
 
-from __future__ import print_function
 
 import optparse
 import os
@@ -22,7 +21,7 @@
 
 def Outputs(filename, defines, ids_file, target_platform=None):
   grd = grd_reader.Parse(
-      filename, defines=defines, tags_to_ignore=set(['messages']),
+      filename, defines=defines, tags_to_ignore={'messages'},
       first_ids_file=ids_file, target_platform=target_platform)
 
   target = []
@@ -52,7 +51,7 @@
 
 def Inputs(filename, defines, ids_file, target_platform=None):
   grd = grd_reader.Parse(
-      filename, debug=False, defines=defines, tags_to_ignore=set(['message']),
+      filename, debug=False, defines=defines, tags_to_ignore={'message'},
       first_ids_file=ids_file, target_platform=target_platform)
   files = set()
   for lang, ctx, fallback in grd.GetConfigurations():
diff --git a/tools/grit/minify_with_uglify.py b/tools/grit/minify_with_uglify.py
index 7fe2a74..69d4e0f 100755
--- a/tools/grit/minify_with_uglify.py
+++ b/tools/grit/minify_with_uglify.py
@@ -3,7 +3,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from __future__ import print_function
 
 import os
 import sys
diff --git a/tools/grit/minify_with_uglify_unittest.py b/tools/grit/minify_with_uglify_unittest.py
index 57cc3c9..8bbf65f 100755
--- a/tools/grit/minify_with_uglify_unittest.py
+++ b/tools/grit/minify_with_uglify_unittest.py
@@ -14,7 +14,7 @@
             var foo = 0;
         """
     minimized = minify_with_uglify.Minify(source)
-    self.assertEquals(minimized, "var foo=0;")
+    self.assertEqual(minimized, "var foo=0;")
 
   def test_complex(self):
     source = """
@@ -26,5 +26,5 @@
             var qux = foo.bar + foo.baz;
         """
     minimized = minify_with_uglify.Minify(source)
-    self.assertEquals(minimized,
+    self.assertEqual(minimized,
                       "var foo={bar:0,baz:5};var qux=foo.bar+foo.baz;")
diff --git a/tools/grit/minimize_css.py b/tools/grit/minimize_css.py
index 7023c0f..fa78d116 100755
--- a/tools/grit/minimize_css.py
+++ b/tools/grit/minimize_css.py
@@ -6,7 +6,7 @@
 import re
 import sys
 
-class CSSMinimizer(object):
+class CSSMinimizer:
 
   INITIAL = 0
   MAYBE_COMMENT_START = 1
diff --git a/tools/grit/minimize_css_unittest.py b/tools/grit/minimize_css_unittest.py
index a295a778..de5fc5c 100755
--- a/tools/grit/minimize_css_unittest.py
+++ b/tools/grit/minimize_css_unittest.py
@@ -17,7 +17,7 @@
             }
         """
     minimized = minimize_css.CSSMinimizer.minimize_css(source)
-    self.assertEquals(minimized, "div{color: blue}")
+    self.assertEqual(minimized, "div{color: blue}")
 
   def test_attribute_selectors(self):
     source = """
@@ -26,7 +26,7 @@
             }
         """
     minimized = minimize_css.CSSMinimizer.minimize_css(source)
-    self.assertEquals(
+    self.assertEqual(
         minimized,
         # pylint: disable=line-too-long
         """input[type="search" i]::-webkit-textfield-decoration-container{direction: ltr}""")
@@ -41,18 +41,18 @@
         /* footer */
         """
     minimized = minimize_css.CSSMinimizer.minimize_css(source)
-    self.assertEquals(minimized, "html{ display: block}")
+    self.assertEqual(minimized, "html{ display: block}")
 
   def test_no_strip_inside_quotes(self):
     source = """div[foo=' bar ']"""
     minimized = minimize_css.CSSMinimizer.minimize_css(source)
-    self.assertEquals(minimized, source)
+    self.assertEqual(minimized, source)
 
     source = """div[foo=" bar "]"""
     minimized = minimize_css.CSSMinimizer.minimize_css(source)
-    self.assertEquals(minimized, source)
+    self.assertEqual(minimized, source)
 
   def test_escape_string(self):
     source = """content: " <a onclick=\\\"javascript:  alert  ( 'foobar' ); \\\">";"""
     minimized = minimize_css.CSSMinimizer.minimize_css(source)
-    self.assertEquals(minimized, source)
+    self.assertEqual(minimized, source)
diff --git a/tools/grit/pak_util.py b/tools/grit/pak_util.py
index 28de620..4a0b5dc 100755
--- a/tools/grit/pak_util.py
+++ b/tools/grit/pak_util.py
@@ -9,7 +9,6 @@
 https://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings
 """
 
-from __future__ import print_function
 
 import argparse
 import gzip
@@ -142,7 +141,7 @@
     desc = '<data>'
     if try_decode:
       try:
-        desc = six.text_type(data, encoding)
+        desc = str(data, encoding)
         if len(desc) > 60:
           desc = desc[:60] + '...'
         desc = desc.replace('\n', '\\n')
diff --git a/tools/grit/preprocess_if_expr.py b/tools/grit/preprocess_if_expr.py
index 821ca27..35c1b20b 100644
--- a/tools/grit/preprocess_if_expr.py
+++ b/tools/grit/preprocess_if_expr.py
@@ -19,7 +19,7 @@
 
 class PreprocessIfExprNode(grit.node.base.Node):
   def __init__(self):
-    super(PreprocessIfExprNode, self).__init__()
+    super().__init__()
 
   def PreprocessIfExpr(self, content, removal_comments_extension):
     return grit.format.html_inline.CheckConditionalElements(
@@ -94,7 +94,7 @@
   for input_file in args.in_files:
     in_path = os.path.join(in_folder, input_file)
     content = ""
-    with io.open(in_path, encoding='utf-8', mode='r') as f:
+    with open(in_path, encoding='utf-8') as f:
       content = f.read()
 
     removal_comments_extension = None  # None means no removal comments
@@ -135,14 +135,14 @@
       if os.path.exists(to_check):
         os.remove(to_check)
 
-    with io.open(out_path, mode='wb') as f:
+    with open(out_path, mode='wb') as f:
       f.write(preprocessed.encode('utf-8'))
 
   if args.out_manifest:
     manifest_data = {}
     manifest_data['base_dir'] = '%s' % args.out_folder
     manifest_data['files'] = args.in_files
-    manifest_file = io.open(
+    manifest_file = open(
         os.path.normpath(os.path.join(_CWD, args.out_manifest)), 'w',
         encoding='utf-8', newline='\n')
     json.dump(manifest_data, manifest_file)
diff --git a/tools/grit/preprocess_if_expr_test.py b/tools/grit/preprocess_if_expr_test.py
index b39c0b2..446254711 100644
--- a/tools/grit/preprocess_if_expr_test.py
+++ b/tools/grit/preprocess_if_expr_test.py
@@ -22,7 +22,7 @@
 
   def _read_out_file(self, file_name):
     assert self._out_folder
-    with open(os.path.join(self._out_folder, file_name), 'r') as f:
+    with open(os.path.join(self._out_folder, file_name)) as f:
       return f.read()
 
   def _run_test(self, additional_options, file_name, expected_file_name):
@@ -37,8 +37,7 @@
         file_name,
     ] + additional_options)
     actual = self._read_out_file(file_name)
-    with open(os.path.join(_HERE_DIR, 'preprocess_tests', expected_file_name),
-              'r') as f:
+    with open(os.path.join(_HERE_DIR, 'preprocess_tests', expected_file_name)) as f:
       expected = f.read()
       self.assertMultiLineEqual(expected, actual)
 
diff --git a/tools/grit/setup.py b/tools/grit/setup.py
index 6886db7..cc2d80a 100755
--- a/tools/grit/setup.py
+++ b/tools/grit/setup.py
@@ -5,7 +5,6 @@
 
 """Install the package!"""
 
-from __future__ import absolute_import
 
 import setuptools
 
diff --git a/tools/grit/stamp_grit_sources.py b/tools/grit/stamp_grit_sources.py
index 0629e8e..35dbded 100644
--- a/tools/grit/stamp_grit_sources.py
+++ b/tools/grit/stamp_grit_sources.py
@@ -12,7 +12,6 @@
 # Usage:
 #    stamp_grit_sources.py <directory> <stamp-file> <.d-file>
 
-from __future__ import print_function
 
 import os
 import sys
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index be40c41..1d9c899 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -442,6 +442,7 @@
       'linux-upload-perfetto': 'release_bot_perfetto_zlib_reclient',
       'linux-wpt-content-shell-asan-fyi-rel': 'asan_lsan_release_trybot_reclient',
       'linux-wpt-content-shell-fyi-rel': 'release_trybot_minimal_symbols_reclient',
+      'linux-wpt-content-shell-leak-detection': 'release_trybot_minimal_symbols_reclient',
       'linux-wpt-fyi-rel': 'release_trybot_minimal_symbols_reclient',
       'linux-wpt-identity-fyi-rel': 'release_bot_minimal_symbols_reclient',
       'linux-wpt-input-fyi-rel': 'release_bot_minimal_symbols_reclient',
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index 410a2e4..cea65515 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -1290,6 +1290,15 @@
       "use_remoteexec": true
     }
   },
+  "linux-wpt-content-shell-leak-detection": {
+    "gn_args": {
+      "dcheck_always_on": true,
+      "is_component_build": false,
+      "is_debug": false,
+      "symbol_level": 1,
+      "use_remoteexec": true
+    }
+  },
   "linux-wpt-fyi-rel": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4b1f210..3145032 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -32410,6 +32410,7 @@
   <int value="1053" label="KerberosUseCustomPrefilledConfig"/>
   <int value="1054" label="DeviceHindiInscriptLayoutEnabled"/>
   <int value="1055" label="DeviceLoginScreenExtensionManifestV2Availability"/>
+  <int value="1056" label="KioskTroubleshootingToolsEnabled"/>
 </enum>
 
 <enum name="EnterprisePoliciesSources">
@@ -51795,6 +51796,42 @@
   <int value="9" label="Canceled"/>
 </enum>
 
+<enum name="InnerHTMLFastPathParserParseResult">
+  <int value="0" label="Content was successfully parsed"/>
+  <int value="1" label="Could not use fast path because tracing is enabled"/>
+  <int value="2"
+      label="Could not use fast path because of ParserContentPolicy"/>
+  <int value="3" label="Could not use fast path because in form"/>
+  <int value="4"
+      label="Failed because context element tag type is unsupported"/>
+  <int value="5"
+      label="Failed because a child element was encountered in an option"/>
+  <int value="6" label="Failed because end of input was not reached"/>
+  <int value="7" label="Failed because a null character was encountered"/>
+  <int value="8" label="Failed parsing the tag name"/>
+  <int value="9" label="Failed parsing a quoted attributed value"/>
+  <int value="10" label="Failed parsing an unquoted attribute value"/>
+  <int value="11" label="Failed parsing a quoted escaped attribute value"/>
+  <int value="12" label="Failed parsing an unquoted escaped attribute value"/>
+  <int value="13" label="Failed parsing an character reference"/>
+  <int value="14" label="Filed because end of input reached prematurely"/>
+  <int value="15" label="Failed parsing attributes"/>
+  <int value="16" label="Failed parsing in ParseSpecificElements()"/>
+  <int value="17" label="Failed parsing the element name"/>
+  <int value="18" label="Failed because an unsupported tag was encountered"/>
+  <int value="19"
+      label="Failed because end of input reached prematurely for a container
+             element"/>
+  <int value="20"
+      label="Failed because '&gt;' was not matched for end tag name"/>
+  <int value="21"
+      label="Failed because end tag name did not not muatch start start name"/>
+  <int value="22" label="Could not use fast path because of shadow roots"/>
+  <int value="23"
+      label="Could not use fast path because Document::IsDirAttributeDirty()
+             is true"/>
+</enum>
+
 <enum name="InProductHelpDismissalReason">
   <int value="0" label="Unknown"/>
   <int value="1" label="Timed Out"/>
@@ -62138,6 +62175,7 @@
   <int value="665409384"
       label="AutofillToolkitViewsCreditCardDialogsMac:enabled"/>
   <int value="665647051" label="DiscountConsentV2:enabled"/>
+  <int value="666428855" label="MediaRemotingWithoutFullscreen:enabled"/>
   <int value="666871156" label="KaleidoscopeModule:enabled"/>
   <int value="667643314" label="LitePageServerPreviews:enabled"/>
   <int value="669097106" label="NtpRealboxMatchOmniboxTheme:disabled"/>
@@ -63901,6 +63939,7 @@
   <int value="1699180023" label="PaymentRequestOptionalTotal:disabled"/>
   <int value="1699182601" label="DockedMagnifier:disabled"/>
   <int value="1699290689" label="CssSelectorFragmentAnchor:enabled"/>
+  <int value="1699499166" label="MediaRemotingWithoutFullscreen:disabled"/>
   <int value="1700117535" label="MediaFeedsBackgroundFetching:disabled"/>
   <int value="1700181145" label="PowerBookmarksSidePanel:enabled"/>
   <int value="1700394127" label="OverlayScrollbar:disabled"/>
@@ -69077,6 +69116,7 @@
   <int value="2" label="Frame size button long touch"/>
   <int value="3" label="Gesture fling"/>
   <int value="4" label="Gesture scroll"/>
+  <int value="5" label="Accelerator"/>
 </enum>
 
 <enum name="MultitouchEvent">
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 5ed191c1..bc68c1d 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -141,7 +141,7 @@
 </histogram>
 
 <histogram name="Ash.Accelerators.WindowSnap"
-    enum="WindowSnapAcceleratorAction" expires_after="2023-02-19">
+    enum="WindowSnapAcceleratorAction" expires_after="2023-11-20">
   <owner>amusbach@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
   <summary>Captures usage of Alt+[ and Alt+].</summary>
@@ -5070,7 +5070,7 @@
 </histogram>
 
 <histogram name="Ash.TabDrag.PresentationTime.MaxLatency" units="ms"
-    expires_after="2023-02-22">
+    expires_after="2024-01-12">
 <!-- Name completed by histogram_suffixes
      name="TabletOrClamshellMode" -->
 
@@ -5653,7 +5653,7 @@
 </histogram>
 
 <histogram name="Ash.Window.PartialSplitDuration" units="minutes"
-    expires_after="2023-06-25">
+    expires_after="2023-11-20">
   <owner>nupurjain@chromium.org</owner>
   <owner>sophiewen@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 1c58a6b..e32f3b9 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -1352,6 +1352,79 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.HTMLFastPathParser.AbortedParseSize" units="bytes"
+    expires_after="2023-12-07">
+  <owner>sky@chromium.org</owner>
+  <owner>tebbi@chromium.org</owner>
+  <owner>dom-dev@chromium.org</owner>
+  <summary>
+    Number of bytes after which the html fast-path parser aborted. This does not
+    count the bytes the fast-path parser did not look at. We measure this since
+    aborting the fast-path and falling back to the normal parser is wasted
+    effort. This is recorded after every invocation of Element::setInnerHTML.
+  </summary>
+</histogram>
+
+<histogram name="Blink.HTMLFastPathParser.AbortedParseTime" units="ms"
+    expires_after="2023-12-07">
+  <owner>sky@chromium.org</owner>
+  <owner>tebbi@chromium.org</owner>
+  <owner>dom-dev@chromium.org</owner>
+  <summary>
+    The amount of time the innerHTML fast-path parser took before reaching input
+    it could not handle. This is recorded when Element::setInnerHTML is called,
+    and at the moment when the input it can't handle is reached.
+  </summary>
+</histogram>
+
+<histogram name="Blink.HTMLFastPathParser.ParseResult"
+    enum="InnerHTMLFastPathParserParseResult" expires_after="2023-12-07">
+  <owner>sky@chromium.org</owner>
+  <owner>tebbi@chromium.org</owner>
+  <owner>dom-dev@chromium.org</owner>
+  <summary>
+    Captures the result of the innerHTML fast-path parser. This is recorded
+    every time Element::setInnerHTML is called and only if the innerHTML
+    fast-path parser is enabled.
+  </summary>
+</histogram>
+
+<histogram name="Blink.HTMLFastPathParser.SuccessfulParseSize" units="bytes"
+    expires_after="2023-12-07">
+  <owner>sky@chromium.org</owner>
+  <owner>tebbi@chromium.org</owner>
+  <owner>dom-dev@chromium.org</owner>
+  <summary>
+    Number of bytes successfully parsed by the innerHTML fast-path parser. This
+    is recorded if the innerHTML fast-path parser completes successfully and is
+    logged at the end of parsing the content supplied to Element::setInnerHTML.
+  </summary>
+</histogram>
+
+<histogram name="Blink.HTMLFastPathParser.SuccessfulParseTime" units="ms"
+    expires_after="2023-12-07">
+  <owner>sky@chromium.org</owner>
+  <owner>tebbi@chromium.org</owner>
+  <owner>dom-dev@chromium.org</owner>
+  <summary>
+    The time it takes for the innerHTML fast-path parser to complete processing
+    text from Element::setInnerHTML. This is recorded if fast-path innerHTML is
+    enabled, and completes successfully.
+  </summary>
+</histogram>
+
+<histogram name="Blink.HTMLFastPathParser.TotalParseTime" units="ms"
+    expires_after="2023-12-07">
+  <owner>sky@chromium.org</owner>
+  <owner>tebbi@chromium.org</owner>
+  <owner>dom-dev@chromium.org</owner>
+  <summary>
+    The total time it takes to complete parsing of Element::setInnerHTML. This
+    includes the time taken by the fast-path parser (even if it failed). This is
+    recorded after every invocation of Element::setInnerHTML.
+  </summary>
+</histogram>
+
 <histogram name="Blink.HTMLParsing.ChunkCount4" units="chunks"
     expires_after="2023-06-30">
   <owner>masonf@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/chrome/histograms.xml b/tools/metrics/histograms/metadata/chrome/histograms.xml
index aa7db50e..72664a27 100644
--- a/tools/metrics/histograms/metadata/chrome/histograms.xml
+++ b/tools/metrics/histograms/metadata/chrome/histograms.xml
@@ -281,7 +281,7 @@
 </histogram>
 
 <histogram name="Chrome.Tabs.AnimationSmoothness.TabLoading" units="%"
-    expires_after="2022-04-24">
+    expires_after="2024-01-12">
   <owner>yichenz@chromium.org</owner>
   <owner>chromeos-wmp@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/commerce/histograms.xml b/tools/metrics/histograms/metadata/commerce/histograms.xml
index 7b1b272..5e18b5c 100644
--- a/tools/metrics/histograms/metadata/commerce/histograms.xml
+++ b/tools/metrics/histograms/metadata/commerce/histograms.xml
@@ -524,6 +524,9 @@
 
 <histogram name="Commerce.SignIn.AccountWaaStatus"
     enum="AccountWaaStatusForCommerce" expires_after="2023-06-25">
+  <obsolete>
+    Deprecated in 01/2023. It's no longer needed.
+  </obsolete>
   <owner>zhiyuancai@chromium.org</owner>
   <owner>ayman@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
@@ -572,6 +575,10 @@
 
 <histogram name="Commerce.Subscriptions.{ManagementType}.Count"
     units="subscriptions" expires_after="2023-04-25">
+  <obsolete>
+    Deprecated in 01/2023. Replaced by
+    Commerce.PriceTracking.PriceTrackedProductCount.
+  </obsolete>
   <owner>zhiyuancai@chromium.org</owner>
   <owner>ayman@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
index 73f546f0..4b77a11 100644
--- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -1603,20 +1603,6 @@
   <affected-histogram name="DevTools.Launch"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="DialogTypes" separator="." ordering="prefix">
-  <suffix name="BubbleDialogDelegateView"
-      label="Counts dialog boxes created using BubbleDialogDelegateView.
-             These are a subset of those created with DialogDelegateView
-             (which, in turn, are a subset of those created with
-             DialogDelegate)."/>
-  <suffix name="DialogDelegate"
-      label="Counts dialog boxes created using DialogDelegate."/>
-  <suffix name="DialogDelegateView"
-      label="Counts dialog boxes created using DialogDelegateView. These are
-             a subset of those created with DialogDelegate."/>
-  <affected-histogram name="Dialog.Create"/>
-</histogram_suffixes>
-
 <histogram_suffixes name="DiskCacheExperiment1" separator="_">
   <suffix name="1" label="Experiment group 1 (70% clients)"/>
   <suffix name="2" label="Experiment group 2 (10% clients)"/>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 8980f7c..9c47b84 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -114,7 +114,7 @@
 </histogram>
 
 <histogram name="History.ClearBrowsingData.Duration.ChromeTask.{Task}"
-    units="ms" expires_after="2023-03-01">
+    units="ms" expires_after="2023-07-01">
   <owner>dullweber@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -246,7 +246,7 @@
 </histogram>
 
 <histogram name="History.ClearBrowsingData.Duration.Task.{Task}" units="ms"
-    expires_after="2023-03-01">
+    expires_after="2023-07-01">
   <owner>dullweber@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index af79f91..de0cb6f 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -2265,6 +2265,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.EME.MediaLicenseDatabaseOpenSQLiteError"
+    enum="SqliteLoggedResultCode" expires_after="2023-12-24">
+  <owner>vpasupathy@chromium.org</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    SQLite specific errors reported while attempting to unsuccessfully open the
+    Media License Database.
+  </summary>
+</histogram>
+
 <histogram name="Media.EME.MediaLicenseStorageHostOpenError"
     enum="MediaLicenseStorageHostOpenError" expires_after="2023-12-24">
   <owner>vpasupathy@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 1a08acc..fdea114 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -4803,32 +4803,6 @@
   </summary>
 </histogram>
 
-<histogram name="Dialog.Create" enum="BooleanCreated" expires_after="M85">
-  <owner>pdyson@chromium.org</owner>
-  <summary>
-    Counts the number times dialog boxes are created using a particular parent
-    class.
-  </summary>
-</histogram>
-
-<histogram name="Dialog.Creation" enum="DialogName" expires_after="M85">
-  <owner>pdyson@chromium.org</owner>
-  <summary>
-    Counts the number times various types of dialog boxes are created.
-  </summary>
-</histogram>
-
-<histogram name="Dialog.DialogDelegate.Duration" units="ms" expires_after="M77">
-  <owner>pdyson@chromium.org</owner>
-  <summary>
-    How long dialog boxes are open, for dialog boxes created using the parent
-    class DialogDelegate. This will include bubbles that use
-    BubbleDialogDelegateView as a parent class, and dialogs that use
-    DialogDelegateView as a parent class. This is logged on delegate
-    destruction.
-  </summary>
-</histogram>
-
 <histogram name="DirectSockets.PermissionDeniedFailures"
     enum="DirectSocketPermissionFailureType" expires_after="2023-09-13">
   <owner>ericwilligers@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index c09b790..8ece1126 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -1536,7 +1536,7 @@
 </histogram>
 
 <histogram name="PasswordManager.FillSuggestionsIncludeAndroidAppCredentials"
-    enum="PasswordManagerOfferedAndroidCredentials" expires_after="2023-03-01">
+    enum="PasswordManagerOfferedAndroidCredentials" expires_after="2023-09-01">
   <owner>kazinova@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2987,7 +2987,7 @@
 </histogram>
 
 <histogram name="PasswordManager.RequirementsSpecFetcher.Result"
-    enum="PasswordRequirementsFetcherResult" expires_after="2023-02-12">
+    enum="PasswordRequirementsFetcherResult" expires_after="2023-08-12">
   <owner>kazinova@google.com</owner>
   <owner>battre@chromium.org</owner>
   <summary>
@@ -3617,7 +3617,7 @@
 </histogram>
 
 <histogram name="PasswordManager.WeakCheck.Time" units="ms"
-    expires_after="2023-03-01">
+    expires_after="2023-09-01">
   <owner>kazinova@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>The time it took to complete the passwords weak check.</summary>
diff --git a/tools/metrics/histograms/metadata/service/histograms.xml b/tools/metrics/histograms/metadata/service/histograms.xml
index f58b9df2..909c2b6 100644
--- a/tools/metrics/histograms/metadata/service/histograms.xml
+++ b/tools/metrics/histograms/metadata/service/histograms.xml
@@ -454,8 +454,8 @@
 </histogram>
 
 <histogram name="ServiceWorker.InstallEvent.All.FetchCount" units="fetches"
-    expires_after="2023-06-11">
-  <owner>wanderview@chromium.org</owner>
+    expires_after="2023-07-17">
+  <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
     The number of network fetches performed by the install event handler.
@@ -463,8 +463,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.InstallEvent.{type}.Status"
-    enum="ServiceWorkerStatusCode" expires_after="2023-01-26">
-  <owner>wanderview@chromium.org</owner>
+    enum="ServiceWorkerStatusCode" expires_after="2023-07-17">
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -479,8 +478,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.InstallEvent.{type}.Time" units="ms"
-    expires_after="2023-01-26">
-  <owner>wanderview@chromium.org</owner>
+    expires_after="2023-07-17">
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/settings/histograms.xml b/tools/metrics/histograms/metadata/settings/histograms.xml
index 982dd1ca..f44302e 100644
--- a/tools/metrics/histograms/metadata/settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/settings/histograms.xml
@@ -327,6 +327,18 @@
   </summary>
 </histogram>
 
+<histogram name="Settings.PrivacySandbox.AdMeasurement.Enabled"
+    enum="BooleanEnabled" expires_after="M115">
+  <owner>sauski@google.com</owner>
+  <owner>olesiamarukhno@chromium.org</owner>
+  <owner>msramek@chromium.org</owner>
+  <owner>kartoffel-core-eng@google.com</owner>
+  <summary>
+    Records the startup state of the ad measurement API for Privacy Sandbox GA
+    M1. This is recorded during the profile start up.
+  </summary>
+</histogram>
+
 <histogram name="Settings.PrivacySandbox.DeprecatedRedirect"
     enum="BooleanRedirected" expires_after="M115">
   <owner>olesiamarukhno@google.com</owner>
@@ -376,6 +388,18 @@
   </summary>
 </histogram>
 
+<histogram name="Settings.PrivacySandbox.Fledge.Enabled" enum="BooleanEnabled"
+    expires_after="M115">
+  <owner>sauski@google.com</owner>
+  <owner>olesiamarukhno@chromium.org</owner>
+  <owner>msramek@chromium.org</owner>
+  <owner>kartoffel-core-eng@google.com</owner>
+  <summary>
+    Records the startup state of the fledge API for Privacy Sandbox GA M1. This
+    is recorded during the profile start up.
+  </summary>
+</histogram>
+
 <histogram name="Settings.PrivacySandbox.InvalidTopicIdLocalized" units="units"
     expires_after="M118">
   <owner>sauski@google.com</owner>
@@ -427,6 +451,18 @@
   </summary>
 </histogram>
 
+<histogram name="Settings.PrivacySandbox.Topics.Enabled" enum="BooleanEnabled"
+    expires_after="M115">
+  <owner>sauski@google.com</owner>
+  <owner>olesiamarukhno@chromium.org</owner>
+  <owner>msramek@chromium.org</owner>
+  <owner>kartoffel-core-eng@google.com</owner>
+  <summary>
+    Records the startup state of the topics API for Privacy Sandbox GA M1. This
+    is recorded during the profile start up.
+  </summary>
+</histogram>
+
 <histogram name="Settings.SafetyCheck.ChromeCleanerResult"
     enum="SafetyCheckChromeCleanerStatus" expires_after="2023-07-09">
   <owner>rainhard@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index 0cc7c4c..67d3163 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -59,6 +59,10 @@
 <variants name="TabDiscardReason">
   <variant name=".External"
       summary="from outside of TabManager (e.g. by an extension)"/>
+  <variant name=".Proactive"
+      summary="proactively by Performance Manager before the system is in a
+               critical condition, such as when Memory Saver Mode discards
+               the tab"/>
   <variant name=".Urgent"
       summary="urgently by TabManager when the system is in a critical
                condition"/>
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index 9923da2c..7ef50a15 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -249,7 +249,7 @@
 </histogram>
 
 <histogram name="Webapp.CheckServiceWorker.Status"
-    enum="ServiceWorkerOfflineCapability" expires_after="2023-02-26">
+    enum="ServiceWorkerOfflineCapability" expires_after="2023-07-17">
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 33bb138..3d3ac48 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,15 +6,15 @@
         },
         "win": {
             "hash": "371bc9104cbabd002f15fd4eda416278636c7ae7",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/e0dd93ef3709f247be7e3ab26a4d3d0aaf8d2898/trace_processor_shell.exe"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/62fdec216f3598aaf050e546fcc5766014adcc64/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "6373f26144aad58f230d11d6a91efda5a09c9873",
             "full_remote_path": "perfetto-luci-artifacts/v31.0/linux-arm/trace_processor_shell"
         },
         "mac": {
-            "hash": "259a2819e0d2457439874cb126d6ab23aec117c1",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/845a57f7e30034e2ed46dd66aa5c69b21dbe8a12/trace_processor_shell"
+            "hash": "73cdd9df6eb717b71e4992101d0ad14d27a8d41c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/62fdec216f3598aaf050e546fcc5766014adcc64/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "5f47ee79e59d00bf3889d30ca52315522c158040",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "da8b1f4b7d01bb8e5f06082c02b585f2460b147a",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/e0dd93ef3709f247be7e3ab26a4d3d0aaf8d2898/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/62fdec216f3598aaf050e546fcc5766014adcc64/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/traffic_annotation/auditor/chromeos/safe_list.txt b/tools/traffic_annotation/auditor/chromeos/safe_list.txt
index f9e6a2ab..6346731 100644
--- a/tools/traffic_annotation/auditor/chromeos/safe_list.txt
+++ b/tools/traffic_annotation/auditor/chromeos/safe_list.txt
@@ -3,12 +3,10 @@
 #
 all,chromeos/printing/printer_config_cache.cc
 all,chromeos/ash/components/geolocation/simple_geolocation_request.cc
-all,chrome/browser/ash/net/network_portal_detector_impl.cc
 all,chrome/browser/ash/policy/uploading/system_log_uploader.cc
 all,chromeos/geolocation/simple_geolocation_request.cc
 all,components/quirks/quirks_client.cc
 all,chromeos/ash/services/device_sync/cryptauth_client_impl.cc
-all,ash/quick_pair/repository/fast_pair/fast_pair_image_decoder.cc
 all,chrome/browser/ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc
 all,chrome/browser/ash/app_list/search/arc/recommend_apps_fetcher_impl.cc
 all,chrome/browser/ui/ash/projector/screencast_manager.cc
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 83966183..ba61cd5 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -418,4 +418,5 @@
  <item id="chromeos_emoji_picker_posts_fetcher" added_in_milestone="111" content_hash_code="06c44c7e" os_list="chromeos" file_path="chrome/browser/ui/webui/ash/emoji/gif_tenor_api_fetcher.cc" />
  <item id="webapk_minter_install_request" added_in_milestone="111" content_hash_code="042fed33" os_list="chromeos" file_path="chrome/browser/apps/app_service/webapk/webapk_install_task.cc" />
  <item id="deduplication_service" added_in_milestone="111" content_hash_code="06a77ea3" os_list="chromeos" file_path="chrome/browser/apps/app_deduplication_service/app_deduplication_server_connector.cc" />
+ <item id="network_portal_detector" added_in_milestone="111" content_hash_code="023cdc21" os_list="chromeos" file_path="chrome/browser/ash/net/network_portal_detector_impl.cc" />
 </annotations>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 3673445..b8886306 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -284,6 +284,7 @@
       <annotation id="chromeos_emoji_picker_posts_fetcher"/>
       <annotation id="deduplication_service"/>
       <annotation id="webapk_minter_install_request"/>
+      <annotation id="network_portal_detector"/>
     </sender>
   </group>
   <group name="Admin Features" hidden="true">
diff --git a/ui/base/ime/fuchsia/keyboard_client.cc b/ui/base/ime/fuchsia/keyboard_client.cc
index 7d59825..87eb9ff 100644
--- a/ui/base/ime/fuchsia/keyboard_client.cc
+++ b/ui/base/ime/fuchsia/keyboard_client.cc
@@ -117,6 +117,9 @@
   int event_flags = EventFlagsForCachedModifiers();
   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
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index cabd236e..4910594 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -159,12 +159,16 @@
   E_CPONLY(kColorButtonBackgroundProminent) \
   E_CPONLY(kColorButtonBackgroundProminentDisabled) \
   E_CPONLY(kColorButtonBackgroundProminentFocused) \
+  E_CPONLY(kColorButtonBackgroundTonal) \
+  E_CPONLY(kColorButtonBackgroundTonalDisabled) \
+  E_CPONLY(kColorButtonBackgroundTonalFocused) \
   E_CPONLY(kColorButtonBorder) \
   E_CPONLY(kColorButtonBorderDisabled) \
   E_CPONLY(kColorButtonForeground) \
   E_CPONLY(kColorButtonForegroundChecked) \
   E_CPONLY(kColorButtonForegroundDisabled) \
   E_CPONLY(kColorButtonForegroundProminent) \
+  E_CPONLY(kColorButtonForegroundTonal) \
   E_CPONLY(kColorButtonForegroundUnchecked) \
   E_CPONLY(kColorCustomFrameCaptionForeground) \
   E_CPONLY(kColorDebugBoundsOutline) \
diff --git a/ui/color/ui_color_mixer.cc b/ui/color/ui_color_mixer.cc
index 80394f2..45bea447 100644
--- a/ui/color/ui_color_mixer.cc
+++ b/ui/color/ui_color_mixer.cc
@@ -40,6 +40,12 @@
       kColorSubtleEmphasisBackground};
   mixer[kColorButtonBackgroundProminentFocused] = {
       kColorButtonBackgroundProminent};
+  // TODO(crbug.com/1406633): Finalize button colors.
+  mixer[kColorButtonBackgroundTonal] = {dark_mode
+                                            ? SkColorSetRGB(0x00, 0x4A, 0x77)
+                                            : SkColorSetRGB(0xC2, 0xE7, 0xFF)};
+  mixer[kColorButtonBackgroundTonalDisabled] = {kColorSubtleEmphasisBackground};
+  mixer[kColorButtonBackgroundTonalFocused] = {kColorButtonBackgroundTonal};
   mixer[kColorButtonBorder] = {kColorMidground};
   mixer[kColorButtonBorderDisabled] = {kColorSubtleEmphasisBackground};
   mixer[kColorButtonForeground] =
@@ -49,6 +55,10 @@
   mixer[kColorButtonForegroundDisabled] = {kColorDisabledForeground};
   mixer[kColorButtonForegroundProminent] =
       GetColorWithMaxContrast(kColorButtonBackgroundProminent);
+  // TODO(crbug.com/1406633): Finalize button colors.
+  mixer[kColorButtonForegroundTonal] = {dark_mode
+                                            ? SkColorSetRGB(0xC2, 0xE7, 0xFF)
+                                            : SkColorSetRGB(0x00, 0x1D, 0x35)};
   mixer[kColorButtonForegroundUnchecked] = {kColorSecondaryForeground};
   mixer[kColorCustomFrameCaptionForeground] = {SK_ColorWHITE};
   mixer[kColorDebugBoundsOutline] = SetAlpha(SK_ColorRED, 0x30);
diff --git a/ui/gtk/printing/print_dialog_gtk.cc b/ui/gtk/printing/print_dialog_gtk.cc
index d74246e3..afa936ce 100644
--- a/ui/gtk/printing/print_dialog_gtk.cc
+++ b/ui/gtk/printing/print_dialog_gtk.cc
@@ -287,10 +287,10 @@
 #if BUILDFLAG(USE_CUPS)
   // Set advanced settings first so they can be overridden by user applied
   // settings.
+  static constexpr char kSettingNamePrefix[] = "cups-";
   for (const auto& pair : settings->advanced_settings()) {
     if (!pair.second.is_string())
       continue;
-    static constexpr char kSettingNamePrefix[] = "cups-";
     const std::string setting_name = kSettingNamePrefix + pair.first;
     gtk_print_settings_set(gtk_settings_, setting_name.c_str(),
                            pair.second.GetString().c_str());
@@ -300,6 +300,7 @@
   std::string color_setting_name;
   printing::GetColorModelForModel(settings->color(), &color_setting_name,
                                   &color_value);
+  color_setting_name.insert(0, kSettingNamePrefix);
   gtk_print_settings_set(gtk_settings_, color_setting_name.c_str(),
                          color_value.c_str());
 
diff --git a/ui/ozone/platform/wayland/host/wayland_surface.cc b/ui/ozone/platform/wayland/host/wayland_surface.cc
index 5d95f544..204a01b 100644
--- a/ui/ozone/platform/wayland/host/wayland_surface.cc
+++ b/ui/ozone/platform/wayland/host/wayland_surface.cc
@@ -727,16 +727,14 @@
   apply_state_immediately_ = true;
 }
 
-void WaylandSurface::SetKeyboardShortcutsInhibition(bool enabled) {
-  if (!enabled) {
-    keyboard_shortcuts_inhibitor_.reset();
-    return;
-  }
-  if (auto* manager = connection_->keyboard_shortcuts_inhibit_manager_v1()) {
+void WaylandSurface::InhibitKeyboardShortcuts() {
+  if (auto* keyboard_shortcuts_inhibit_manager =
+          connection_->keyboard_shortcuts_inhibit_manager_v1()) {
     keyboard_shortcuts_inhibitor_ =
         wl::Object<zwp_keyboard_shortcuts_inhibitor_v1>(
             zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
-                manager, surface_.get(), connection_->seat()->wl_object()));
+                keyboard_shortcuts_inhibit_manager, surface_.get(),
+                connection_->seat()->wl_object()));
   }
 }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_surface.h b/ui/ozone/platform/wayland/host/wayland_surface.h
index 60888b4..0f00196 100644
--- a/ui/ozone/platform/wayland/host/wayland_surface.h
+++ b/ui/ozone/platform/wayland/host/wayland_surface.h
@@ -216,10 +216,9 @@
   // effect immediately.
   void ForceImmediateStateApplication();
 
-  // Requests to Wayland compositor to enables or disable the keyboard shortcuts
-  // inhibition. i.e: to send key events even if they match compositor
-  // accelerators, for example.
-  void SetKeyboardShortcutsInhibition(bool enabled);
+  // Requests to wayland compositor to send key events even if it matches
+  // with the compositor's accelerator keys.
+  void InhibitKeyboardShortcuts();
 
  private:
   FRIEND_TEST_ALL_PREFIXES(WaylandWindowTest,
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 72075a4..80296b4 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -489,18 +489,9 @@
   // Thus, we must store previous bounds to restore later.
   SetOrResetRestoredBounds();
 
-  if (old_state != state_) {
-    if (!did_send_delegate_notification) {
-      previous_state_ = old_state;
-      delegate()->OnWindowStateChanged(previous_state_, state_);
-    }
-
-    if (inhibit_keyboard_shortcuts() &&
-        (state_ == PlatformWindowState::kFullScreen ||
-         old_state == PlatformWindowState::kFullScreen)) {
-      root_surface()->SetKeyboardShortcutsInhibition(
-          window_states.is_fullscreen);
-    }
+  if (old_state != state_ && !did_send_delegate_notification) {
+    previous_state_ = old_state;
+    delegate()->OnWindowStateChanged(previous_state_, state_);
   }
 
   if (did_active_change)
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc
index ac80b926..068537e 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -655,6 +655,10 @@
     return false;
   }
 
+  if (properties.inhibit_keyboard_shortcuts) {
+    root_surface_->InhibitKeyboardShortcuts();
+  }
+
   State state;
   state.bounds_dip = properties.bounds;
   // Properties contain DIP bounds but the buffer scale is initially 1 so it's
@@ -664,7 +668,6 @@
 
   opacity_ = properties.opacity;
   type_ = properties.type;
-  inhibit_keyboard_shortcuts_ = properties.inhibit_keyboard_shortcuts;
 
   connection_->window_manager()->AddWindow(GetWidget(), this);
 
diff --git a/ui/ozone/platform/wayland/host/wayland_window.h b/ui/ozone/platform/wayland/host/wayland_window.h
index feea5c1..3031056 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.h
+++ b/ui/ozone/platform/wayland/host/wayland_window.h
@@ -397,10 +397,6 @@
 
   const gfx::Size& restored_size_dip() const { return restored_size_dip_; }
 
-  bool inhibit_keyboard_shortcuts() const {
-    return inhibit_keyboard_shortcuts_;
-  }
-
   // Configure related:
 
   // Processes the currently pending State. This may generate a new in-flight
@@ -636,8 +632,6 @@
 
   base::OnceClosure drag_loop_quit_closure_;
 
-  bool inhibit_keyboard_shortcuts_ = false;
-
 #if DCHECK_IS_ON()
   bool disable_null_target_dcheck_for_test_ = false;
 #endif
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index f30b7cb..455d8e7d 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -505,9 +505,7 @@
 BubbleDialogDelegateView::BubbleDialogDelegateView(View* anchor_view,
                                                    BubbleBorder::Arrow arrow,
                                                    BubbleBorder::Shadow shadow)
-    : BubbleDialogDelegate(anchor_view, arrow, shadow) {
-  UMA_HISTOGRAM_BOOLEAN("Dialog.BubbleDialogDelegateView.Create", true);
-}
+    : BubbleDialogDelegate(anchor_view, arrow, shadow) {}
 
 BubbleDialogDelegateView::~BubbleDialogDelegateView() {
   // TODO(pbos): Investigate if this is actually still needed, and if so
diff --git a/ui/views/controls/button/md_text_button.cc b/ui/views/controls/button/md_text_button.cc
index 9571509..f4d8b90b 100644
--- a/ui/views/controls/button/md_text_button.cc
+++ b/ui/views/controls/button/md_text_button.cc
@@ -76,18 +76,27 @@
 MdTextButton::~MdTextButton() = default;
 
 void MdTextButton::SetProminent(bool is_prominent) {
-  if (is_prominent_ == is_prominent)
-    return;
-
-  is_prominent_ = is_prominent;
-  SetProperty(kDrawFocusRingBackgroundOutline, is_prominent);
-
+  SetStyle(is_prominent ? Style::kProminent : Style::kDefault);
   UpdateColors();
-  OnPropertyChanged(&is_prominent_, kPropertyEffectsNone);
 }
 
 bool MdTextButton::GetProminent() const {
-  return is_prominent_;
+  return style_ == Style::kProminent;
+}
+
+void MdTextButton::SetStyle(views::MdTextButton::Style button_style) {
+  if (style_ == button_style) {
+    return;
+  }
+
+  style_ = button_style;
+  SetProperty(kDrawFocusRingBackgroundOutline,
+              button_style == Style::kProminent);
+  UpdateColors();
+}
+
+views::MdTextButton::Style MdTextButton::GetStyle() const {
+  return style_;
 }
 
 void MdTextButton::SetBgColorOverride(const absl::optional<SkColor>& color) {
@@ -166,8 +175,7 @@
 }
 
 PropertyEffects MdTextButton::UpdateStyleToIndicateDefaultStatus() {
-  is_prominent_ = is_prominent_ || GetIsDefault();
-  UpdateColors();
+  SetProminent(style_ == Style::kProminent || GetIsDefault());
   return kPropertyEffectsNone;
 }
 
@@ -204,11 +212,15 @@
   if (explicitly_set_normal_color())
     return;
 
-  SkColor enabled_text_color =
-      style::GetColor(*this, label()->GetTextContext(),
-                      is_prominent_ ? style::STYLE_DIALOG_BUTTON_DEFAULT
-                                    : style::STYLE_PRIMARY);
+  style::TextStyle text_style = style::STYLE_PRIMARY;
+  if (style_ == Style::kProminent) {
+    text_style = style::STYLE_DIALOG_BUTTON_DEFAULT;
+  } else if (style_ == Style::kTonal) {
+    text_style = style::STYLE_DIALOG_BUTTON_TONAL;
+  }
 
+  SkColor enabled_text_color =
+      style::GetColor(*this, label()->GetTextContext(), text_style);
   const auto colors = explicitly_set_colors();
   LabelButton::SetEnabledTextColors(enabled_text_color);
   // Disabled buttons need the disabled color explicitly set.
@@ -230,7 +242,7 @@
 
   if (bg_color_override_) {
     bg_color = *bg_color_override_;
-  } else if (is_prominent_) {
+  } else if (style_ == Style::kProminent) {
     bg_color = color_provider->GetColor(
         HasFocus() ? ui::kColorButtonBackgroundProminentFocused
                    : ui::kColorButtonBackgroundProminent);
@@ -238,18 +250,24 @@
       bg_color =
           color_provider->GetColor(ui::kColorButtonBackgroundProminentDisabled);
     }
+  } else if (style_ == Style::kTonal) {
+    bg_color = color_provider->GetColor(
+        HasFocus() ? ui::kColorButtonBackgroundTonalFocused
+                   : ui::kColorButtonBackgroundTonal);
+    if (is_disabled) {
+      bg_color =
+          color_provider->GetColor(ui::kColorButtonBackgroundTonalDisabled);
+    }
   }
 
   if (GetState() == STATE_PRESSED) {
     bg_color = GetNativeTheme()->GetSystemButtonPressedColor(bg_color);
   }
 
-  SkColor stroke_color;
-  if (is_prominent_) {
+  SkColor stroke_color = color_provider->GetColor(
+      is_disabled ? ui::kColorButtonBorderDisabled : ui::kColorButtonBorder);
+  if (style_ == Style::kProminent) {
     stroke_color = SK_ColorTRANSPARENT;
-  } else {
-    stroke_color = color_provider->GetColor(
-        is_disabled ? ui::kColorButtonBorderDisabled : ui::kColorButtonBorder);
   }
 
   SetBackground(
diff --git a/ui/views/controls/button/md_text_button.h b/ui/views/controls/button/md_text_button.h
index 099160f..90bcf74 100644
--- a/ui/views/controls/button/md_text_button.h
+++ b/ui/views/controls/button/md_text_button.h
@@ -17,6 +17,17 @@
 // A button class that implements the Material Design text button spec.
 class VIEWS_EXPORT MdTextButton : public LabelButton {
  public:
+  // MdTextButton has various button styles that can change the button's
+  // background and text color.
+  // kDefault: white background with a blue text and a solid outline.
+  // kProminent: blue background with white text.
+  // kTonal: Cyan background with black text.
+  enum class Style {
+    kDefault = 0,
+    kProminent = 1,
+    kTonal = 2,
+  };
+
   METADATA_HEADER(MdTextButton);
 
   explicit MdTextButton(PressedCallback callback = PressedCallback(),
@@ -28,10 +39,14 @@
 
   ~MdTextButton() override;
 
-  // See |is_prominent_|.
+  // TODO(crbug.com/1406008): Remove the use of Prominent state and use button
+  // style state instead.
   void SetProminent(bool is_prominent);
   bool GetProminent() const;
 
+  void SetStyle(views::MdTextButton::Style button_style);
+  Style GetStyle() const;
+
   // See |bg_color_override_|.
   void SetBgColorOverride(const absl::optional<SkColor>& color);
   absl::optional<SkColor> GetBgColorOverride() const;
@@ -66,8 +81,7 @@
   void UpdateBackgroundColor() override;
   void UpdateColors();
 
-  // True if this button uses prominent styling (blue fill, etc.).
-  bool is_prominent_ = false;
+  Style style_ = Style::kDefault;
 
   // When set, this provides the background color.
   absl::optional<SkColor> bg_color_override_;
@@ -80,9 +94,10 @@
 
 BEGIN_VIEW_BUILDER(VIEWS_EXPORT, MdTextButton, LabelButton)
 VIEW_BUILDER_PROPERTY(bool, Prominent)
-VIEW_BUILDER_PROPERTY(absl::optional<SkColor>, BgColorOverride)
 VIEW_BUILDER_PROPERTY(float, CornerRadius)
+VIEW_BUILDER_PROPERTY(absl::optional<SkColor>, BgColorOverride)
 VIEW_BUILDER_PROPERTY(absl::optional<gfx::Insets>, CustomPadding)
+VIEW_BUILDER_PROPERTY(MdTextButton::Style, Style)
 END_VIEW_BUILDER
 
 }  // namespace views
diff --git a/ui/views/style/typography.h b/ui/views/style/typography.h
index 26c2a75..9cb4958 100644
--- a/ui/views/style/typography.h
+++ b/ui/views/style/typography.h
@@ -90,6 +90,9 @@
   // Style for the default button on a dialog.
   STYLE_DIALOG_BUTTON_DEFAULT,
 
+  // Style for the tonal button on a dialog.
+  STYLE_DIALOG_BUTTON_TONAL,
+
   // Disabled "greyed out" text.
   STYLE_DISABLED,
 
diff --git a/ui/views/style/typography_provider.cc b/ui/views/style/typography_provider.cc
index ba61877..49cc794 100644
--- a/ui/views/style/typography_provider.cc
+++ b/ui/views/style/typography_provider.cc
@@ -68,25 +68,37 @@
 }
 
 ui::ColorId GetColorId(int context, int style) {
-  if (style == style::STYLE_DIALOG_BUTTON_DEFAULT)
+  if (style == style::STYLE_DIALOG_BUTTON_DEFAULT) {
     return ui::kColorButtonForegroundProminent;
-  if (style == style::STYLE_DISABLED)
+  }
+  if (style == style::STYLE_DIALOG_BUTTON_TONAL) {
+    return ui::kColorButtonForegroundTonal;
+  }
+  if (style == style::STYLE_DISABLED) {
     return GetDisabledColorId(context);
-  if (style == style::STYLE_LINK)
+  }
+  if (style == style::STYLE_LINK) {
     return ui::kColorLinkForeground;
-  if (style == style::STYLE_HINT)
+  }
+  if (style == style::STYLE_HINT) {
     return GetHintColorId(context);
-  if (context == style::CONTEXT_BUTTON_MD)
+  }
+  if (context == style::CONTEXT_BUTTON_MD) {
     return ui::kColorButtonForeground;
-  if (context == style::CONTEXT_LABEL && style == style::STYLE_SECONDARY)
+  }
+  if (context == style::CONTEXT_LABEL && style == style::STYLE_SECONDARY) {
     return ui::kColorLabelForegroundSecondary;
+  }
   if (context == style::CONTEXT_DIALOG_BODY_TEXT &&
-      (style == style::STYLE_PRIMARY || style == style::STYLE_SECONDARY))
+      (style == style::STYLE_PRIMARY || style == style::STYLE_SECONDARY)) {
     return ui::kColorDialogForeground;
-  if (context == style::CONTEXT_TEXTFIELD)
+  }
+  if (context == style::CONTEXT_TEXTFIELD) {
     return ui::kColorTextfieldForeground;
-  if (context == style::CONTEXT_MENU || context == style::CONTEXT_TOUCH_MENU)
+  }
+  if (context == style::CONTEXT_MENU || context == style::CONTEXT_TOUCH_MENU) {
     return GetMenuColorId(style);
+  }
   return ui::kColorLabelForeground;
 }
 
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index de5517f..19afca4 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -53,8 +53,6 @@
 
   WidgetDelegate::RegisterWindowWillCloseCallback(
       base::BindOnce(&DialogDelegate::WindowWillClose, base::Unretained(this)));
-  UMA_HISTOGRAM_BOOLEAN("Dialog.DialogDelegate.Create", true);
-  creation_time_ = base::TimeTicks::Now();
 }
 
 // static
@@ -437,8 +435,6 @@
 }
 
 DialogDelegate::~DialogDelegate() {
-  UMA_HISTOGRAM_LONG_TIMES("Dialog.DialogDelegate.Duration",
-                           base::TimeTicks::Now() - creation_time_);
   --g_instance_count;
 }
 
@@ -468,7 +464,6 @@
 
 DialogDelegateView::DialogDelegateView() {
   SetOwnedByWidget(true);
-  UMA_HISTOGRAM_BOOLEAN("Dialog.DialogDelegateView.Create", true);
 }
 
 DialogDelegateView::~DialogDelegateView() = default;
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index 4ce1f88..0b660d3 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -346,9 +346,6 @@
   // Use a fixed dialog width for dialog. Used by DialogClientView.
   int fixed_width_ = 0;
 
-  // The time the dialog is created.
-  base::TimeTicks creation_time_;
-
   // Dialog parameters for this dialog.
   Params params_;
 
diff --git a/ui/webui/resources/cr_components/omnibox/realbox_icon.ts b/ui/webui/resources/cr_components/omnibox/realbox_icon.ts
index 68858d3..c493f7f 100644
--- a/ui/webui/resources/cr_components/omnibox/realbox_icon.ts
+++ b/ui/webui/resources/cr_components/omnibox/realbox_icon.ts
@@ -172,16 +172,17 @@
   }
 
   private computeImageSrc_(): string {
-    if (!this.match || !this.match.imageUrl) {
+    const imageUrl = this.match?.imageUrl;
+    if (!imageUrl) {
       return '';
     }
 
-    if (this.match.imageUrl.startsWith('data:image/')) {
-      // Zero-prefix matches come with the image content in |match.imageUrl|.
-      return this.match.imageUrl;
+    if (imageUrl.startsWith('data:image/')) {
+      // Zero-prefix matches come with the data URI content in |match.imageUrl|.
+      return imageUrl;
     }
 
-    return `//image?${this.match.imageUrl}`;
+    return `//image?staticEncode=true&encodeType=webp&url=${imageUrl}`;
   }
 
   private containerBgColor_(imageDominantColor: string, imageLoading: boolean):
diff --git a/weblayer/browser/content_view_render_view.cc b/weblayer/browser/content_view_render_view.cc
index 24b9878..e95b2a4 100644
--- a/weblayer/browser/content_view_render_view.cc
+++ b/weblayer/browser/content_view_render_view.cc
@@ -221,9 +221,6 @@
 
   compositor_.reset(content::Compositor::Create(this, root_window_));
   root_container_layer_ = cc::Layer::Create();
-  root_container_layer_->SetHitTestable(false);
-  root_container_layer_->SetElementId(
-      cc::ElementId(root_container_layer_->id()));
   root_container_layer_->SetIsDrawable(false);
   compositor_->SetRootLayer(root_container_layer_);
   UpdateBackgroundColor(base::android::AttachCurrentThread());
diff --git a/weblayer/public/java/org/chromium/webengine/WebSandbox.java b/weblayer/public/java/org/chromium/webengine/WebSandbox.java
index e4e1e11..daf557c 100644
--- a/weblayer/public/java/org/chromium/webengine/WebSandbox.java
+++ b/weblayer/public/java/org/chromium/webengine/WebSandbox.java
@@ -183,6 +183,7 @@
 
         void unbind() {
             mContext.unbindService(this);
+            sSandboxConnectionInstance = null;
             sInstance = null;
         }