diff --git a/.rustfmt.toml b/.rustfmt.toml
index 83a7c411..a6fdda8ed 100644
--- a/.rustfmt.toml
+++ b/.rustfmt.toml
@@ -19,5 +19,5 @@
 
 # Third party code is formatted upstream.
 ignore = [
-  "third_party/rust",
+  "third_party/rust/**/crate",
 ]
diff --git a/DEPS b/DEPS
index 7fd3337..99967eb 100644
--- a/DEPS
+++ b/DEPS
@@ -295,15 +295,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': '5443a067413cd32a4ceea694d37297c0ed7e26d2',
+  'skia_revision': '3744490336cf54f6a167643a8e40298cd6ce2756',
   # 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': '6b342ccdca794fbf34d6a3fdbe9f566fc68da414',
+  'v8_revision': 'acf8224030a79d87470640caced05e2a4abae976',
   # 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': '5c301b52947bd844a94ed7221d2837de234bf0fb',
+  'angle_revision': '71ead7b41c8beb1c992f9395a616ea0f41b08b7b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -311,7 +311,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '5a84ee63078c2903c1dd1d5857b4a5e5f614d200',
+  'pdfium_revision': '76cec9aed8e2293f955f87b68bc7563bae5e6a8f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -342,7 +342,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '8951ee06b9ea017812877089233c14f96717d9e7',
+  'nacl_revision': 'b33cd73e78cdd9b330436ce27b74c5369ea2d220',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -366,7 +366,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'cce68bc50e1127115eca0db671c1657f2750b6f5',
+  'catapult_revision': 'ddb664396541c88dc09b7ea2a528b86efa8dc7c6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -374,7 +374,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': '1b5d0021398cbc8025773c96e3afaad69aa02c87',
+  'devtools_frontend_revision': 'aeb2dc95cce6b337892a9315f2304d8862dc3cf3',
   # 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.
@@ -434,7 +434,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': 'c499d18af696bf73b36a5db8c3388e01b5c41b0c',
+  'nearby_revision': '8bd3d3458056c59801ecf2d86145844c4137070c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -782,7 +782,7 @@
 
   'src/clank': {
     'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' +
-    '86becc1ac0eea08ce0cde78319243392b6971c67',
+    '23dad63168e72047bbb6aa121f53172cc156777d',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -811,7 +811,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '515ba2f70ecdeb0f596697f4d8266892a516bb2e',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'e6003808d0a0b3a3436af215d275fbe925339cca',
       'condition': 'checkout_ios',
   },
 
@@ -1220,7 +1220,7 @@
     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' + '@' + '15818974c3d5773c2cabc0380a6a272bf38ca4fe',
+      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '79130b882092b6ab79785ea4180de97a72ca7e15',
     'condition': 'checkout_src_internal',
   },
 
@@ -1635,7 +1635,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cf25b47c405ecca6bc775581f21ba4513e219035',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '197b7113ead0b489c3a4a4366786d6733d0d7c35',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1890,7 +1890,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b9345780aceb9f7648fac46b0860acdba6225ed0',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@361f807203c45702f11298d4b6f43c167513dea9',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 3cb4814..3e2c5a36 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -4520,9 +4520,12 @@
       </message>
 
       <!-- Strings for microphone mute switch notification -->
-      <message name="IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE" desc="Title for a notification shown to the users when an app tries to use the microphone while the microphone is muted.i Similar to IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE_WITH_APP_NAME, except this message contains a generic app name string. Used when the name of the app that's using the microphone cannot be determined.">
+      <message name="IDS_MICROPHONE_MUTED_BY_SW_SWITCH_NOTIFICATION_TITLE" desc="Title for a notification shown to the users when an app tries to use the microphone while the microphone is muted by one of the software switches.">
         Turn on microphone access?
       </message>
+      <message name="IDS_MICROPHONE_MUTED_BY_HW_SWITCH_NOTIFICATION_TITLE" desc="Title for a notification shown to the users when an app tries to use the microphone while the microphone is muted by the hardware switch.">
+        Turn on physical mic switch on your device
+      </message>
       <message name="IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE" desc="Message for a notification shown to the users when an app tries to use the microphone while the microphone is muted.">
         This allows microphone access for all apps and websites with the microphone permission
       </message>
@@ -4535,9 +4538,6 @@
       <message name="IDS_MICROPHONE_MUTED_NOTIFICATION_ACTION_BUTTON" desc="Label for an action button in a notification shown to the users when an app tries to use the microphone while the microphone is muted. The button, when tapped unmutes the microphone.">
         Turn on microphone access
       </message>
-      <message name="IDS_MICROPHONE_MUTE_SWITCH_ON_NOTIFICATION_MESSAGE" desc="Message for a notification shown to the users when an app tries to use the microphone while the microphone mute switch, which disables microphone input, is turned on.">
-        Your microphone is off. Turn on your device’s microphone button.
-      </message>
 
       <message name="IDS_ASH_MESSAGE_CENTER_UNLOCK_TO_PERFORM_ACTION" desc="The short message to encourage user to unlock the device so that ChromeOS can perform the notification action selected by user after unlocking.">
         Unlock device to perform the notification action
diff --git a/ash/ash_strings_grd/IDS_MICROPHONE_MUTED_BY_HW_SWITCH_NOTIFICATION_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_MICROPHONE_MUTED_BY_HW_SWITCH_NOTIFICATION_TITLE.png.sha1
new file mode 100644
index 0000000..7f636ae
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_MICROPHONE_MUTED_BY_HW_SWITCH_NOTIFICATION_TITLE.png.sha1
@@ -0,0 +1 @@
+b1b0bbe9584159c67e0a54355d7b267c87fda375
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_MICROPHONE_MUTED_BY_SW_SWITCH_NOTIFICATION_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_MICROPHONE_MUTED_BY_SW_SWITCH_NOTIFICATION_TITLE.png.sha1
new file mode 100644
index 0000000..ac2d1924
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_MICROPHONE_MUTED_BY_SW_SWITCH_NOTIFICATION_TITLE.png.sha1
@@ -0,0 +1 @@
+c99f3e94cfdf4be83b7b77f7ca340dc7aeaccd79
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE.png.sha1
deleted file mode 100644
index 9999d0e..0000000
--- a/ash/ash_strings_grd/IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a3f5dd22925b6f620dd7d349dcc8fea011dd9faa
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_MICROPHONE_MUTE_SWITCH_ON_NOTIFICATION_MESSAGE.png.sha1 b/ash/ash_strings_grd/IDS_MICROPHONE_MUTE_SWITCH_ON_NOTIFICATION_MESSAGE.png.sha1
deleted file mode 100644
index 9b9816d..0000000
--- a/ash/ash_strings_grd/IDS_MICROPHONE_MUTE_SWITCH_ON_NOTIFICATION_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f4433441e2dfd36b12a9fa82809e53866aee4823
\ No newline at end of file
diff --git a/ash/shell.cc b/ash/shell.cc
index 5d60c86..8a786474 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -161,7 +161,6 @@
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/usb_peripheral/usb_peripheral_notification_controller.h"
 #include "ash/system/video_conference/fake_video_conference_tray_controller.h"
-#include "ash/system/video_conference/video_conference_tray_controller.h"
 #include "ash/touch/ash_touch_transform_controller.h"
 #include "ash/touch/touch_devices_controller.h"
 #include "ash/tray_action/tray_action.h"
@@ -924,11 +923,6 @@
   // Depends on |focus_controller_|, so must be destroyed before.
   window_tree_host_manager_.reset();
 
-  // The UI has been destructed, so it's now OK to destruct the video conference
-  // UI controller.
-  if (features::IsVcControlsUiEnabled())
-    video_conference_tray_controller_.reset();
-
   // The desks controller is destroyed after the window tree host manager and
   // before the focus controller. At this point it is guaranteed that querying
   // the active desk is no longer needed.
@@ -1436,19 +1430,6 @@
   if (features::IsWmModeEnabled())
     wm_mode_controller_ = std::make_unique<WmModeController>();
 
-  // This controller MUST be initialized before the UI is constructed. The
-  // video conferencing views will have their own reference to this controller,
-  // may be observers of it, and will assume it exists for as long as they
-  // themselves exist.
-  if (features::IsVcControlsUiEnabled()) {
-    // `VideoConferenceTrayController` relies on audio and camera services to
-    // function properly, so we will use the fake version when `dbus_bus` is not
-    // available so that this works on linux-chromeos and unit tests.
-    video_conference_tray_controller_ =
-        dbus_bus ? std::make_unique<VideoConferenceTrayController>()
-                 : std::make_unique<FakeVideoConferenceTrayController>();
-  }
-
   window_tree_host_manager_->InitHosts();
 
   // Create virtual keyboard after WindowTreeHostManager::InitHosts() since
diff --git a/ash/shell.h b/ash/shell.h
index 836da2d..13aaada 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -221,7 +221,6 @@
 class TouchDevicesController;
 class TrayAction;
 class UserMetricsRecorder;
-class VideoConferenceTrayController;
 class VideoActivityNotifier;
 class VideoDetector;
 class WallpaperControllerImpl;
@@ -660,10 +659,6 @@
 
   UserMetricsRecorder* metrics() { return user_metrics_recorder_.get(); }
 
-  VideoConferenceTrayController* video_conference_tray_controller() {
-    return video_conference_tray_controller_.get();
-  }
-
   VideoDetector* video_detector() { return video_detector_.get(); }
   WallpaperControllerImpl* wallpaper_controller() {
     return wallpaper_controller_.get();
@@ -935,8 +930,6 @@
   std::unique_ptr<ClipboardHistoryControllerImpl> clipboard_history_controller_;
   std::unique_ptr<TouchDevicesController> touch_devices_controller_;
   std::unique_ptr<TrayAction> tray_action_;
-  std::unique_ptr<VideoConferenceTrayController>
-      video_conference_tray_controller_;
   std::unique_ptr<WallpaperControllerImpl> wallpaper_controller_;
   std::unique_ptr<WindowCycleController> window_cycle_controller_;
   std::unique_ptr<WindowRestoreController> window_restore_controller_;
diff --git a/ash/system/microphone_mute/microphone_mute_notification_controller.cc b/ash/system/microphone_mute/microphone_mute_notification_controller.cc
index f86fa1a..bc8cb3d2 100644
--- a/ash/system/microphone_mute/microphone_mute_notification_controller.cc
+++ b/ash/system/microphone_mute/microphone_mute_notification_controller.cc
@@ -161,11 +161,6 @@
 
 std::u16string MicrophoneMuteNotificationController::GetNotificationMessage(
     const std::vector<std::u16string>& app_names) const {
-  if (mic_muted_by_mute_switch_) {
-    return l10n_util::GetStringUTF16(
-        IDS_MICROPHONE_MUTE_SWITCH_ON_NOTIFICATION_MESSAGE);
-  }
-
   if (app_names.size() == 1) {
     return l10n_util::GetStringFUTF16(
         IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME,
@@ -184,7 +179,12 @@
 
 std::u16string MicrophoneMuteNotificationController::GetNotificationTitle()
     const {
-  return l10n_util::GetStringUTF16(IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE);
+  if (mic_muted_by_mute_switch_) {
+    return l10n_util::GetStringUTF16(
+        IDS_MICROPHONE_MUTED_BY_HW_SWITCH_NOTIFICATION_TITLE);
+  }
+  return l10n_util::GetStringUTF16(
+      IDS_MICROPHONE_MUTED_BY_SW_SWITCH_NOTIFICATION_TITLE);
 }
 
 void MicrophoneMuteNotificationController::RemoveMicrophoneMuteNotification() {
diff --git a/ash/system/microphone_mute/microphone_mute_notification_controller_unittest.cc b/ash/system/microphone_mute/microphone_mute_notification_controller_unittest.cc
index 8517a64..25e7af9 100644
--- a/ash/system/microphone_mute/microphone_mute_notification_controller_unittest.cc
+++ b/ash/system/microphone_mute/microphone_mute_notification_controller_unittest.cc
@@ -431,11 +431,11 @@
   EXPECT_TRUE(GetPopupNotification());
 }
 
-TEST_F(MicrophoneMuteNotificationControllerTest, NotificationContents) {
+TEST_F(MicrophoneMuteNotificationControllerTest, NotificationText) {
   // No notification initially.
   EXPECT_FALSE(GetNotification());
 
-  // Mute the mic, still no notification.
+  // Mute the mic using sw switch, still no notification.
   MuteMicrophone();
   EXPECT_FALSE(GetNotification());
 
@@ -449,7 +449,8 @@
   SetNumberOfActiveInputStreams(1);
   EXPECT_TRUE(GetNotification());
   EXPECT_TRUE(GetPopupNotification());
-  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE),
+  EXPECT_EQ(l10n_util::GetStringUTF16(
+                IDS_MICROPHONE_MUTED_BY_SW_SWITCH_NOTIFICATION_TITLE),
             GetNotification()->title());
   // The notification body should not contain any app name.
   EXPECT_EQ(
@@ -491,6 +492,18 @@
   EXPECT_EQ(
       l10n_util::GetStringUTF16(IDS_MICROPHONE_MUTED_NOTIFICATION_MESSAGE),
       GetNotification()->message());
+
+  EXPECT_FALSE(
+      ui::MicrophoneMuteSwitchMonitor::Get()->microphone_mute_switch_on());
+  // Toggle the hw switch.
+  SetMicrophoneMuteSwitchState(/*muted=*/true);
+  EXPECT_TRUE(GetNotification());
+  EXPECT_TRUE(GetPopupNotification());
+  // The title of the notification should be different when microphone is muted
+  // by the hw switch.
+  EXPECT_EQ(l10n_util::GetStringUTF16(
+                IDS_MICROPHONE_MUTED_BY_HW_SWITCH_NOTIFICATION_TITLE),
+            GetNotification()->title());
 }
 
 TEST_F(MicrophoneMuteNotificationControllerTest, MetricCollection) {
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 2870ce1..a17abb0 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -251,15 +251,18 @@
       welcome_bubble_(std::make_unique<PaletteWelcomeBubble>(this)),
       stylus_event_handler_(std::make_unique<StylusEventHandler>(this)),
       scoped_session_observer_(this) {
+  SetPressedCallback(base::BindRepeating(&PaletteTray::OnPaletteTrayPressed,
+                                         base::Unretained(this)));
+
   PaletteTool::RegisterToolInstances(palette_tool_manager_.get());
 
   SetLayoutManager(std::make_unique<views::FillLayout>());
-  icon_ = new views::ImageView();
-  icon_->SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_TITLE));
-  UpdateTrayIcon();
 
+  auto icon = std::make_unique<views::ImageView>();
+  icon->SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_TITLE));
   tray_container()->SetMargin(kTrayIconMainAxisInset, kTrayIconCrossAxisInset);
-  tray_container()->AddChildView(icon_);
+  icon_ = tray_container()->AddChildView(std::move(icon));
+  UpdateTrayIcon();
 
   Shell::Get()->AddShellObserver(this);
   Shell::Get()->window_tree_host_manager()->AddObserver(this);
@@ -606,27 +609,6 @@
   InitializeWithLocalState();
 }
 
-bool PaletteTray::PerformAction(const ui::Event& event) {
-  if (bubble_) {
-    if (num_actions_in_bubble_ == 0) {
-      RecordPaletteOptionsUsage(PaletteTrayOptions::PALETTE_CLOSED_NO_ACTION,
-                                PaletteInvocationMethod::MENU);
-    }
-    HidePalette();
-    return true;
-  }
-
-  // Do not show the bubble if there was an action on the palette tray while
-  // there was an active tool.
-  if (DeactivateActiveTool()) {
-    SetIsActive(false);
-    return true;
-  }
-
-  ShowBubble();
-  return true;
-}
-
 void PaletteTray::CloseBubble() {
   HidePalette();
 }
@@ -759,6 +741,26 @@
   }
 }
 
+void PaletteTray::OnPaletteTrayPressed(const ui::Event& event) {
+  if (bubble_) {
+    if (num_actions_in_bubble_ == 0) {
+      RecordPaletteOptionsUsage(PaletteTrayOptions::PALETTE_CLOSED_NO_ACTION,
+                                PaletteInvocationMethod::MENU);
+    }
+    HidePalette();
+    return;
+  }
+
+  // Do not show the bubble if there was an action on the palette tray while
+  // there was an active tool.
+  if (DeactivateActiveTool()) {
+    SetIsActive(false);
+    return;
+  }
+
+  ShowBubble();
+}
+
 void PaletteTray::OnHasSeenStylusPrefChanged() {
   DCHECK(local_state_);
 
diff --git a/ash/system/palette/palette_tray.h b/ash/system/palette/palette_tray.h
index 55e957d..e9c10d2 100644
--- a/ash/system/palette/palette_tray.h
+++ b/ash/system/palette/palette_tray.h
@@ -27,8 +27,15 @@
 class Point;
 }
 
+namespace ui {
+class Event;
+class EventHandler;
+class TouchEvent;
+}  // namespace ui
+
 namespace views {
 class ImageView;
+class Widget;
 }
 
 namespace ash {
@@ -36,6 +43,8 @@
 class PaletteTrayTestApi;
 class PaletteToolManager;
 class PaletteWelcomeBubble;
+class Shelf;
+class TrayBubbleView;
 class TrayBubbleWrapper;
 
 // The PaletteTray shows the palette in the bottom area of the screen. This
@@ -91,7 +100,6 @@
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
   void AnchorUpdated() override;
   void Initialize() override;
-  bool PerformAction(const ui::Event& event) override;
   void CloseBubble() override;
   void ShowBubble() override;
   TrayBubbleView* GetBubbleView() override;
@@ -145,6 +153,9 @@
   // Called when the palette enabled pref has changed.
   void OnPaletteEnabledPrefChanged();
 
+  // Callback called when this TrayBackgroundView is pressed.
+  void OnPaletteTrayPressed(const ui::Event& event);
+
   // Called when the has seen stylus pref has changed.
   void OnHasSeenStylusPrefChanged();
 
@@ -172,7 +183,7 @@
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_user_;
 
   // Weak pointer, will be parented by TrayContainer for its lifetime.
-  views::ImageView* icon_;
+  views::ImageView* icon_ = nullptr;
 
   // Cached palette pref value.
   bool is_palette_enabled_ = true;
diff --git a/ash/system/palette/palette_tray_unittest.cc b/ash/system/palette/palette_tray_unittest.cc
index 4e2dc45..fb67a6a 100644
--- a/ash/system/palette/palette_tray_unittest.cc
+++ b/ash/system/palette/palette_tray_unittest.cc
@@ -62,13 +62,6 @@
 
   ~PaletteTrayTest() override = default;
 
-  // Performs a tap on the palette tray button.
-  void PerformTap() {
-    ui::GestureEvent tap(0, 0, 0, base::TimeTicks(),
-                         ui::GestureEventDetails(ui::ET_GESTURE_TAP));
-    palette_tray_->PerformAction(tap);
-  }
-
   // Fake a stylus ejection.
   void EjectStylus() {
     test_api_->OnStylusStateChanged(ui::StylusState::REMOVED);
@@ -96,6 +89,16 @@
         .SetFirstDisplayAsInternalDisplay();
   }
 
+  // Sends a stylus event, which makes the `PaletteTray` show up.
+  void ShowPaletteTray() {
+    ui::test::EventGenerator* generator = GetEventGenerator();
+    generator->EnterPenPointerMode();
+    generator->PressTouch();
+    generator->ReleaseTouch();
+    generator->ExitPenPointerMode();
+    ASSERT_TRUE(palette_tray_->GetVisible());
+  }
+
   PrefService* prefs() {
     return Shell::Get()->session_controller()->GetPrimaryUserPrefService();
   }
@@ -173,8 +176,19 @@
   EXPECT_FALSE(palette_tray_->GetVisible());
 }
 
+// A basic test to ensure the OnPressedCallback is triggered on tap.
+TEST_F(PaletteTrayTest, PressingTrayButton) {
+  ShowPaletteTray();
+
+  GestureTapOn(palette_tray_);
+
+  EXPECT_TRUE(palette_tray_->is_active());
+}
+
 // Verify taps on the palette tray button results in expected behaviour.
 TEST_F(PaletteTrayTest, PaletteTrayWorkflow) {
+  ShowPaletteTray();
+
   // Verify the palette tray button is not active, and the palette tray bubble
   // is not shown initially.
   EXPECT_FALSE(palette_tray_->is_active());
@@ -182,7 +196,7 @@
 
   // Verify that by tapping the palette tray button, the button will become
   // active and the palette tray bubble will be open.
-  PerformTap();
+  GestureTapOn(palette_tray_);
   EXPECT_TRUE(palette_tray_->is_active());
   EXPECT_TRUE(test_api_->tray_bubble_wrapper());
 
@@ -196,14 +210,14 @@
 
   // Verify that tapping the palette tray while a tool is active will deactivate
   // the tool, and the palette tray button will not be active.
-  PerformTap();
+  GestureTapOn(palette_tray_);
   EXPECT_FALSE(palette_tray_->is_active());
   EXPECT_FALSE(test_api_->palette_tool_manager()->IsToolActive(
       PaletteToolId::LASER_POINTER));
 
   // Verify that activating a action tool will close the palette tray bubble and
   // the palette tray button is will not be active.
-  PerformTap();
+  GestureTapOn(palette_tray_);
   ASSERT_TRUE(test_api_->tray_bubble_wrapper());
   const auto capture_tool_id = PaletteToolId::ENTER_CAPTURE_MODE;
   test_api_->palette_tool_manager()->ActivateTool(capture_tool_id);
@@ -220,7 +234,8 @@
 // capture region) are deactivated.
 TEST_F(PaletteTrayTest, ModeToolDeactivatedAutomatically) {
   // Open the palette tray with a tap.
-  PerformTap();
+  ShowPaletteTray();
+  GestureTapOn(palette_tray_);
   ASSERT_TRUE(palette_tray_->is_active());
   ASSERT_TRUE(test_api_->tray_bubble_wrapper());
 
diff --git a/ash/system/privacy_hub/camera_privacy_switch_controller.cc b/ash/system/privacy_hub/camera_privacy_switch_controller.cc
index ff5f1a8..3b7de8ca 100644
--- a/ash/system/privacy_hub/camera_privacy_switch_controller.cc
+++ b/ash/system/privacy_hub/camera_privacy_switch_controller.cc
@@ -129,6 +129,8 @@
 
 CameraSWPrivacySwitchSetting
 CameraPrivacySwitchController::GetUserSwitchPreference() {
+  DCHECK(pref_change_registrar_);
+  DCHECK(pref_change_registrar_->prefs());
   const bool allowed =
       pref_change_registrar_->prefs()->GetBoolean(prefs::kUserCameraAllowed);
 
diff --git a/ash/system/tray/status_area_overflow_button_tray.cc b/ash/system/tray/status_area_overflow_button_tray.cc
index b476f62..dff93c5 100644
--- a/ash/system/tray/status_area_overflow_button_tray.cc
+++ b/ash/system/tray/status_area_overflow_button_tray.cc
@@ -12,6 +12,7 @@
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/animation/tween.h"
@@ -59,8 +60,8 @@
   else if (state == CLICK_TO_COLLAPSE)
     slide_animation_->Hide();
 
-  // TODO(tengs): Currently, the collpase/expand animation is not fully spec'd,
-  // so skip it for now.
+  // TODO(b/260253147): Currently, the collapse/expand animation is not fully
+  // spec'd, so skip it for now.
   slide_animation_->End();
 }
 
@@ -95,8 +96,9 @@
     : TrayBackgroundView(
           shelf,
           TrayBackgroundViewCatalogName::kStatusAreaOverflowButton),
-      icon_(new IconView()) {
-  tray_container()->AddChildView(icon_);
+      icon_(tray_container()->AddChildView(std::make_unique<IconView>())) {
+  SetPressedCallback(base::BindRepeating(
+      &StatusAreaOverflowButtonTray::OnButtonPressed, base::Unretained(this)));
   set_use_bounce_in_animation(false);
 }
 
@@ -104,11 +106,6 @@
 
 void StatusAreaOverflowButtonTray::ClickedOutsideBubble() {}
 
-void StatusAreaOverflowButtonTray::ResetStateToCollapsed() {
-  state_ = CLICK_TO_EXPAND;
-  icon_->ToggleState(state_);
-}
-
 std::u16string StatusAreaOverflowButtonTray::GetAccessibleNameForTray() {
   return l10n_util::GetStringUTF16(
       state_ == CLICK_TO_COLLAPSE ? IDS_ASH_STATUS_AREA_OVERFLOW_BUTTON_COLLAPSE
@@ -125,28 +122,31 @@
   SetVisiblePreferred(false);
 }
 
-bool StatusAreaOverflowButtonTray::PerformAction(const ui::Event& event) {
-  state_ = state_ == CLICK_TO_COLLAPSE ? CLICK_TO_EXPAND : CLICK_TO_COLLAPSE;
-  icon_->ToggleState(state_);
-  shelf()->GetStatusAreaWidget()->UpdateCollapseState();
-  return false;
-}
-
 void StatusAreaOverflowButtonTray::SetVisiblePreferred(bool visible_preferred) {
-  // The visibility of the overflow tray button is completed controlled by the
-  // StatusAreaWidget, so we bypass all default visibility logic from
-  // TrayBackgroundView.
+  // The visibility of the overflow tray button is controlled by the
+  // `StatusAreaWidget`, so we bypass all default visibility logic from
+  // `TrayBackgroundView`.
   views::View::SetVisible(visible_preferred);
 }
 
 void StatusAreaOverflowButtonTray::UpdateAfterStatusAreaCollapseChange() {
-  // The visibility of the overflow tray button is completed controlled by the
-  // StatusAreaWidget, so we bypass all default visibility logic from
-  // TrayBackgroundView.
+  // The visibility of the overflow tray button is controlled by the
+  // `StatusAreaWidget`, so we bypass all default visibility logic from
+  // `TrayBackgroundView`.
 }
 
-const char* StatusAreaOverflowButtonTray::GetClassName() const {
-  return "StatusAreaOverflowButtonTray";
+void StatusAreaOverflowButtonTray::OnButtonPressed(const ui::Event& event) {
+  state_ = state_ == CLICK_TO_COLLAPSE ? CLICK_TO_EXPAND : CLICK_TO_COLLAPSE;
+  icon_->ToggleState(state_);
+  shelf()->GetStatusAreaWidget()->UpdateCollapseState();
 }
 
+void StatusAreaOverflowButtonTray::ResetStateToCollapsed() {
+  state_ = CLICK_TO_EXPAND;
+  icon_->ToggleState(state_);
+}
+
+BEGIN_METADATA(StatusAreaOverflowButtonTray, TrayBackgroundView);
+END_METADATA
+
 }  // namespace ash
diff --git a/ash/system/tray/status_area_overflow_button_tray.h b/ash/system/tray/status_area_overflow_button_tray.h
index f69515f..16ea177d 100644
--- a/ash/system/tray/status_area_overflow_button_tray.h
+++ b/ash/system/tray/status_area_overflow_button_tray.h
@@ -5,12 +5,15 @@
 #ifndef ASH_SYSTEM_TRAY_STATUS_AREA_OVERFLOW_BUTTON_TRAY_H_
 #define ASH_SYSTEM_TRAY_STATUS_AREA_OVERFLOW_BUTTON_TRAY_H_
 
+#include <memory>
+
 #include "ash/ash_export.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/tray_background_view.h"
 #include "ash/system/tray/tray_bubble_view.h"
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/transform.h"
@@ -21,19 +24,24 @@
 class SlideAnimation;
 }  // namespace gfx
 
+namespace ui {
+class Event;
+}  // namespace ui
+
 namespace ash {
+class Shelf;
 
 // The collapse/expand tray button in tablet mode, which is shown when the
 // status area contains more buttons than the maximum width. Tapping on this
 // button will show/hide the overflown tray buttons.
 class ASH_EXPORT StatusAreaOverflowButtonTray : public TrayBackgroundView {
  public:
-  explicit StatusAreaOverflowButtonTray(Shelf* shelf);
+  METADATA_HEADER(StatusAreaOverflowButtonTray);
 
+  explicit StatusAreaOverflowButtonTray(Shelf* shelf);
   StatusAreaOverflowButtonTray(const StatusAreaOverflowButtonTray&) = delete;
   StatusAreaOverflowButtonTray& operator=(const StatusAreaOverflowButtonTray&) =
       delete;
-
   ~StatusAreaOverflowButtonTray() override;
 
   enum State { CLICK_TO_EXPAND = 0, CLICK_TO_COLLAPSE };
@@ -44,10 +52,11 @@
   void HandleLocaleChange() override;
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
   void Initialize() override;
-  bool PerformAction(const ui::Event& event) override;
   void SetVisiblePreferred(bool visible_preferred) override;
   void UpdateAfterStatusAreaCollapseChange() override;
-  const char* GetClassName() const override;
+
+  // Callback called when this button is pressed.
+  void OnButtonPressed(const ui::Event& event);
 
   // Resets the state back to be collapsed (i.e. CLICK_TO_EXPAND).
   void ResetStateToCollapsed();
@@ -76,6 +85,7 @@
 
   State state_ = CLICK_TO_EXPAND;
 
+  // Owned by the views hierarchy.
   IconView* const icon_;
 };
 
diff --git a/ash/system/tray/status_area_overflow_button_tray_unittest.cc b/ash/system/tray/status_area_overflow_button_tray_unittest.cc
index 708ccd2..0f54e9e 100644
--- a/ash/system/tray/status_area_overflow_button_tray_unittest.cc
+++ b/ash/system/tray/status_area_overflow_button_tray_unittest.cc
@@ -17,41 +17,30 @@
 
 namespace ash {
 
-class StatusAreaOverflowButtonTrayTest : public AshTestBase {
- public:
-  StatusAreaOverflowButtonTrayTest() = default;
+using StatusAreaOverflowButtonTrayTest = AshTestBase;
 
-  StatusAreaOverflowButtonTrayTest(const StatusAreaOverflowButtonTrayTest&) =
-      delete;
-  StatusAreaOverflowButtonTrayTest& operator=(
-      const StatusAreaOverflowButtonTrayTest&) = delete;
-
-  ~StatusAreaOverflowButtonTrayTest() override = default;
-
-  void SetUp() override { AshTestBase::SetUp(); }
-
-  void TapButton() {
-    ui::GestureEvent tap_event =
-        ui::GestureEvent(0, 0, 0, base::TimeTicks(),
-                         ui::GestureEventDetails(ui::ET_GESTURE_TAP));
-    GetTray()->PerformAction(tap_event);
-  }
-
-  StatusAreaOverflowButtonTray* GetTray() {
-    return StatusAreaWidgetTestHelper::GetStatusAreaWidget()
-        ->overflow_button_tray();
-  }
-};
-
+// Tests that the button reacts to press as expected. Artificially sets the
+// button to be visible.
 TEST_F(StatusAreaOverflowButtonTrayTest, ToggleExpanded) {
-  EXPECT_EQ(StatusAreaOverflowButtonTray::CLICK_TO_EXPAND, GetTray()->state());
-  TapButton();
-  base::RunLoop().RunUntilIdle();
+  auto* overflow_button_tray =
+      StatusAreaWidgetTestHelper::GetStatusAreaWidget()->overflow_button_tray();
+  overflow_button_tray->SetVisiblePreferred(true);
+
+  EXPECT_EQ(StatusAreaOverflowButtonTray::CLICK_TO_EXPAND,
+            overflow_button_tray->state());
+
+  GestureTapOn(overflow_button_tray);
 
   EXPECT_EQ(StatusAreaOverflowButtonTray::CLICK_TO_COLLAPSE,
-            GetTray()->state());
-  TapButton();
-  EXPECT_EQ(StatusAreaOverflowButtonTray::CLICK_TO_EXPAND, GetTray()->state());
+            overflow_button_tray->state());
+
+  // Force the tray button to be visible. It is currently not visible because
+  // tablet mode is not enabled.
+  overflow_button_tray->SetVisiblePreferred(true);
+  GestureTapOn(overflow_button_tray);
+
+  EXPECT_EQ(StatusAreaOverflowButtonTray::CLICK_TO_EXPAND,
+            overflow_button_tray->state());
 }
 
 }  // namespace ash
diff --git a/ash/system/video_conference/video_conference_tray.cc b/ash/system/video_conference/video_conference_tray.cc
index f7fa5e25..281e42b 100644
--- a/ash/system/video_conference/video_conference_tray.cc
+++ b/ash/system/video_conference/video_conference_tray.cc
@@ -6,12 +6,9 @@
 
 #include <string>
 
-#include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/resources/vector_icons/vector_icons.h"
-#include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_id.h"
 #include "ash/style/icon_button.h"
@@ -25,7 +22,6 @@
 #include "base/functional/bind.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/base/models/image_model.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/skbitmap_operations.h"
 #include "ui/views/controls/image_view.h"
@@ -186,7 +182,7 @@
 }
 
 void VideoConferenceTray::OnCameraButtonClicked(const ui::Event& event) {
-  Shell::Get()->video_conference_tray_controller()->SetCameraSoftwareMuted(
+  VideoConferenceTrayController::Get()->SetCameraSoftwareMuted(
       /*mute_camera=*/!camera_icon_->toggled());
 }
 
diff --git a/ash/system/video_conference/video_conference_tray_controller.cc b/ash/system/video_conference/video_conference_tray_controller.cc
index 4573dac..a7aa272 100644
--- a/ash/system/video_conference/video_conference_tray_controller.cc
+++ b/ash/system/video_conference/video_conference_tray_controller.cc
@@ -14,20 +14,29 @@
 
 namespace ash {
 
+namespace {
+VideoConferenceTrayController* g_controller_instance = nullptr;
+}  // namespace
+
 VideoConferenceTrayController::VideoConferenceTrayController() {
+  DCHECK(!g_controller_instance);
+  g_controller_instance = this;
+
   media::CameraHalDispatcherImpl::GetInstance()->AddCameraPrivacySwitchObserver(
       this);
 }
 
 VideoConferenceTrayController::~VideoConferenceTrayController() {
+  DCHECK_EQ(this, g_controller_instance);
+  g_controller_instance = nullptr;
+
   media::CameraHalDispatcherImpl::GetInstance()
       ->RemoveCameraPrivacySwitchObserver(this);
 }
 
-void VideoConferenceTrayController::SetCameraSoftwareMuted(bool mute_camera) {
-  media::CameraHalDispatcherImpl::GetInstance()->SetCameraSWPrivacySwitchState(
-      mute_camera ? cros::mojom::CameraPrivacySwitchState::ON
-                  : cros::mojom::CameraPrivacySwitchState::OFF);
+// static
+VideoConferenceTrayController* VideoConferenceTrayController::Get() {
+  return g_controller_instance;
 }
 
 void VideoConferenceTrayController::OnCameraSWPrivacySwitchStateChanged(
diff --git a/ash/system/video_conference/video_conference_tray_controller.h b/ash/system/video_conference/video_conference_tray_controller.h
index 5312ab4..3ee053b 100644
--- a/ash/system/video_conference/video_conference_tray_controller.h
+++ b/ash/system/video_conference/video_conference_tray_controller.h
@@ -26,8 +26,11 @@
 
   ~VideoConferenceTrayController() override;
 
+  // Returns the singleton instance.
+  static VideoConferenceTrayController* Get();
+
   // Set the state for camera software mute. Virtual for testing/mocking.
-  virtual void SetCameraSoftwareMuted(bool mute_camera);
+  virtual void SetCameraSoftwareMuted(bool mute_camera) = 0;
 
   // media::CameraPrivacySwitchObserver:
   void OnCameraSWPrivacySwitchStateChanged(
diff --git a/ash/system/video_conference/video_conference_tray_unittest.cc b/ash/system/video_conference/video_conference_tray_unittest.cc
index 32945fe..14a1318 100644
--- a/ash/system/video_conference/video_conference_tray_unittest.cc
+++ b/ash/system/video_conference/video_conference_tray_unittest.cc
@@ -7,7 +7,6 @@
 #include "ash/constants/ash_features.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shell.h"
 #include "ash/style/ash_color_id.h"
 #include "ash/style/icon_button.h"
 #include "ash/system/status_area_widget.h"
@@ -16,7 +15,6 @@
 #include "ash/system/video_conference/fake_video_conference_tray_controller.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/scoped_feature_list.h"
-#include "ui/events/test/event_generator.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/views/controls/image_view.h"
@@ -39,10 +37,21 @@
   VideoConferenceTrayTest& operator=(const VideoConferenceTrayTest&) = delete;
   ~VideoConferenceTrayTest() override = default;
 
+  // AshTestBase:
   void SetUp() override {
     scoped_feature_list_.InitAndEnableFeature(features::kVcControlsUi);
 
     AshTestBase::SetUp();
+
+    // Instantiates a fake controller (the real one is created in
+    // ChromeBrowserMainExtraPartsAsh::PreProfileInit() which is not called in
+    // ash unit tests).
+    controller_ = std::make_unique<FakeVideoConferenceTrayController>();
+  }
+
+  void TearDown() override {
+    controller_.reset();
+    AshTestBase::TearDown();
   }
 
   VideoConferenceTray* video_conference_tray() {
@@ -54,13 +63,11 @@
     return video_conference_tray()->expand_indicator_;
   }
 
-  FakeVideoConferenceTrayController* controller() {
-    return static_cast<FakeVideoConferenceTrayController*>(
-        Shell::Get()->video_conference_tray_controller());
-  }
+  FakeVideoConferenceTrayController* controller() { return controller_.get(); }
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<FakeVideoConferenceTrayController> controller_;
 };
 
 TEST_F(VideoConferenceTrayTest, ClickTrayButton) {
diff --git a/ash/webui/shortcut_customization_ui/backend/BUILD.gn b/ash/webui/shortcut_customization_ui/backend/BUILD.gn
index d86bf626..e6227c6d 100644
--- a/ash/webui/shortcut_customization_ui/backend/BUILD.gn
+++ b/ash/webui/shortcut_customization_ui/backend/BUILD.gn
@@ -10,7 +10,6 @@
   sources = [
     "accelerator_configuration_provider.cc",
     "accelerator_configuration_provider.h",
-    "shortcut_customization_delegate.h",
   ]
 
   deps = [
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
index 2702f40..7ddd88f 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.cc
@@ -4,8 +4,6 @@
 
 #include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h"
 
-#include <memory>
-#include <utility>
 #include <vector>
 
 #include "ash/accelerators/accelerator_layout_table.h"
@@ -14,7 +12,6 @@
 #include "ash/public/cpp/accelerators_util.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
-#include "ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h"
 #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h"
 #include "base/containers/fixed_flat_map.h"
 #include "base/containers/flat_map.h"
@@ -135,13 +132,9 @@
 
 namespace shortcut_ui {
 
-AcceleratorConfigurationProvider::AcceleratorConfigurationProvider(
-    std::unique_ptr<ShortcutCustomizationDelegate>
-        shortcut_customization_delegate)
+AcceleratorConfigurationProvider::AcceleratorConfigurationProvider()
     : ash_accelerator_configuration_(
-          Shell::Get()->ash_accelerator_configuration()),
-      shortcut_customization_delegate_(
-          std::move(shortcut_customization_delegate)) {
+          Shell::Get()->ash_accelerator_configuration()) {
   // Observe connected keyboard events.
   ui::DeviceDataManager::GetInstance()->AddObserver(this);
 
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h
index 5187b42..c5d9927 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h
@@ -5,8 +5,6 @@
 #ifndef ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_ACCELERATOR_CONFIGURATION_PROVIDER_H_
 #define ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_ACCELERATOR_CONFIGURATION_PROVIDER_H_
 
-#include <memory>
-
 #include "ash/public/cpp/accelerator_configuration.h"
 #include "ash/public/mojom/accelerator_keys.mojom.h"
 #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h"
@@ -22,8 +20,6 @@
 namespace ash {
 namespace shortcut_ui {
 
-class ShortcutCustomizationDelegate;
-
 class AcceleratorConfigurationProvider
     : public shortcut_customization::mojom::AcceleratorConfigurationProvider,
       public ui::InputDeviceEventObserver,
@@ -37,9 +33,7 @@
       mojom::AcceleratorSource,
       std::map<AcceleratorActionId, std::vector<ui::Accelerator>>>;
 
-  explicit AcceleratorConfigurationProvider(
-      std::unique_ptr<ShortcutCustomizationDelegate>
-          shortcut_customization_delegate);
+  AcceleratorConfigurationProvider();
   AcceleratorConfigurationProvider(const AcceleratorConfigurationProvider&) =
       delete;
   AcceleratorConfigurationProvider& operator=(
@@ -123,11 +117,6 @@
   mojo::Remote<shortcut_customization::mojom::AcceleratorsUpdatedObserver>
       accelerators_updated_observers_;
 
-  // Provides browser functionality from //chrome to the Shortcut Customization
-  // UI.
-  std::unique_ptr<ShortcutCustomizationDelegate>
-      shortcut_customization_delegate_;
-
   base::WeakPtrFactory<AcceleratorConfigurationProvider> weak_ptr_factory_{
       this};
 };
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
index 74e78f2..1abf38f 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider_unittest.cc
@@ -17,7 +17,6 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h"
 #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h"
 #include "base/bind.h"
 #include "base/callback_forward.h"
@@ -165,14 +164,6 @@
 
 namespace shortcut_ui {
 
-class FakeShortcutCustomizationDelegate : public ShortcutCustomizationDelegate {
- public:
-  FakeShortcutCustomizationDelegate() = default;
-  ~FakeShortcutCustomizationDelegate() override = default;
-
-  PrefService* GetPrefService() override { return nullptr; }
-};
-
 class AcceleratorConfigurationProviderTest : public AshTestBase {
  public:
   AcceleratorConfigurationProviderTest() = default;
@@ -212,8 +203,7 @@
     AshTestSuite::LoadTestResources();
     AshTestBase::SetUp();
 
-    provider_ = std::make_unique<AcceleratorConfigurationProvider>(
-        std::make_unique<FakeShortcutCustomizationDelegate>());
+    provider_ = std::make_unique<AcceleratorConfigurationProvider>();
     base::RunLoop().RunUntilIdle();
   }
 
diff --git a/ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h b/ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h
deleted file mode 100644
index cb0597d..0000000
--- a/ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_SHORTCUT_CUSTOMIZATION_DELEGATE_H_
-#define ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_SHORTCUT_CUSTOMIZATION_DELEGATE_H_
-
-#include "components/prefs/pref_service.h"
-
-namespace ash::shortcut_ui {
-
-// A delegate which exposes browser functionality from //chrome to the Shortcut
-// Customization UI.
-class ShortcutCustomizationDelegate {
- public:
-  virtual ~ShortcutCustomizationDelegate() = default;
-
-  // Get the pref service.
-  virtual PrefService* GetPrefService() = 0;
-};
-
-}  // namespace ash::shortcut_ui
-
-#endif  // ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_BACKEND_SHORTCUT_CUSTOMIZATION_DELEGATE_H_
\ No newline at end of file
diff --git a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc
index 8ef7fdc0..22168dab 100644
--- a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc
+++ b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc
@@ -10,7 +10,6 @@
 #include "ash/webui/grit/ash_shortcut_customization_app_resources.h"
 #include "ash/webui/grit/ash_shortcut_customization_app_resources_map.h"
 #include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h"
-#include "ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h"
 #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h"
 #include "ash/webui/shortcut_customization_ui/url_constants.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
@@ -55,10 +54,7 @@
 
 }  // namespace
 
-ShortcutCustomizationAppUI::ShortcutCustomizationAppUI(
-    content::WebUI* web_ui,
-    std::unique_ptr<shortcut_ui::ShortcutCustomizationDelegate>
-        shortcut_customization_delegate)
+ShortcutCustomizationAppUI::ShortcutCustomizationAppUI(content::WebUI* web_ui)
     : ui::MojoWebUIController(web_ui) {
   content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
       web_ui->GetWebContents()->GetBrowserContext(),
@@ -79,10 +75,7 @@
 
   AddFeatureFlags(source);
 
-  // TODO(longbowei): Use GetPrefService() to get pref_service and
-  // add it as a param for AcceleratorConfigurationProvider.
-  provider_ = std::make_unique<shortcut_ui::AcceleratorConfigurationProvider>(
-      std::move(shortcut_customization_delegate));
+  provider_ = std::make_unique<shortcut_ui::AcceleratorConfigurationProvider>();
 }
 
 ShortcutCustomizationAppUI::~ShortcutCustomizationAppUI() = default;
diff --git a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h
index 45ee5ffa..b3753ef 100644
--- a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h
+++ b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h
@@ -20,29 +20,20 @@
 
 namespace ash {
 
-namespace shortcut_ui {
-class ShortcutCustomizationDelegate;
-}  // namespace shortcut_ui
-
 class ShortcutCustomizationAppUI;
 
 // The WebUIConfig for chrome://shortcut-customization.
 class ShortcutCustomizationAppUIConfig
     : public SystemWebAppUIConfig<ShortcutCustomizationAppUI> {
  public:
-  explicit ShortcutCustomizationAppUIConfig(
-      SystemWebAppUIConfig::CreateWebUIControllerFunc create_controller_func)
+  ShortcutCustomizationAppUIConfig()
       : SystemWebAppUIConfig(kChromeUIShortcutCustomizationAppHost,
-                             SystemWebAppType::SHORTCUT_CUSTOMIZATION,
-                             create_controller_func) {}
+                             SystemWebAppType::SHORTCUT_CUSTOMIZATION) {}
 };
 
 class ShortcutCustomizationAppUI : public ui::MojoWebUIController {
  public:
-  ShortcutCustomizationAppUI(
-      content::WebUI* web_ui,
-      std::unique_ptr<shortcut_ui::ShortcutCustomizationDelegate>
-          shortcut_customization_delegate);
+  explicit ShortcutCustomizationAppUI(content::WebUI* web_ui);
   ShortcutCustomizationAppUI(const ShortcutCustomizationAppUI&) = delete;
   ShortcutCustomizationAppUI& operator=(const ShortcutCustomizationAppUI&) =
       delete;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 217d4ed2..1484cc6 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3366,6 +3366,7 @@
     "test/gmock_callback_support_unittest.cc",
     "test/gmock_move_support_unittest.cc",
     "test/gtest_links_unittest.cc",
+    "test/gtest_tags_unittest.cc",
     "test/gtest_xml_unittest_result_printer_unittest.cc",
     "test/launcher/test_launcher_unittest.cc",
     "test/launcher/test_results_tracker_unittest.cc",
@@ -3683,6 +3684,7 @@
       "process/process_unittest.cc",
       "process/process_util_unittest.cc",
       "test/gtest_links_unittest.cc",
+      "test/gtest_tags_unittest.cc",
       "test/gtest_xml_unittest_result_printer_unittest.cc",
       "test/launcher/test_launcher_unittest.cc",
       "test/launcher/test_results_tracker_unittest.cc",
@@ -3715,6 +3717,7 @@
       "sync_socket_unittest.cc",
       "synchronization/waitable_event_watcher_unittest.cc",
       "test/gtest_links_unittest.cc",
+      "test/gtest_tags_unittest.cc",
       "test/gtest_xml_unittest_result_printer_unittest.cc",
       "test/launcher/test_launcher_unittest.cc",
       "test/launcher/test_results_tracker_unittest.cc",
diff --git a/base/allocator/dispatcher/tls.h b/base/allocator/dispatcher/tls.h
index 1f7ba3d..4f460534 100644
--- a/base/allocator/dispatcher/tls.h
+++ b/base/allocator/dispatcher/tls.h
@@ -20,7 +20,6 @@
 #include "base/check.h"
 #include "base/compiler_specific.h"
 #include "base/dcheck_is_on.h"
-#include "base/immediate_crash.h"
 
 #include <algorithm>
 #include <atomic>
@@ -206,9 +205,7 @@
 
     if (UNLIKELY(slot == nullptr)) {
       slot = FindAndAllocateFreeSlot(root_.load(std::memory_order_relaxed));
-      if (!tls_system.SetThreadSpecificData(slot)) {
-        IMMEDIATE_CRASH();
-      }
+      CHECK(tls_system.SetThreadSpecificData(slot));
 
       // Reset the content to wipe out any previous data.
       slot->item = {};
@@ -308,11 +305,7 @@
     // SingleSlot and reset the is_used flag.
     auto* const slot = static_cast<SingleSlot*>(data);
 
-#if DCHECK_IS_ON()
-    if (!(slot && slot->is_used.test_and_set())) {
-      IMMEDIATE_CRASH();
-    }
-#endif
+    DCHECK(slot && slot->is_used.test_and_set());
 
     slot->is_used.clear(std::memory_order_relaxed);
   }
@@ -332,9 +325,7 @@
     void* const uninitialized_memory =
         dereference(allocator_).AllocateMemory(sizeof(Chunk));
 
-    if (uninitialized_memory == nullptr) {
-      IMMEDIATE_CRASH();
-    }
+    CHECK(uninitialized_memory);
 
     return new (uninitialized_memory) Chunk{};
   }
@@ -342,10 +333,8 @@
   void FreeAndDeallocateChunkForTesting(Chunk* chunk_to_erase) {
     chunk_to_erase->~Chunk();
 
-    if (!dereference(allocator_)
-             .FreeMemoryForTesting(chunk_to_erase, sizeof(Chunk))) {
-      IMMEDIATE_CRASH();
-    }
+    CHECK(dereference(allocator_)
+              .FreeMemoryForTesting(chunk_to_erase, sizeof(Chunk)));
   }
 
   // Find a free slot in the passed chunk, reserve it and return it to the
diff --git a/base/files/file.h b/base/files/file.h
index c252375..b09bdf7 100644
--- a/base/files/file.h
+++ b/base/files/file.h
@@ -50,7 +50,7 @@
   // a file.
   // FLAG_(WRITE|APPEND) are mutually exclusive. This is so that APPEND behavior
   // will be consistent with O_APPEND on POSIX.
-  enum Flags {
+  enum Flags : uint32_t {
     FLAG_OPEN = 1 << 0,            // Opens a file, only if it exists.
     FLAG_CREATE = 1 << 1,          // Creates a new file, only if it does not
                                    // already exist.
diff --git a/base/immediate_crash.h b/base/immediate_crash.h
index 6f7abeb9..2bee553 100644
--- a/base/immediate_crash.h
+++ b/base/immediate_crash.h
@@ -149,9 +149,4 @@
 
 }  // namespace base
 
-// TODO(pbos): Migrate callers to base::ImmediateCrash(). This should be done
-// within a week or two so if you see this at or after November 2022 please ping
-// me.
-#define IMMEDIATE_CRASH() base::ImmediateCrash()
-
 #endif  // BASE_IMMEDIATE_CRASH_H_
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
index 952bdc6d..6cfe45cb 100644
--- a/base/logging_unittest.cc
+++ b/base/logging_unittest.cc
@@ -427,14 +427,14 @@
 // is lower. Furthermore, since the Fuchsia implementation uses threads, it is
 // not possible to rely on an implementation of CHECK that calls abort(), which
 // takes down the whole process, preventing the thread exception handler from
-// handling the exception. DO_CHECK here falls back on IMMEDIATE_CRASH() in
+// handling the exception. DO_CHECK here falls back on base::ImmediateCrash() in
 // non-official builds, to catch regressions earlier in the CQ.
 #if !CHECK_WILL_STREAM()
 #define DO_CHECK CHECK
 #else
-#define DO_CHECK(cond) \
-  if (!(cond)) {       \
-    IMMEDIATE_CRASH(); \
+#define DO_CHECK(cond)      \
+  if (!(cond)) {            \
+    base::ImmediateCrash(); \
   }
 #endif
 
@@ -600,9 +600,10 @@
 #if !CHECK_WILL_STREAM()
 #define DO_CHECK CHECK
 #else
-#define DO_CHECK(cond) \
-  if (!(cond))         \
-  IMMEDIATE_CRASH()
+#define DO_CHECK(cond)      \
+  if (!(cond)) {            \
+    base::ImmediateCrash(); \
+  }
 #endif
 
 void CrashChildMain(int death_location) {
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc
index 052bfa2..8cef744 100644
--- a/base/profiler/stack_sampling_profiler.cc
+++ b/base/profiler/stack_sampling_profiler.cc
@@ -760,12 +760,13 @@
 }
 
 // static
-// The profiler is currently supported for Windows x64, macOS, iOS 64-bit, and
-// Android ARM32.
+// The profiler is currently supported for Windows x64, macOS, iOS 64-bit,
+// Android ARM32, and Android ARM64.
 bool StackSamplingProfiler::IsSupportedForCurrentPlatform() {
-#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_64)) || BUILDFLAG(IS_MAC) ||  \
-    (BUILDFLAG(IS_IOS) && defined(ARCH_CPU_64_BITS)) ||                      \
-    (BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE))
+#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_64)) || BUILDFLAG(IS_MAC) || \
+    (BUILDFLAG(IS_IOS) && defined(ARCH_CPU_64_BITS)) ||                     \
+    (BUILDFLAG(IS_ANDROID) &&                                               \
+     (BUILDFLAG(ENABLE_ARM_CFI_TABLE) || defined(ARCH_CPU_ARM64)))
 #if BUILDFLAG(IS_WIN)
   // Do not start the profiler when Application Verifier is in use; running them
   // simultaneously can cause crashes and has no known use case.
diff --git a/base/task/thread_pool/task_tracker.h b/base/task/thread_pool/task_tracker.h
index 6b53bcc..ae758b8 100644
--- a/base/task/thread_pool/task_tracker.h
+++ b/base/task/thread_pool/task_tracker.h
@@ -216,9 +216,6 @@
 
   TaskAnnotator task_annotator_;
 
-  // Suffix for histograms recorded by this TaskTracker.
-  const std::string histogram_label_;
-
   // Indicates whether logging information about TaskPriority::BEST_EFFORT tasks
   // was enabled with a command line switch.
   const bool has_log_best_effort_tasks_switch_;
diff --git a/base/task/thread_pool/thread_group_impl.cc b/base/task/thread_pool/thread_group_impl.cc
index 4acfac2..0958a705 100644
--- a/base/task/thread_pool/thread_group_impl.cc
+++ b/base/task/thread_pool/thread_group_impl.cc
@@ -20,6 +20,7 @@
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/numerics/clamped_math.h"
 #include "base/ranges/algorithm.h"
 #include "base/sequence_token.h"
@@ -211,6 +212,7 @@
   void DidProcessTask(RegisteredTaskSource task_source) override;
   TimeDelta GetSleepTimeout() override;
   void OnMainExit(WorkerThread* worker) override;
+  void RecordUnnecessaryWakeup() override;
 
   // BlockingObserver:
   void BlockingStarted(BlockingType blocking_type) override;
@@ -334,6 +336,7 @@
                                  TrackedRef<TaskTracker> task_tracker,
                                  TrackedRef<Delegate> delegate)
     : ThreadGroup(std::move(task_tracker), std::move(delegate)),
+      histogram_label_(histogram_label),
       thread_group_label_(thread_group_label),
       thread_type_hint_(thread_type_hint),
       idle_workers_set_cv_for_testing_(lock_.CreateConditionVariable()),
@@ -775,6 +778,15 @@
     outer_->num_workers_cleaned_up_for_testing_cv_->Signal();
 }
 
+void ThreadGroupImpl::WorkerThreadDelegateImpl::RecordUnnecessaryWakeup() {
+  base::BooleanHistogram::FactoryGet(
+      std::string("ThreadPool.UnnecessaryWakeup.") + outer_->histogram_label_,
+      base::Histogram::kUmaTargetedHistogramFlag)
+      ->Add(true);
+
+  TRACE_EVENT_INSTANT("wakeup.flow", "ThreadPool.UnnecessaryWakeup");
+}
+
 void ThreadGroupImpl::WorkerThreadDelegateImpl::BlockingStarted(
     BlockingType blocking_type) {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
diff --git a/base/task/thread_pool/thread_group_impl.h b/base/task/thread_pool/thread_group_impl.h
index adf45d0..e9e18d7 100644
--- a/base/task/thread_pool/thread_group_impl.h
+++ b/base/task/thread_pool/thread_group_impl.h
@@ -274,6 +274,7 @@
     return initialized_in_start_;
   }
 
+  const std::string histogram_label_;
   const std::string thread_group_label_;
   const ThreadType thread_type_hint_;
 
diff --git a/base/task/thread_pool/worker_thread.cc b/base/task/thread_pool/worker_thread.cc
index 35c4113..7386f276 100644
--- a/base/task/thread_pool/worker_thread.cc
+++ b/base/task/thread_pool/worker_thread.cc
@@ -428,7 +428,7 @@
     delegate_->WaitForWork(&wake_up_event_);
     TRACE_EVENT_BEGIN0("base", "WorkerThread active");
   }
-
+  bool got_work_this_wakeup = false;
   while (!ShouldExit()) {
 #if BUILDFLAG(IS_APPLE)
     mac::ScopedNSAutoreleasePool autorelease_pool;
@@ -446,15 +446,23 @@
       if (ShouldExit())
         break;
 
+      // If this is the first time we called GetWork and the worker's still
+      // alive, record that this is an unnecessary wakeup.
+      if (!got_work_this_wakeup)
+        delegate_->RecordUnnecessaryWakeup();
+
       TRACE_EVENT_END0("base", "WorkerThread active");
       // TODO(crbug.com/1021571): Remove this once fixed.
       PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
       hang_watch_scope.reset();
       delegate_->WaitForWork(&wake_up_event_);
       TRACE_EVENT_BEGIN0("base", "WorkerThread active");
+      got_work_this_wakeup = false;
       continue;
     }
 
+    got_work_this_wakeup = true;
+
     // Alias pointer for investigation of memory corruption. crbug.com/1218384
     TaskSource* task_source_before_run = task_source.get();
     base::debug::Alias(&task_source_before_run);
diff --git a/base/task/thread_pool/worker_thread.h b/base/task/thread_pool/worker_thread.h
index 28104df7..9b65469c 100644
--- a/base/task/thread_pool/worker_thread.h
+++ b/base/task/thread_pool/worker_thread.h
@@ -93,6 +93,10 @@
     // TaskTracker after calling OnMainExit() on the Delegate.
     virtual void OnMainExit(WorkerThread* worker) {}
 
+    // Called by a WorkerThread when it is woken up without any work being
+    // available for it to run.
+    virtual void RecordUnnecessaryWakeup() {}
+
     static constexpr TimeDelta kPurgeThreadCacheIdleDelay = Seconds(1);
   };
 
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index e5f88a2..ca4a051 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -52,6 +52,8 @@
     "gmock_move_support.h",
     "gtest_links.cc",
     "gtest_links.h",
+    "gtest_tags.cc",
+    "gtest_tags.h",
     "gtest_util.cc",
     "gtest_util.h",
     "gtest_xml_unittest_result_printer.cc",
diff --git a/base/test/gtest_tags.cc b/base/test/gtest_tags.cc
new file mode 100644
index 0000000..69c5a0f6
--- /dev/null
+++ b/base/test/gtest_tags.cc
@@ -0,0 +1,18 @@
+// 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 "base/test/gtest_tags.h"
+
+#include "base/check.h"
+#include "base/strings/string_util.h"
+#include "base/test/gtest_xml_unittest_result_printer.h"
+
+namespace base {
+
+void AddTagToTestResult(const std::string& name, const std::string& value) {
+  DCHECK(!name.empty()) << name << " is an empty name";
+  XmlUnitTestResultPrinter::Get()->AddTag(name, value);
+}
+
+}  // namespace base
diff --git a/base/test/gtest_tags.h b/base/test/gtest_tags.h
new file mode 100644
index 0000000..b864f4f
--- /dev/null
+++ b/base/test/gtest_tags.h
@@ -0,0 +1,24 @@
+// 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 BASE_TEST_GTEST_TAGS_H_
+#define BASE_TEST_GTEST_TAGS_H_
+
+#include <string>
+
+namespace base {
+
+// Add a tag in the gtest xml output.
+// Must be called on the thread where gtest is running the test case.
+// Only works on desktop, which uses the test launcher.
+// A test can call this function when the test generates a tag and save it
+// as part of the test result.
+// Example: AddTagToTestResult("tag_name", "tag_value")
+// `name` is the tag name, should not be empty.
+// `value` the actual tag value.
+void AddTagToTestResult(const std::string& name, const std::string& value);
+
+}  // namespace base
+
+#endif  // BASE_TEST_GTEST_TAGS_H_
diff --git a/base/test/gtest_tags_unittest.cc b/base/test/gtest_tags_unittest.cc
new file mode 100644
index 0000000..e19606c3
--- /dev/null
+++ b/base/test/gtest_tags_unittest.cc
@@ -0,0 +1,20 @@
+// 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 "base/test/gtest_tags.h"
+
+#include "base/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(GtestTagsTest, AddInvalidName) {
+  EXPECT_DCHECK_DEATH(AddTagToTestResult("", "value"));
+}
+
+TEST(GtestTagsTest, AddValidTag) {
+  AddTagToTestResult("name", "value");
+}
+
+}  // namespace base
diff --git a/base/test/gtest_xml_unittest_result_printer.cc b/base/test/gtest_xml_unittest_result_printer.cc
index dc3e018..dbe37d7 100644
--- a/base/test/gtest_xml_unittest_result_printer.cc
+++ b/base/test/gtest_xml_unittest_result_printer.cc
@@ -23,14 +23,14 @@
     "Test part results limit exceeded. Use --test-launcher-test-part-limit to "
     "increase or disable limit.";
 
-std::string EscapeUrl(const std::string& url) {
-  std::string escaped_url;
-  ReplaceChars(url, "&", "&amp;", &escaped_url);
-  ReplaceChars(escaped_url, "<", "&lt;", &escaped_url);
-  ReplaceChars(escaped_url, ">", "&gt;", &escaped_url);
-  ReplaceChars(escaped_url, "'", "&apos;", &escaped_url);
-  ReplaceChars(escaped_url, "\"", "&quot;", &escaped_url);
-  return escaped_url;
+std::string EscapeString(const std::string& input_string) {
+  std::string escaped_string;
+  ReplaceChars(input_string, "&", "&amp;", &escaped_string);
+  ReplaceChars(escaped_string, "<", "&lt;", &escaped_string);
+  ReplaceChars(escaped_string, ">", "&gt;", &escaped_string);
+  ReplaceChars(escaped_string, "'", "&apos;", &escaped_string);
+  ReplaceChars(escaped_string, "\"", "&quot;", &escaped_string);
+  return escaped_string;
 }
 
 }  // namespace
@@ -63,7 +63,7 @@
   DCHECK(output_file_);
   DCHECK(!open_failed_);
   // Escape the url so it's safe to save in xml file.
-  const std::string escaped_url = EscapeUrl(url);
+  const std::string escaped_url = EscapeString(url);
   const testing::TestInfo* info =
       testing::UnitTest::GetInstance()->current_test_info();
   // When this function is not called from a gtest test body, it will
@@ -80,6 +80,28 @@
   fflush(output_file_);
 }
 
+void XmlUnitTestResultPrinter::AddTag(const std::string& name,
+                                      const std::string& value) {
+  DCHECK(output_file_);
+  DCHECK(!open_failed_);
+  // Escape the value so it's safe to save in xml file.
+  const std::string escaped_value = EscapeString(value);
+  const testing::TestInfo* info =
+      testing::UnitTest::GetInstance()->current_test_info();
+  // When this function is not called from a gtest test body, it will
+  // return null. E.g. call from Chromium itself or from test launcher.
+  // But when that happens, the previous two DCHECK won't pass. So in
+  // theory it should not be possible to reach here and the info is null.
+  DCHECK(info);
+
+  fprintf(output_file_.get(),
+          "    <tag name=\"%s\" classname=\"%s\" "
+          "tag_name=\"%s\">%s</tag>\n",
+          info->name(), info->test_case_name(), name.c_str(),
+          escaped_value.c_str());
+  fflush(output_file_);
+}
+
 bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) {
   DCHECK(!output_file_);
   output_file_ = OpenFile(output_file_path, "w");
diff --git a/base/test/gtest_xml_unittest_result_printer.h b/base/test/gtest_xml_unittest_result_printer.h
index e0ef662e..d659892 100644
--- a/base/test/gtest_xml_unittest_result_printer.h
+++ b/base/test/gtest_xml_unittest_result_printer.h
@@ -33,6 +33,11 @@
   // explanation and usage.
   void AddLink(const std::string& name, const std::string& url);
 
+  // Add tag in the gtest xml output.
+  // Please see AddTagToTestResult in gtest_tags.h for detailed
+  // explanation and usage.
+  void AddTag(const std::string& name, const std::string& value);
+
   // Must be called before adding as a listener. Returns true on success.
   [[nodiscard]] bool Initialize(const FilePath& output_file_path);
 
diff --git a/base/test/gtest_xml_unittest_result_printer_unittest.cc b/base/test/gtest_xml_unittest_result_printer_unittest.cc
index 17315ac..05eb5e8e 100644
--- a/base/test/gtest_xml_unittest_result_printer_unittest.cc
+++ b/base/test/gtest_xml_unittest_result_printer_unittest.cc
@@ -49,6 +49,23 @@
       << expected_content << " not found in " << content;
 }
 
+TEST(XmlUnitTestResultPrinterTest, TagInXmlFile) {
+  XmlUnitTestResultPrinter::Get()->AddTag("tag_name", "tag_value");
+  std::string file_path =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kTestLauncherOutput);
+  std::string content;
+  ASSERT_TRUE(
+      base::ReadFileToString(FilePath::FromUTF8Unsafe(file_path), &content));
+  std::string expected_content =
+      base::StrCat({"<tag name=\"TagInXmlFile\" "
+                    "classname=\"XmlUnitTestResultPrinterTest\" "
+                    "tag_name=\"tag_name\">",
+                    "tag_value", "</tag>"});
+  EXPECT_TRUE(content.find(expected_content) != std::string::npos)
+      << expected_content << " not found in " << content;
+}
+
 class XmlUnitTestResultPrinterTimestampTest : public ::testing::Test {
  public:
   static void TearDownTestSuite() {
diff --git a/base/test/gtest_xml_util.cc b/base/test/gtest_xml_util.cc
index 32a7800..6c8b950 100644
--- a/base/test/gtest_xml_util.cc
+++ b/base/test/gtest_xml_util.cc
@@ -34,6 +34,17 @@
   std::string value;
 };
 
+struct Tag {
+  // The name of the test case.
+  std::string name;
+  // The name of the classname of the test.
+  std::string classname;
+  // The name of the tag.
+  std::string tag_name;
+  // The value of the tag.
+  std::string tag_value;
+};
+
 bool ProcessGTestOutput(const base::FilePath& output_file,
                         std::vector<TestResult>* results,
                         bool* crashed) {
@@ -60,6 +71,8 @@
 
   std::vector<Property> properties;
 
+  std::vector<Tag> tags;
+
   while (xml_reader.Read()) {
     xml_reader.SkipToElement();
     std::string node_name(xml_reader.NodeName());
@@ -166,7 +179,13 @@
           for (const Property& property : properties) {
             result.AddProperty(property.name, property.value);
           }
-          links.clear();
+          properties.clear();
+          for (const Tag& tag : tags) {
+            if (tag.name == test_name && tag.classname == test_case_name) {
+              result.AddTag(tag.tag_name, tag.tag_value);
+            }
+          }
+          tags.clear();
           results->push_back(result);
         } else if (node_name == "link" && !xml_reader.IsClosingElement()) {
           Link link;
@@ -181,6 +200,19 @@
           links.push_back(link);
         } else if (node_name == "link" && xml_reader.IsClosingElement()) {
           // Deliberately empty.
+        } else if (node_name == "tag" && !xml_reader.IsClosingElement()) {
+          Tag tag;
+          if (!xml_reader.NodeAttribute("name", &tag.name))
+            return false;
+          if (!xml_reader.NodeAttribute("classname", &tag.classname))
+            return false;
+          if (!xml_reader.NodeAttribute("tag_name", &tag.tag_name))
+            return false;
+          if (!xml_reader.ReadElementContent(&tag.tag_value))
+            return false;
+          tags.push_back(tag);
+        } else if (node_name == "tag" && xml_reader.IsClosingElement()) {
+          // Deliberately empty.
         } else if (node_name == "properties" &&
                    !xml_reader.IsClosingElement()) {
           // Deliberately empty, begin of the test properties.
diff --git a/base/test/launcher/test_result.cc b/base/test/launcher/test_result.cc
index f528926..73b63d5 100644
--- a/base/test/launcher/test_result.cc
+++ b/base/test/launcher/test_result.cc
@@ -105,6 +105,10 @@
   DCHECK(inserted) << name << " is already used as a link name. Ignoring...";
 }
 
+void TestResult::AddTag(const std::string& name, const std::string& value) {
+  tags.insert({name, value});
+}
+
 void TestResult::AddProperty(const std::string& name,
                              const std::string& value) {
   auto [it, inserted] = properties.insert({name, value});
diff --git a/base/test/launcher/test_result.h b/base/test/launcher/test_result.h
index fe4ea56a..59d073d 100644
--- a/base/test/launcher/test_result.h
+++ b/base/test/launcher/test_result.h
@@ -83,6 +83,10 @@
   // See more in gtest_links.h.
   void AddLink(const std::string& name, const std::string& url);
 
+  // Add tag in the xml output.
+  // See more in gtest_tags.h.
+  void AddTag(const std::string& name, const std::string& value);
+
   // Add property in the xml output.
   void AddProperty(const std::string& name, const std::string& value);
 
@@ -129,6 +133,9 @@
 
   // The key is property name.
   std::map<std::string, std::string> properties;
+
+  // The key is tag name.
+  std::map<std::string, std::string> tags;
 };
 
 }  // namespace base
diff --git a/base/test/launcher/test_results_tracker.cc b/base/test/launcher/test_results_tracker.cc
index 9801c8d..ea2aa1e 100644
--- a/base/test/launcher/test_results_tracker.cc
+++ b/base/test/launcher/test_results_tracker.cc
@@ -508,6 +508,15 @@
           }
           test_result_value.Set("links", std::move(links));
         }
+        if (!test_result.tags.empty()) {
+          Value::Dict tags;
+          for (const auto& tag : test_result.tags) {
+            Value::Dict tag_info;
+            tag_info.Set("value", tag.second);
+            tags.SetByDottedPath(tag.first, std::move(tag_info));
+          }
+          test_result_value.Set("tags", std::move(tags));
+        }
         if (!test_result.properties.empty()) {
           Value::Dict properties;
           for (const auto& property : test_result.properties) {
diff --git a/base/test/launcher/test_results_tracker.h b/base/test/launcher/test_results_tracker.h
index 81fef30..a304d7b 100644
--- a/base/test/launcher/test_results_tracker.h
+++ b/base/test/launcher/test_results_tracker.h
@@ -108,6 +108,8 @@
   FRIEND_TEST_ALL_PREFIXES(TestResultsTrackerTest,
                            SaveSummaryAsJSONWithLinkInResult);
   FRIEND_TEST_ALL_PREFIXES(TestResultsTrackerTest,
+                           SaveSummaryAsJSONWithTagInResult);
+  FRIEND_TEST_ALL_PREFIXES(TestResultsTrackerTest,
                            SaveSummaryAsJSONWithPropertyInResult);
   FRIEND_TEST_ALL_PREFIXES(TestResultsTrackerTest,
                            SaveSummaryAsJSONWithOutTimestampInResult);
diff --git a/base/test/launcher/test_results_tracker_unittest.cc b/base/test/launcher/test_results_tracker_unittest.cc
index 67464c2..c2e47c18 100644
--- a/base/test/launcher/test_results_tracker_unittest.cc
+++ b/base/test/launcher/test_results_tracker_unittest.cc
@@ -34,6 +34,25 @@
       << expected_content << " not found in " << content;
 }
 
+TEST(TestResultsTrackerTest, SaveSummaryAsJSONWithTagInResult) {
+  TestResultsTracker tracker;
+  TestResult result;
+  result.AddTag("tag_name", "tag_value");
+  TestResultsTracker::AggregateTestResult aggregate_result;
+  aggregate_result.test_results.push_back(result);
+  tracker.per_iteration_data_.push_back({});
+  tracker.per_iteration_data_.back().results["dummy"] = aggregate_result;
+  FilePath temp_file;
+  CreateTemporaryFile(&temp_file);
+  ASSERT_TRUE(tracker.SaveSummaryAsJSON(temp_file, std::vector<std::string>()));
+  std::string content;
+  ASSERT_TRUE(ReadFileToString(temp_file, &content));
+  std::string expected_content = R"raw("tags":{"tag_name":)raw"
+                                 R"raw({"value":"tag_value"}})raw";
+  EXPECT_TRUE(content.find(expected_content) != std::string::npos)
+      << expected_content << " not found in " << content;
+}
+
 TEST(TestResultsTrackerTest, SaveSummaryAsJSONWithPropertyInResult) {
   TestResultsTracker tracker;
   TestResult result;
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
index 236085e..387c90a6 100644
--- a/base/threading/thread_unittest.cc
+++ b/base/threading/thread_unittest.cc
@@ -296,13 +296,15 @@
 
   Thread b("NonOwningThread");
   b.Start();
-  EXPECT_DCHECK_DEATH({
-    // Stopping |a| on |b| isn't allowed.
-    b.task_runner()->PostTask(
-        FROM_HERE, base::BindOnce(&Thread::Stop, base::Unretained(&a)));
-    // Block here so the DCHECK on |b| always happens in this scope.
-    base::PlatformThread::Sleep(base::TimeDelta::Max());
-  });
+  EXPECT_DCHECK_DEATH_WITH(
+      {
+        // Stopping |a| on |b| isn't allowed.
+        b.task_runner()->PostTask(
+            FROM_HERE, base::BindOnce(&Thread::Stop, base::Unretained(&a)));
+        // Block here so the DCHECK on |b| always happens in this scope.
+        base::PlatformThread::Sleep(base::TimeDelta::Max());
+      },
+      "owning_sequence_checker_.CalledOnValidSequence()");
 }
 
 TEST_F(ThreadTest, TransferOwnershipAndStop) {
diff --git a/build/fuchsia/PRESUBMIT.py b/build/fuchsia/PRESUBMIT.py
index f8c7df2..df2baa9 100644
--- a/build/fuchsia/PRESUBMIT.py
+++ b/build/fuchsia/PRESUBMIT.py
@@ -26,7 +26,6 @@
       J('device_target_test.py'),
       J('gcs_download_test.py'),
       J('update_images_test.py'),
-      J('update_product_bundles_test.py'),
       J('update_sdk_test.py'),
   ]
 
diff --git a/build/fuchsia/update_product_bundles.py b/build/fuchsia/update_product_bundles.py
index 1cde6c4..d531ac6 100755
--- a/build/fuchsia/update_product_bundles.py
+++ b/build/fuchsia/update_product_bundles.py
@@ -10,7 +10,6 @@
 import json
 import logging
 import os
-import re
 import subprocess
 import sys
 
@@ -40,14 +39,6 @@
 }
 
 
-_PRODUCT_BUNDLE_FIX_INSTRUCTIONS = (
-    'This could be because an earlier version of the product bundle was not '
-    'properly removed. Run |ffx product-bundle list| and |ffx repository list|,'
-    ' remove the available product bundles listed using '
-    '|ffx product-bundle remove| and |ffx repository remove|, '
-    f'remove the directory {common.IMAGES_ROOT} and rerun hooks/this script.')
-
-
 # TODO(crbug/1361089): Remove when the old scripts have been deprecated.
 def convert_to_product_bundle(images_list):
   """Convert image names in the SDK to product bundle names."""
@@ -72,195 +63,22 @@
     return json.load(f)['id']
 
 
-def remove_repositories(repo_names_to_remove, ffx_runner):
-  """Removes given repos from repo list.
-  Repo MUST be present in list to succeed.
-
-  Args:
-    repo_names_to_remove: List of repo names (as strings) to remove.
-    ffx_runner: ffx_session.FfxRunner instance to run the command.
-  """
-  for repo_name in repo_names_to_remove:
-    ffx_runner.run_ffx(('repository', 'remove', repo_name), check=True)
-
-
-def get_repositories(ffx_runner):
-  """Lists repositories that are available on disk.
-
-  Also prunes repositories that are listed, but do not have an actual packages
-  directory.
-
-  Args:
-    ffx_runner: An FfxRunner instance.
-
-  Returns:
-    List of dictionaries containing info about the repositories. They have the
-    following structure:
-    {
-      'name': <repo name>,
-      'spec': {
-        'type': <type, usually pm>,
-        'path': <path to packages directory>
-      },
-    }
-  """
-
-  repos = json.loads(
-      ffx_runner.run_ffx(('--machine', 'json', 'repository', 'list')).strip())
-  to_prune = set()
-  sdk_root_abspath = os.path.abspath(os.path.dirname(common.SDK_ROOT))
-  for repo in repos:
-    # Confirm the path actually exists. If not, prune list.
-    # Also assert the product-bundle repository is for the current repo
-    # (IE within the same directory).
-    if not os.path.exists(repo['spec']['path']):
-      to_prune.add(repo['name'])
-
-    if not repo['spec']['path'].startswith(sdk_root_abspath):
-      to_prune.add(repo['name'])
-
-  repos = [repo for repo in repos if repo['name'] not in to_prune]
-
-  remove_repositories(to_prune, ffx_runner)
-  return repos
-
-
-def update_repositories_list(ffx_runner):
-  """Used to prune stale repositories."""
-  get_repositories(ffx_runner)
-
-
-def remove_product_bundle(product_bundle, ffx_runner):
-  """Removes product-bundle given."""
-  ffx_runner.run_ffx(('product-bundle', 'remove', '-f', product_bundle))
-
-
-def get_product_bundle_urls(ffx_runner):
-  """Retrieves URLs of available product-bundles.
-
-  Args:
-    ffx_runner: An FfxRunner instance.
-
-  Returns:
-    List of dictionaries of structure, indicating whether the product-bundle
-    has been downloaded.
-    {
-      'url': <GCS path of product-bundle>,
-      'downloaded': <True|False>
-    }
-  """
-  # TODO(fxb/115328): Replaces with JSON API when available.
-  bundles = ffx_runner.run_ffx(('product-bundle', 'list'), check=True)
-
-  urls = [
-      line.strip() for line in bundles.splitlines() if 'gs://fuchsia' in line
-  ]
-  structured_urls = []
-  for url in urls:
-    downloaded = False
-    if '*' in url:
-      downloaded = True
-      url = url.split(' ')[1]
-    structured_urls.append({'downloaded': downloaded, 'url': url.strip()})
-  return structured_urls
-
-
-def keep_product_bundles_by_sdk_version(sdk_version, ffx_runner):
-  """Prunes product bundles not containing the sdk_version given."""
-  urls = get_product_bundle_urls(ffx_runner)
-  for url in urls:
-    if url['downloaded'] and sdk_version not in url['url']:
-      remove_product_bundle(url['url'], ffx_runner)
-
-
-def get_product_bundles(ffx_runner):
-  """Lists all downloaded product-bundles for the given SDK.
-
-  Cross-references the repositories with downloaded packages and the stated
-  downloaded product-bundles to validate whether or not a product-bundle is
-  present. Prunes invalid product-bundles with each call as well.
-
-  Args:
-    ffx_runner: An FfxRunner instance.
-
-  Returns:
-    List of strings of product-bundle names downloaded and that FFX is aware
-    of.
-  """
-  downloaded_bundles = []
-
-  for url in get_product_bundle_urls(ffx_runner):
-    if url['downloaded']:
-      # The product is separated by a #
-      product = url['url'].split('#')
-      downloaded_bundles.append(product[1])
-
-  # For each downloaded bundle, need to verify whether ffx repository believes
-  # it exists.
-  to_prune_bundles_index = []
-  repos = get_repositories(ffx_runner)
-
-  # Some repo names do not match product-bundle names due to underscores.
-  # Normalize them both.
-  repo_names = set([repo['name'].replace('-', '_') for repo in repos])
-
-  def bundle_is_active(name):
-    # Returns True if the product-bundle named `name` is present in a package
-    # repository (assuming it is downloaded already); otherwise, removes the
-    # product-bundle and returns False.
-    if name.replace('-', '_') in repo_names:
-      return True
-
-    remove_product_bundle(name, ffx_runner)
-    return False
-
-  return list(filter(bundle_is_active, downloaded_bundles))
-
-
 def download_product_bundle(product_bundle, ffx_runner):
   """Download product bundles using the SDK."""
-  # This also updates the repository list, in case it is stale.
-  update_repositories_list(ffx_runner)
 
+  logging.info('Downloading Fuchsia product bundle %s', product_bundle)
   try:
     ffx_runner.run_ffx(('product-bundle', 'get', product_bundle))
   except subprocess.CalledProcessError as cpe:
-    logging.error('Product bundle download has failed. ' +
-                  _PRODUCT_BUNDLE_FIX_INSTRUCTIONS)
+    logging.error('Product bundle download has failed. This could be '
+                  'because an earlier version of the product bundle was not '
+                  'properly removed. Run |ffx product-bundle list|, remove '
+                  'the available product bundles listed using '
+                  '|ffx product-bundle remove|, remove the directory '
+                  f'{common.IMAGES_ROOT} and rerun hooks/this script.')
     raise
 
 
-def get_current_signature(ffx_runner):
-  """Determines the SDK version of the product-bundles associated with the SDK.
-
-  Parses this information from the URLs of the product-bundle.
-
-  Args:
-    ffx_runner: An FfxRunner instance.
-
-  Returns:
-    An SDK version string, or None if no product-bundle versions are downloaded.
-  """
-  product_bundles = get_product_bundles(ffx_runner)
-  if not product_bundles:
-    logging.warning('No product bundles - signature will default to None')
-    return None
-  product_bundle_urls = get_product_bundle_urls(ffx_runner)
-
-  # Get the numbers, hope they're the same.
-  signatures = set()
-  for bundle in product_bundle_urls:
-    m = re.search(r'/(\d+\.\d+\.\d+.\d+|\d+)/', bundle['url'])
-    assert m, 'Must have a signature in each URL'
-    signatures.add(m.group(1))
-
-  if len(signatures) > 1:
-    raise RuntimeError('Found more than one product signature. ' +
-                       _PRODUCT_BUNDLE_FIX_INSTRUCTIONS)
-
-  return next(iter(signatures)) if signatures else None
-
-
 def main():
   parser = argparse.ArgumentParser()
   parser.add_argument('--verbose',
@@ -281,8 +99,6 @@
 
   new_product_bundles = convert_to_product_bundle(
       args.product_bundles.split(','))
-  logging.info('Searching for the following product bundles: %s',
-               str(new_product_bundles))
   for pb in new_product_bundles:
     if pb not in _PRODUCT_BUNDLES:
       raise ValueError(f'{pb} is not part of the Fuchsia product bundle.')
@@ -307,42 +123,56 @@
     override_file = os.path.join(os.path.dirname(__file__), 'sdk_override.txt')
     if os.path.isfile(override_file):
       with open(override_file) as f:
-        pb_metadata = f.read().strip().split('\n')
+        pb_metadata = f.read().split('\n')
         pb_metadata.append('{sdk.root}/*.json')
       stack.enter_context(
           ffx_runner.scoped_config('pbms.metadata', json.dumps((pb_metadata))))
       logging.debug('Applied overrides')
 
+    logging.debug('Checking for current signature')
+    signature_filename = common.PRODUCT_BUNDLE_SIGNATURE_FILE
+    curr_signature = {}
+    if os.path.exists(signature_filename):
+      with open(signature_filename, 'r') as f:
+        curr_signature = json.load(f)
+      logging.debug('Loaded current signature')
+
     logging.debug('Getting new SDK hash')
     new_sdk_hash = get_hash_from_sdk()
-    keep_product_bundles_by_sdk_version(new_sdk_hash, ffx_runner)
-    logging.debug('Checking for current signature')
-    curr_signature = get_current_signature(ffx_runner)
-
-    current_images = get_product_bundles(ffx_runner)
+    new_signature = {'sdk_hash': new_sdk_hash}
 
     # If SDK versions match, remove the product bundles that are no longer
     # needed and download missing ones.
-    if curr_signature == new_sdk_hash:
-      logging.debug('Current images: %s, desired images %s',
-                    str(current_images), str(new_product_bundles))
-      for image in current_images:
-        if image not in new_product_bundles:
-          logging.debug('Removing no longer needed Fuchsia image %s' % image)
-          remove_product_bundle(image, ffx_runner)
+    if curr_signature.get('sdk_hash') == new_sdk_hash:
+      logging.debug('SDK hash has not changed')
+      new_signature['path'] = curr_signature.get('path')
+      new_product_bundle_hash = []
+
+      for image in curr_signature.get('images', []):
+        logging.debug('Checking hash for image: %s', image)
+        if image in new_product_bundles:
+          new_product_bundle_hash.append(image)
+        else:
+          logging.info('Removing no longer needed Fuchsia image %s', image)
+          ffx_runner.run_ffx(('product-bundle', 'remove', '-f', image))
 
       bundles_to_download = set(new_product_bundles) - \
-                            set(current_images)
+                            set(curr_signature.get('images', []))
       for bundle in bundles_to_download:
         logging.debug('Downloading image: %s', image)
         download_product_bundle(bundle, ffx_runner)
+      new_product_bundle_hash.extend(bundles_to_download)
+      new_signature['images'] = new_product_bundle_hash
 
+      logging.debug('Writing bundle signatures')
+      with open(signature_filename, 'w') as f:
+        f.write(json.dumps(new_signature))
       return 0
 
     # If SDK versions do not match, remove all existing product bundles
     # and download the ones required.
-    for pb in current_images:
-      remove_product_bundle(pb, ffx_runner)
+    logging.debug('Removing all images')
+    ffx_runner.run_ffx(('product-bundle', 'remove', '-a', '-f'))
 
     logging.debug('Make clean images root')
     curr_subdir = []
@@ -353,14 +183,18 @@
     for pb in new_product_bundles:
       logging.debug('Downloading bundle: %s', pb)
       download_product_bundle(pb, ffx_runner)
+    new_signature['images'] = new_product_bundles
 
-    current_pb = get_product_bundles(ffx_runner)
+    logging.debug('Writing new images signature')
+    new_subdir = os.listdir(common.IMAGES_ROOT)
+    subdir_diff = set(new_subdir) - set(curr_subdir)
+    if len(subdir_diff) != 1:
+      raise RuntimeError(f'Expected one new created subdirectory but got '
+                         '{subdir_diff} instead.')
+    new_signature['path'] = list(subdir_diff)[0]
 
-    diff = set(current_pb) - set(new_product_bundles)
-    assert set(current_pb) == set(new_product_bundles), (
-        'Failed to download expected set of product-bundles. '
-        f'Expected {new_product_bundles}, got {current_pb}')
-
+    with open(signature_filename, 'w') as f:
+      f.write(json.dumps(new_signature))
   return 0
 
 
diff --git a/build/fuchsia/update_product_bundles_test.py b/build/fuchsia/update_product_bundles_test.py
deleted file mode 100755
index 8c65e55..0000000
--- a/build/fuchsia/update_product_bundles_test.py
+++ /dev/null
@@ -1,285 +0,0 @@
-#!/usr/bin/env vpython3
-# 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 io
-import json
-import os
-import unittest
-from unittest import mock
-
-from parameterized import parameterized
-
-import common
-import ffx_session
-import update_product_bundles
-
-
-class TestUpdateProductBundles(unittest.TestCase):
-  def testConvertToProductBundleDefaultsUnknownImage(self):
-    self.assertEqual(
-        update_product_bundles.convert_to_product_bundle(['unknown-image']),
-        ['unknown-image'])
-
-  def testConvertToProductBundleWarnsDeprecated(self):
-    with self.assertLogs(level='WARNING') as logs:
-      deprecated_images = [
-          'qemu.arm64', 'qemu.x64', 'core.x64-dfv2-release',
-          'workstation_eng.chromebook-x64-release'
-      ]
-      self.assertEqual(
-          update_product_bundles.convert_to_product_bundle(deprecated_images), [
-              'terminal.qemu-arm64', 'terminal.qemu-x64', 'core.x64-dfv2',
-              'workstation_eng.chromebook-x64'
-          ])
-      for i, deprecated_image in enumerate(deprecated_images):
-        self.assertIn(f'Image name {deprecated_image} has been deprecated',
-                      logs.output[i])
-
-  @mock.patch('builtins.open')
-  @mock.patch('os.path.exists')
-  def testGetHashFromSDK(self, mock_exists, mock_open):
-    mock_open.return_value = io.StringIO(json.dumps({'id': 'foo-bar'}))
-    mock_exists.return_value = True
-
-    self.assertEqual(update_product_bundles.get_hash_from_sdk(), 'foo-bar')
-
-    manifest_file = os.path.join(common.SDK_ROOT, 'meta', 'manifest.json')
-    mock_exists.assert_called_once_with(manifest_file)
-    mock_open.assert_called_once_with(manifest_file, 'r')
-
-  @mock.patch('builtins.open')
-  @mock.patch('os.path.exists')
-  def testGetHashFromSDKRaisesErrorIfNoManifestExists(self, mock_exists,
-                                                      mock_open):
-    mock_exists.return_value = False
-
-    self.assertRaises(RuntimeError, update_product_bundles.get_hash_from_sdk)
-
-  def testRemoveRepositoriesRunsRemoveOnGivenRepos(self):
-    ffx_runner = mock.create_autospec(ffx_session.FfxRunner, instance=True)
-
-    update_product_bundles.remove_repositories(['foo', 'bar', 'fizz', 'buzz'],
-                                               ffx_runner)
-
-    ffx_runner.run_ffx.assert_has_calls([
-        mock.call(('repository', 'remove', 'foo'), check=True),
-        mock.call(('repository', 'remove', 'bar'), check=True),
-        mock.call(('repository', 'remove', 'fizz'), check=True),
-        mock.call(('repository', 'remove', 'buzz'), check=True),
-    ])
-
-  @mock.patch('os.path.exists')
-  @mock.patch('os.path.abspath')
-  def testGetRepositoriesPrunesReposThatDoNotExist(self, mock_abspath,
-                                                   mock_exists):
-    with mock.patch('common.SDK_ROOT', 'some/path'):
-      ffx_runner = mock.create_autospec(ffx_session.FfxRunner, instance=True)
-      ffx_runner.run_ffx.return_value = json.dumps([{
-          "name": "terminal.qemu-x64",
-          "spec": {
-              "type": "pm",
-              "path": "some/path/that/exists"
-          }
-      }, {
-          "name": "workstation-eng.chromebook-x64",
-          "spec": {
-              "type": "pm",
-              "path": "some/path/that/does/not/exist"
-          }
-      }])
-      mock_exists.side_effect = [True, False]
-      mock_abspath.side_effect = lambda x: x
-
-      self.assertEqual(update_product_bundles.get_repositories(ffx_runner),
-                       [{
-                           "name": "terminal.qemu-x64",
-                           "spec": {
-                               "type": "pm",
-                               "path": "some/path/that/exists"
-                           }
-                       }])
-
-      ffx_runner.run_ffx.assert_has_calls([
-          mock.call(('--machine', 'json', 'repository', 'list')),
-          mock.call(('repository', 'remove', 'workstation-eng.chromebook-x64'),
-                    check=True)
-      ])
-
-  def testRemoveProductBundle(self):
-    ffx_runner = mock.create_autospec(ffx_session.FfxRunner, instance=True)
-
-    update_product_bundles.remove_product_bundle('some-bundle-foo-bar',
-                                                 ffx_runner)
-
-    ffx_runner.run_ffx.assert_called_once_with(
-        ('product-bundle', 'remove', '-f', 'some-bundle-foo-bar'))
-
-  def _InitFFXRunWithProductBundleList(self, sdk_version='10.20221114.2.1'):
-    ffx_runner = mock.create_autospec(ffx_session.FfxRunner, instance=True)
-
-    ffx_runner.run_ffx.return_value = f"""
-  gs://fuchsia/{sdk_version}/bundles.json#workstation_eng.qemu-x64
-  gs://fuchsia/{sdk_version}/bundles.json#workstation_eng.chromebook-x64-dfv2
-* gs://fuchsia/{sdk_version}/bundles.json#workstation_eng.chromebook-x64
-* gs://fuchsia/{sdk_version}/bundles.json#terminal.qemu-x64
-  gs://fuchsia/{sdk_version}/bundles.json#terminal.qemu-arm64
-* gs://fuchsia/{sdk_version}/bundles.json#core.x64-dfv2
-
-*No need to fetch with `ffx product-bundle get ...`.
-    """
-    return ffx_runner
-
-  def testGetProductBundleUrlsMarksDesiredAsDownloaded(self):
-    urls = update_product_bundles.get_product_bundle_urls(
-        self._InitFFXRunWithProductBundleList())
-    expected_urls = [{
-        'url':
-        'gs://fuchsia/10.20221114.2.1/bundles.json#workstation_eng.qemu-x64',
-        'downloaded': False,
-    }, {
-        'url': ('gs://fuchsia/10.20221114.2.1/bundles.json#workstation_eng.'
-                'chromebook-x64-dfv2'),
-        'downloaded':
-        False,
-    }, {
-        'url': ('gs://fuchsia/10.20221114.2.1/bundles.json#workstation_eng.'
-                'chromebook-x64'),
-        'downloaded':
-        True,
-    }, {
-        'url': 'gs://fuchsia/10.20221114.2.1/bundles.json#terminal.qemu-x64',
-        'downloaded': True,
-    }, {
-        'url': 'gs://fuchsia/10.20221114.2.1/bundles.json#terminal.qemu-arm64',
-        'downloaded': False,
-    }, {
-        'url': 'gs://fuchsia/10.20221114.2.1/bundles.json#core.x64-dfv2',
-        'downloaded': True,
-    }]
-
-    for i, url in enumerate(urls):
-      self.assertEqual(url, expected_urls[i])
-
-  @mock.patch('update_product_bundles.get_repositories')
-  def testGetProductBundlesExtractsProductBundlesFromURLs(self, mock_get_repos):
-    ffx_runner = self._InitFFXRunWithProductBundleList()
-    mock_get_repos.return_value = [{
-        'name': 'workstation-eng.chromebook-x64'
-    }, {
-        'name': 'terminal.qemu-x64'
-    }, {
-        'name': 'core.x64-dfv2'
-    }]
-
-    self.assertEqual(
-        set(update_product_bundles.get_product_bundles(ffx_runner)),
-        set([
-            'workstation_eng.chromebook-x64',
-            'terminal.qemu-x64',
-            'core.x64-dfv2',
-        ]))
-
-  @mock.patch('update_product_bundles.get_repositories')
-  def testGetProductBundlesExtractsProductBundlesFromURLsFiltersMissingRepos(
-      self, mock_get_repos):
-    ffx_runner = self._InitFFXRunWithProductBundleList()
-
-    # This will be missing two repos from the bundle list:
-    # core and terminal.qemu-x64
-    # Additionally, workstation-eng != workstation_eng, but they will be treated
-    # as the same product-bundle
-    mock_get_repos.return_value = [{
-        'name': 'workstation-eng.chromebook-x64'
-    }, {
-        'name': 'terminal.qemu-arm64'
-    }]
-
-    self.assertEqual(update_product_bundles.get_product_bundles(ffx_runner),
-                     ['workstation_eng.chromebook-x64'])
-    ffx_runner.run_ffx.assert_has_calls([
-        mock.call(('product-bundle', 'remove', '-f', 'terminal.qemu-x64')),
-        mock.call(('product-bundle', 'remove', '-f', 'core.x64-dfv2')),
-    ],
-                                        any_order=True)
-
-  @mock.patch('update_product_bundles.update_repositories_list')
-  def testDownloadProductBundleUpdatesRepoListBeforeCall(
-      self, mock_update_repo):
-    ffx_runner = mock.create_autospec(ffx_session.FfxRunner, instance=True)
-    mock_sequence = mock.Mock()
-    mock_sequence.attach_mock(mock_update_repo, 'update_repo_list')
-    mock_sequence.attach_mock(ffx_runner.run_ffx, 'run_ffx')
-
-    update_product_bundles.download_product_bundle('some-bundle', ffx_runner)
-
-    mock_sequence.assert_has_calls([
-        mock.call.update_repo_list(ffx_runner),
-        mock.call.run_ffx(('product-bundle', 'get', 'some-bundle'))
-    ])
-
-  @mock.patch('update_product_bundles.get_product_bundle_urls')
-  def testFilterProductBundleURLsRemovesBundlesWithoutGivenString(
-      self, mock_get_urls):
-    ffx_runner = mock.create_autospec(ffx_session.FfxRunner, instance=True)
-    mock_get_urls.return_value = [
-        {
-            'url': 'some-url-has-buzz',
-            'downloaded': True,
-        },
-        {
-            'url': 'some-url-to-remove-has-foo',
-            'downloaded': True,
-        },
-        {
-            'url': 'some-url-to-not-remove-has-foo',
-            'downloaded': False,
-        },
-    ]
-    update_product_bundles.keep_product_bundles_by_sdk_version(
-        'buzz', ffx_runner)
-    ffx_runner.run_ffx.assert_called_once_with(
-        ('product-bundle', 'remove', '-f', 'some-url-to-remove-has-foo'))
-
-  @mock.patch('update_product_bundles.get_repositories')
-  def testGetCurrentSignatureReturnsNoneIfNoProductBundles(
-      self, mock_get_repos):
-    ffx_runner = self._InitFFXRunWithProductBundleList()
-
-    # Forces no product-bundles
-    mock_get_repos.return_value = []
-
-    # Mutes logs
-    with self.assertLogs():
-      self.assertIsNone(
-          update_product_bundles.get_current_signature(ffx_runner))
-
-  @mock.patch('update_product_bundles.get_repositories')
-  def testGetCurrentSignatureParsesVersionCorrectly(self, mock_get_repos):
-    ffx_runner = self._InitFFXRunWithProductBundleList()
-    mock_get_repos.return_value = [{
-        'name': 'workstation-eng.chromebook-x64'
-    }, {
-        'name': 'terminal.qemu-x64'
-    }]
-
-    self.assertEqual('10.20221114.2.1',
-                     update_product_bundles.get_current_signature(ffx_runner))
-
-  @mock.patch('update_product_bundles.get_repositories')
-  def testGetCurrentSignatureParsesCustomArtifactsCorrectlys(
-      self, mock_get_repos):
-    ffx_runner = self._InitFFXRunWithProductBundleList(sdk_version='51390009')
-    mock_get_repos.return_value = [{
-        'name': 'workstation-eng.chromebook-x64'
-    }, {
-        'name': 'terminal.qemu-x64'
-    }]
-
-    self.assertEqual('51390009',
-                     update_product_bundles.get_current_signature(ffx_runner))
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/cc/DEPS b/cc/DEPS
index ae56e384..d823169c 100644
--- a/cc/DEPS
+++ b/cc/DEPS
@@ -38,7 +38,6 @@
   "+third_party/skia/include/private/chromium/SkChromeRemoteGlyphCache.h",
   "+third_party/skia/modules/skottie/include",
   "+third_party/skia/modules/skresources/include",
-  "+third_party/skia/src/effects/imagefilters/SkRuntimeImageFilter.h",
   "+third_party/perfetto/protos/perfetto/trace/track_event",
   "+ui/base",
   "+ui/events/types",
diff --git a/cc/base/math_util.cc b/cc/base/math_util.cc
index 3946a919..d011b43 100644
--- a/cc/base/math_util.cc
+++ b/cc/base/math_util.cc
@@ -782,21 +782,21 @@
   if (!raw_value->is_list())
     return false;
 
-  base::Value::ConstListView list_view = raw_value->GetListDeprecated();
+  const base::Value::List& list = raw_value->GetList();
 
-  if (list_view.size() != 4)
+  if (list.size() != 4)
     return false;
 
-  for (const auto& val : list_view) {
+  for (const auto& val : list) {
     if (!val.is_int()) {
       return false;
     }
   }
 
-  int x = list_view[0].GetInt();
-  int y = list_view[1].GetInt();
-  int w = list_view[2].GetInt();
-  int h = list_view[3].GetInt();
+  int x = list[0].GetInt();
+  int y = list[1].GetInt();
+  int w = list[2].GetInt();
+  int h = list[3].GetInt();
 
   *out_rect = gfx::Rect(x, y, w, h);
   return true;
diff --git a/cc/paint/filter_operation.cc b/cc/paint/filter_operation.cc
index d3879d8..0d0c514b 100644
--- a/cc/paint/filter_operation.cc
+++ b/cc/paint/filter_operation.cc
@@ -40,10 +40,6 @@
     return shape_ == other.shape_ && amount_ == other.amount_ &&
            outer_threshold_ == other.outer_threshold_;
   }
-  if (type_ == STRETCH) {
-    return amount_ == other.amount_ &&
-           outer_threshold_ == other.outer_threshold_;
-  }
   return amount_ == other.amount_;
 }
 
@@ -113,19 +109,6 @@
 }
 
 FilterOperation::FilterOperation(FilterType type,
-                                 float amount,
-                                 float outer_threshold)
-    : type_(type),
-      amount_(amount),
-      outer_threshold_(outer_threshold),
-      drop_shadow_offset_(0, 0),
-      drop_shadow_color_(SkColors::kTransparent),
-      zoom_inset_(0) {
-  DCHECK_EQ(type_, STRETCH);
-  matrix_.fill(0.0f);
-}
-
-FilterOperation::FilterOperation(FilterType type,
                                  sk_sp<PaintFilter> image_filter)
     : type_(type),
       amount_(0),
@@ -193,8 +176,6 @@
     case FilterOperation::ALPHA_THRESHOLD:
       return FilterOperation::CreateAlphaThresholdFilter(
           FilterOperation::ShapeRects(), 1.f, 0.f);
-    case FilterOperation::STRETCH:
-      return FilterOperation::CreateStretchFilter(0.f, 0.f);
   }
   NOTREACHED();
   return FilterOperation::CreateEmptyFilter();
@@ -214,7 +195,6 @@
     case FilterOperation::CONTRAST:
     case FilterOperation::BLUR:
     case FilterOperation::DROP_SHADOW:
-    case FilterOperation::STRETCH:
       return std::max(amount, 0.f);
     case FilterOperation::ZOOM:
       return std::max(amount, 1.f);
@@ -340,10 +320,6 @@
       }
       value->EndArray();
     } break;
-    case FilterOperation::STRETCH:
-      value->SetDouble("amount_x", amount_);
-      value->SetDouble("amount_y", outer_threshold_);
-      break;
   }
 }
 
diff --git a/cc/paint/filter_operation.h b/cc/paint/filter_operation.h
index fdc7a00..46eb3e3 100644
--- a/cc/paint/filter_operation.h
+++ b/cc/paint/filter_operation.h
@@ -49,8 +49,7 @@
     REFERENCE,
     SATURATING_BRIGHTNESS,  // Not used in CSS/SVG.
     ALPHA_THRESHOLD,        // Not used in CSS/SVG.
-    STRETCH,                // Not used in CSS/SVG.
-    FILTER_TYPE_LAST = STRETCH
+    FILTER_TYPE_LAST = ALPHA_THRESHOLD
   };
 
   FilterOperation();
@@ -68,7 +67,7 @@
   }
 
   float outer_threshold() const {
-    DCHECK(type_ == ALPHA_THRESHOLD || type_ == STRETCH);
+    DCHECK_EQ(type_, ALPHA_THRESHOLD);
     return outer_threshold_;
   }
 
@@ -175,10 +174,6 @@
                            outer_threshold);
   }
 
-  static FilterOperation CreateStretchFilter(float amount_x, float amount_y) {
-    return FilterOperation(STRETCH, amount_x, amount_y);
-  }
-
   bool operator==(const FilterOperation& other) const;
 
   bool operator!=(const FilterOperation& other) const {
@@ -199,7 +194,7 @@
   }
 
   void set_outer_threshold(float outer_threshold) {
-    DCHECK(type_ == ALPHA_THRESHOLD || type_ == STRETCH);
+    DCHECK_EQ(type_, ALPHA_THRESHOLD);
     outer_threshold_ = outer_threshold;
   }
 
@@ -273,8 +268,6 @@
 
   FilterOperation(FilterType type, float amount, int inset);
 
-  FilterOperation(FilterType type, float amount, float outer_threshold);
-
   FilterOperation(FilterType type, sk_sp<PaintFilter> image_filter);
 
   FilterOperation(FilterType type,
diff --git a/cc/paint/filter_operations.cc b/cc/paint/filter_operations.cc
index d7d3f93..1769b34 100644
--- a/cc/paint/filter_operations.cc
+++ b/cc/paint/filter_operations.cc
@@ -84,7 +84,6 @@
       case FilterOperation::BLUR:
       case FilterOperation::DROP_SHADOW:
       case FilterOperation::ZOOM:
-      case FilterOperation::STRETCH:
         return true;
       case FilterOperation::REFERENCE:
         // TODO(hendrikw): SkImageFilter needs a function that tells us if the
@@ -131,10 +130,6 @@
         // the filter can move pixels. See crbug.com/523538 (sort of).
         max_movement = fmax(max_movement, 100);
         continue;
-      case FilterOperation::STRETCH:
-        max_movement =
-            fmax(max_movement, fmax(op.amount(), op.outer_threshold()));
-        continue;
       case FilterOperation::OPACITY:
       case FilterOperation::COLOR_MATRIX:
       case FilterOperation::GRAYSCALE:
@@ -180,7 +175,6 @@
       case FilterOperation::BRIGHTNESS:
       case FilterOperation::CONTRAST:
       case FilterOperation::SATURATING_BRIGHTNESS:
-      case FilterOperation::STRETCH:
         break;
     }
   }
diff --git a/cc/paint/paint_filter.cc b/cc/paint/paint_filter.cc
index c089ed9..a03ce02 100644
--- a/cc/paint/paint_filter.cc
+++ b/cc/paint/paint_filter.cc
@@ -28,230 +28,11 @@
 #include "third_party/skia/include/effects/SkImageFilters.h"
 #include "third_party/skia/include/effects/SkPerlinNoiseShader.h"
 #include "third_party/skia/include/effects/SkRuntimeEffect.h"
-#include "third_party/skia/src/effects/imagefilters/SkRuntimeImageFilter.h"
 
 namespace cc {
 namespace {
 const bool kHasNoDiscardableImages = false;
 
-#if BUILDFLAG(IS_ANDROID)
-struct StretchShaderUniforms {
-  // multiplier to apply to scale effect
-  float uMaxStretchIntensity;
-
-  // Maximum percentage to stretch beyond bounds  of target
-  float uStretchAffectedDistX;
-  float uStretchAffectedDistY;
-
-  // Distance stretched as a function of the normalized overscroll times
-  // scale intensity
-  float uDistanceStretchedX;
-  float uDistanceStretchedY;
-  float uInverseDistanceStretchedX;
-  float uInverseDistanceStretchedY;
-  float uDistDiffX;
-
-  // Difference between the peak stretch amount and overscroll amount normalized
-  float uDistDiffY;
-
-  // Horizontal offset represented as a ratio of pixels divided by the target
-  // width
-  float uScrollX;
-  // Vertical offset represented as a ratio of pixels divided by the target
-  // height
-  float uScrollY;
-
-  // Normalized overscroll amount in the horizontal direction
-  float uOverscrollX;
-
-  // Normalized overscroll amount in the vertical direction
-  float uOverscrollY;
-  float viewportWidth;   // target height in pixels
-  float viewportHeight;  // target width in pixels
-
-  // uInterpolationStrength is the intensity of the interpolation.
-  // if uInterpolationStrength is 0, then the stretch is constant for all the
-  // uStretchAffectedDist. if uInterpolationStrength is 1, then stretch
-  // intensity is interpolated based on the pixel position in the
-  // uStretchAffectedDist area; The closer we are from the scroll anchor point,
-  // the more it stretches, and the other way around.
-  float uInterpolationStrength;
-};
-
-const char* kStretchShader = R"(
-    uniform shader uContentTexture;
-
-    // multiplier to apply to scale effect
-    uniform float uMaxStretchIntensity;
-
-    // Maximum percentage to stretch beyond bounds  of target
-    uniform float uStretchAffectedDistX;
-    uniform float uStretchAffectedDistY;
-
-    // Distance stretched as a function of the normalized overscroll times
-    // scale intensity
-    uniform float uDistanceStretchedX;
-    uniform float uDistanceStretchedY;
-    uniform float uInverseDistanceStretchedX;
-    uniform float uInverseDistanceStretchedY;
-    uniform float uDistDiffX;
-
-    // Difference between the peak stretch amount and overscroll amount
-    // normalized
-    uniform float uDistDiffY;
-
-    // Horizontal offset represented as a ratio of pixels divided by the target
-    // width
-    uniform float uScrollX;
-    // Vertical offset represented as a ratio of pixels divided by the target
-    // height
-    uniform float uScrollY;
-
-    // Normalized overscroll amount in the horizontal direction
-    uniform float uOverscrollX;
-
-    // Normalized overscroll amount in the vertical direction
-    uniform float uOverscrollY;
-    uniform float viewportWidth; // target height in pixels
-    uniform float viewportHeight; // target width in pixels
-
-    // uInterpolationStrength is the intensity of the interpolation.
-    // if uInterpolationStrength is 0, then the stretch is constant for all the
-    // uStretchAffectedDist. if uInterpolationStrength is 1, then stretch
-    // intensity is interpolated based on the pixel position in the
-    // uStretchAffectedDist area; The closer we are from the scroll anchor
-    // point, the more it stretches, and the other way around.
-    uniform float uInterpolationStrength;
-
-    float easeInCubic(float t, float d) {
-        float tmp = t * d;
-        return tmp * tmp * tmp;
-    }
-
-    float computeOverscrollStart(
-        float inPos,
-        float overscroll,
-        float uStretchAffectedDist,
-        float uInverseStretchAffectedDist,
-        float distanceStretched,
-        float interpolationStrength
-    ) {
-        float offsetPos = uStretchAffectedDist - inPos;
-        float posBasedVariation = mix(
-                1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist),
-                interpolationStrength);
-        float stretchIntensity = overscroll * posBasedVariation;
-        return distanceStretched - (offsetPos / (1. + stretchIntensity));
-    }
-
-    float computeOverscrollEnd(
-        float inPos,
-        float overscroll,
-        float reverseStretchDist,
-        float uStretchAffectedDist,
-        float uInverseStretchAffectedDist,
-        float distanceStretched,
-        float interpolationStrength
-    ) {
-        float offsetPos = inPos - reverseStretchDist;
-        float posBasedVariation = mix(
-                1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist),
-                interpolationStrength);
-        float stretchIntensity = (-overscroll) * posBasedVariation;
-        return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
-    }
-
-    // Prefer usage of return values over out parameters as it enables
-    // SKSL to properly inline method calls and works around potential GPU
-    // driver issues on Wembly. See b/182566543 for details
-    float computeOverscroll(
-        float inPos,
-        float overscroll,
-        float uStretchAffectedDist,
-        float uInverseStretchAffectedDist,
-        float distanceStretched,
-        float distanceDiff,
-        float interpolationStrength
-    ) {
-      float outPos = inPos;
-      if (overscroll > 0) {
-            if (inPos <= uStretchAffectedDist) {
-                outPos = computeOverscrollStart(
-                  inPos,
-                  overscroll,
-                  uStretchAffectedDist,
-                  uInverseStretchAffectedDist,
-                  distanceStretched,
-                  interpolationStrength
-                );
-            } else if (inPos >= distanceStretched) {
-                outPos = distanceDiff + inPos;
-            }
-        }
-        if (overscroll < 0) {
-            float stretchAffectedDist = 1. - uStretchAffectedDist;
-            if (inPos >= stretchAffectedDist) {
-                outPos = computeOverscrollEnd(
-                  inPos,
-                  overscroll,
-                  stretchAffectedDist,
-                  uStretchAffectedDist,
-                  uInverseStretchAffectedDist,
-                  distanceStretched,
-                  interpolationStrength
-                );
-            } else if (inPos < stretchAffectedDist) {
-                outPos = -distanceDiff + inPos;
-            }
-        }
-        return outPos;
-    }
-
-    vec4 main(vec2 coord) {
-        // Normalize SKSL pixel coordinate into a unit vector
-        float inU = coord.x / viewportWidth;
-        float inV = coord.y / viewportHeight;
-        float outU;
-        float outV;
-        float stretchIntensity;
-        // Add the normalized scroll position within scrolling list
-        inU += uScrollX;
-        inV += uScrollY;
-        outU = inU;
-        outV = inV;
-        outU = computeOverscroll(
-            inU,
-            uOverscrollX,
-            uStretchAffectedDistX,
-            uInverseDistanceStretchedX,
-            uDistanceStretchedX,
-            uDistDiffX,
-            uInterpolationStrength
-        );
-        outV = computeOverscroll(
-            inV,
-            uOverscrollY,
-            uStretchAffectedDistY,
-            uInverseDistanceStretchedY,
-            uDistanceStretchedY,
-            uDistDiffY,
-            uInterpolationStrength
-        );
-        coord.x = outU * viewportWidth;
-        coord.y = outV * viewportHeight;
-        return uContentTexture.eval(coord);
-    })";
-
-static const float CONTENT_DISTANCE_STRETCHED = 1.f;
-static const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
-
-sk_sp<SkRuntimeEffect> getStretchEffect() {
-  static base::NoDestructor<SkRuntimeEffect::Result> effect(
-      SkRuntimeEffect::MakeForShader(SkString(kStretchShader)));
-  return effect->effect;
-}
-#endif
-
 bool AreScalarsEqual(SkScalar one, SkScalar two) {
   return PaintOp::AreEqualEvenIfNaN(one, two);
 }
@@ -336,8 +117,6 @@
       return "kLightingPoint";
     case Type::kLightingSpot:
       return "kLightingSpot";
-    case Type::kStretch:
-      return "kStretch";
   }
   NOTREACHED();
   return "Unknown";
@@ -454,9 +233,6 @@
     case Type::kLightingSpot:
       return *static_cast<const LightingSpotPaintFilter*>(this) ==
              static_cast<const LightingSpotPaintFilter&>(other);
-    case Type::kStretch:
-      return *static_cast<const StretchPaintFilter*>(this) ==
-             static_cast<const StretchPaintFilter&>(other);
   }
   NOTREACHED();
   return true;
@@ -1575,78 +1351,4 @@
          base::ValuesEquivalent(input_.get(), other.input_.get());
 }
 
-StretchPaintFilter::StretchPaintFilter(SkScalar stretch_x,
-                                       SkScalar stretch_y,
-                                       SkScalar width,
-                                       SkScalar height,
-                                       sk_sp<PaintFilter> input,
-                                       const CropRect* crop_rect)
-    : PaintFilter(kType, crop_rect, HasDiscardableImages(input)),
-      stretch_x_(stretch_x),
-      stretch_y_(stretch_y),
-      width_(width),
-      height_(height),
-      input_(std::move(input)) {
-#if BUILDFLAG(IS_ANDROID)
-  float normOverScrollDistX = stretch_x_;
-  float normOverScrollDistY = stretch_y_;
-  float distanceStretchedX =
-      CONTENT_DISTANCE_STRETCHED / (1 + std::abs(normOverScrollDistX));
-  float distanceStretchedY =
-      CONTENT_DISTANCE_STRETCHED / (1 + std::abs(normOverScrollDistY));
-  float inverseDistanceStretchedX = 1.f / CONTENT_DISTANCE_STRETCHED;
-  float inverseDistanceStretchedY = 1.f / CONTENT_DISTANCE_STRETCHED;
-  float diffX = distanceStretchedX - CONTENT_DISTANCE_STRETCHED;
-  float diffY = distanceStretchedY - CONTENT_DISTANCE_STRETCHED;
-  StretchShaderUniforms uniforms;
-
-  uniforms.uInterpolationStrength = INTERPOLATION_STRENGTH_VALUE;
-  uniforms.uStretchAffectedDistX = CONTENT_DISTANCE_STRETCHED;
-  uniforms.uStretchAffectedDistY = CONTENT_DISTANCE_STRETCHED;
-  uniforms.uDistanceStretchedX = distanceStretchedX;
-  uniforms.uDistanceStretchedY = distanceStretchedY;
-  uniforms.uInverseDistanceStretchedX = inverseDistanceStretchedX;
-  uniforms.uInverseDistanceStretchedY = inverseDistanceStretchedY;
-  uniforms.uDistDiffX = diffX;
-  uniforms.uDistDiffY = diffY;
-  uniforms.uOverscrollX = normOverScrollDistX;
-  uniforms.uOverscrollY = normOverScrollDistY;
-  uniforms.uScrollX = 0;
-  uniforms.uScrollY = 0;
-  uniforms.viewportWidth = width;
-  uniforms.viewportHeight = height;
-  sk_sp<SkData> uniformVals = SkData::MakeWithCopy(&uniforms, sizeof(uniforms));
-  cached_sk_filter_ = SkMakeRuntimeImageFilter(getStretchEffect(), uniformVals,
-                                               GetSkFilter(input_.get()));
-#else   // BUILDFLAG(IS_ANDROID)
-  // Stretch filter is only used on android and removed from other platforms
-  // to reduce size. See https://crbug.com/1226170.
-#endif  // BUILDFLAG(IS_ANDROID)
-}
-
-StretchPaintFilter::~StretchPaintFilter() = default;
-
-size_t StretchPaintFilter::SerializedSize() const {
-  base::CheckedNumeric<size_t> total_size =
-      BaseSerializedSize() + sizeof(stretch_x_) + sizeof(stretch_y_) +
-      sizeof(width_) + sizeof(height_);
-  total_size += GetFilterSize(input_.get());
-  return total_size.ValueOrDefault(0u);
-}
-
-sk_sp<PaintFilter> StretchPaintFilter::SnapshotWithImagesInternal(
-    ImageProvider* image_provider) const {
-  return sk_make_sp<StretchPaintFilter>(stretch_x_, stretch_y_, width_, height_,
-                                        Snapshot(input_, image_provider),
-                                        GetCropRect());
-}
-
-bool StretchPaintFilter::operator==(const StretchPaintFilter& other) const {
-  return PaintOp::AreEqualEvenIfNaN(stretch_x_, other.stretch_x_) &&
-         PaintOp::AreEqualEvenIfNaN(stretch_y_, other.stretch_y_) &&
-         PaintOp::AreEqualEvenIfNaN(width_, other.width_) &&
-         PaintOp::AreEqualEvenIfNaN(height_, other.height_) &&
-         base::ValuesEquivalent(input_.get(), other.input_.get());
-}
-
 }  // namespace cc
diff --git a/cc/paint/paint_filter.h b/cc/paint/paint_filter.h
index 3f7936f0..a3d12e6 100644
--- a/cc/paint/paint_filter.h
+++ b/cc/paint/paint_filter.h
@@ -55,9 +55,8 @@
     kLightingDistant,
     kLightingPoint,
     kLightingSpot,
-    kStretch,
-    // Update the following if kStretch is not the max anymore.
-    kMaxValue = kStretch
+    // Update the following if kLightingSpot is not the max anymore.
+    kMaxValue = kLightingSpot
   };
   enum class LightingType {
     kDiffuse,
@@ -878,39 +877,6 @@
   sk_sp<PaintFilter> input_;
 };
 
-class CC_PAINT_EXPORT StretchPaintFilter final : public PaintFilter {
- public:
-  static constexpr Type kType = Type::kStretch;
-  StretchPaintFilter(SkScalar stretch_x,
-                     SkScalar stretch_y,
-                     SkScalar width,
-                     SkScalar height,
-                     sk_sp<PaintFilter> input,
-                     const CropRect* crop_rect = nullptr);
-  ~StretchPaintFilter() override;
-
-  const sk_sp<PaintFilter>& input() const { return input_; }
-
-  SkScalar stretch_x() const { return stretch_x_; }
-  SkScalar stretch_y() const { return stretch_y_; }
-  SkScalar width() const { return width_; }
-  SkScalar height() const { return height_; }
-
-  size_t SerializedSize() const override;
-  bool operator==(const StretchPaintFilter& other) const;
-
- protected:
-  sk_sp<PaintFilter> SnapshotWithImagesInternal(
-      ImageProvider* image_provider) const override;
-
- private:
-  SkScalar stretch_x_;
-  SkScalar stretch_y_;
-  SkScalar width_;
-  SkScalar height_;
-  sk_sp<PaintFilter> input_;
-};
-
 }  // namespace cc
 
 #endif  // CC_PAINT_PAINT_FILTER_H_
diff --git a/cc/paint/paint_filter_unittest.cc b/cc/paint/paint_filter_unittest.cc
index c88a719..781a20d 100644
--- a/cc/paint/paint_filter_unittest.cc
+++ b/cc/paint/paint_filter_unittest.cc
@@ -134,9 +134,6 @@
           PaintFilter::LightingType::kDiffuse, SkPoint3::Make(0.1f, 0.2f, 0.3f),
           SkPoint3::Make(0.4f, 0.5f, 0.6f), 0.1f, 0.2f, SkColors::kWhite, 0.4f,
           0.5f, 0.6f, image_filter, &crop_rect);
-    case PaintFilter::Type::kStretch:
-      return sk_make_sp<StretchPaintFilter>(0.1f, 0.2f, 100.f, 200.f,
-                                            image_filter, &crop_rect);
   }
   NOTREACHED();
   return nullptr;
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index 4852e18..76f4dc87 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -858,9 +858,6 @@
     case PaintFilter::Type::kLightingSpot:
       ReadLightingSpotPaintFilter(filter, crop_rect);
       break;
-    case PaintFilter::Type::kStretch:
-      ReadStretchPaintFilter(filter, crop_rect);
-      break;
   }
 }
 
@@ -1365,28 +1362,6 @@
       std::move(input), base::OptionalToPtr(crop_rect)));
 }
 
-void PaintOpReader::ReadStretchPaintFilter(
-    sk_sp<PaintFilter>* filter,
-    const absl::optional<PaintFilter::CropRect>& crop_rect) {
-  SkScalar stretch_x = 0.f;
-  SkScalar stretch_y = 0.f;
-  SkScalar width = 0.f;
-  SkScalar height = 0.f;
-  sk_sp<PaintFilter> input;
-
-  Read(&stretch_x);
-  Read(&stretch_y);
-  Read(&width);
-  Read(&height);
-  Read(&input);
-
-  if (!valid_)
-    return;
-  filter->reset(new StretchPaintFilter(stretch_x, stretch_y, width, height,
-                                       std::move(input),
-                                       base::OptionalToPtr(crop_rect)));
-}
-
 size_t PaintOpReader::Read(sk_sp<PaintRecord>* record) {
   size_t size_bytes = 0;
   ReadSize(&size_bytes);
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h
index 92bd3c5..5ca5cef 100644
--- a/cc/paint/paint_op_reader.h
+++ b/cc/paint/paint_op_reader.h
@@ -298,9 +298,6 @@
   void ReadLightingSpotPaintFilter(
       sk_sp<PaintFilter>* filter,
       const absl::optional<PaintFilter::CropRect>& crop_rect);
-  void ReadStretchPaintFilter(
-      sk_sp<PaintFilter>* filter,
-      const absl::optional<PaintFilter::CropRect>& crop_rect);
 
   // Returns the size of the read record, 0 if error.
   size_t Read(sk_sp<PaintRecord>* record);
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
index 9134fd3..0ac2a3c 100644
--- a/cc/paint/paint_op_writer.cc
+++ b/cc/paint/paint_op_writer.cc
@@ -693,9 +693,6 @@
     case PaintFilter::Type::kLightingSpot:
       Write(static_cast<const LightingSpotPaintFilter&>(*filter), current_ctm);
       break;
-    case PaintFilter::Type::kStretch:
-      Write(static_cast<const StretchPaintFilter&>(*filter), current_ctm);
-      break;
   }
 }
 
@@ -919,15 +916,6 @@
   Write(filter.input().get(), current_ctm);
 }
 
-void PaintOpWriter::Write(const StretchPaintFilter& filter,
-                          const SkM44& current_ctm) {
-  WriteSimple(filter.stretch_x());
-  WriteSimple(filter.stretch_y());
-  WriteSimple(filter.width());
-  WriteSimple(filter.height());
-  Write(filter.input().get(), current_ctm);
-}
-
 void PaintOpWriter::Write(const PaintRecord* record,
                           const gfx::Rect& playback_rect,
                           const gfx::SizeF& post_scale) {
diff --git a/cc/paint/paint_op_writer.h b/cc/paint/paint_op_writer.h
index 093ac49..c1b92df6 100644
--- a/cc/paint/paint_op_writer.h
+++ b/cc/paint/paint_op_writer.h
@@ -170,7 +170,6 @@
              const SkM44& current_ctm);
   void Write(const LightingPointPaintFilter& filter, const SkM44& current_ctm);
   void Write(const LightingSpotPaintFilter& filter, const SkM44& current_ctm);
-  void Write(const StretchPaintFilter& filter, const SkM44& current_ctm);
 
   void Write(const PaintRecord* record,
              const gfx::Rect& playback_rect,
diff --git a/cc/paint/render_surface_filters.cc b/cc/paint/render_surface_filters.cc
index b81fdc2..e44d6820 100644
--- a/cc/paint/render_surface_filters.cc
+++ b/cc/paint/render_surface_filters.cc
@@ -295,12 +295,6 @@
         }
         break;
       }
-      case FilterOperation::STRETCH: {
-        image_filter = sk_make_sp<StretchPaintFilter>(
-            op.amount(), op.outer_threshold(), size.width(), size.height(),
-            std::move(image_filter));
-        break;
-      }
     }
   }
   return image_filter;
diff --git a/cc/paint/skottie_mru_resource_provider.cc b/cc/paint/skottie_mru_resource_provider.cc
index 1846ddc6..58e055b 100644
--- a/cc/paint/skottie_mru_resource_provider.cc
+++ b/cc/paint/skottie_mru_resource_provider.cc
@@ -37,21 +37,23 @@
     return image_asset_sizes;
   }
 
-  const base::Value* assets = animation_dict->FindListKey(kAssetsKey);
+  const base::Value::List* assets =
+      animation_dict->GetDict().FindList(kAssetsKey);
   // An animation may legitimately have no assets in it.
   if (!assets)
     return image_asset_sizes;
 
-  for (const base::Value& asset : assets->GetListDeprecated()) {
+  for (const base::Value& asset : *assets) {
     if (!asset.is_dict()) {
       LOG(ERROR) << "Found invalid asset in animation with type "
                  << base::Value::GetTypeName(asset.type());
       continue;
     }
+    const base::Value::Dict& asset_dict = asset.GetDict();
 
-    const std::string* id = asset.FindStringKey(kIdKey);
-    absl::optional<int> width = asset.FindIntKey(kWidthKey);
-    absl::optional<int> height = asset.FindIntKey(kHeightKey);
+    const std::string* id = asset_dict.FindString(kIdKey);
+    absl::optional<int> width = asset_dict.FindInt(kWidthKey);
+    absl::optional<int> height = asset_dict.FindInt(kHeightKey);
     if (id && width && height && *width > 0 && *height > 0 &&
         !image_asset_sizes.emplace(*id, gfx::Size(*width, *height)).second) {
       LOG(WARNING) << "Multiple assets found in animation with id " << *id;
diff --git a/cc/test/layer_tree_json_parser.cc b/cc/test/layer_tree_json_parser.cc
index 6da28ea..d380e8b 100644
--- a/cc/test/layer_tree_json_parser.cc
+++ b/cc/test/layer_tree_json_parser.cc
@@ -26,29 +26,28 @@
                                         ContentLayerClient* content_client) {
   if (!val.is_dict())
     return nullptr;
+  const base::Value::Dict& dict = val.GetDict();
 
-  const std::string* layer_type = val.FindStringKey("LayerType");
+  const std::string* layer_type = dict.FindString("LayerType");
   if (!layer_type)
     return nullptr;
 
-  const base::Value* bounds_list_value = val.FindListKey("Bounds");
-  if (!bounds_list_value)
+  const base::Value::List* bounds_list = dict.FindList("Bounds");
+  if (!bounds_list)
     return nullptr;
-  base::Value::ConstListView bounds_list =
-      bounds_list_value->GetListDeprecated();
-  if (bounds_list.size() < 2)
+  if (bounds_list->size() < 2)
     return nullptr;
 
-  absl::optional<int> width = bounds_list[0].GetIfInt();
-  absl::optional<int> height = bounds_list[1].GetIfInt();
+  absl::optional<int> width = (*bounds_list)[0].GetIfInt();
+  absl::optional<int> height = (*bounds_list)[1].GetIfInt();
   if (!width.has_value() || !height.has_value())
     return nullptr;
 
-  absl::optional<bool> draws_content = val.FindBoolKey("DrawsContent");
+  absl::optional<bool> draws_content = dict.FindBool("DrawsContent");
   if (!draws_content.has_value())
     return nullptr;
 
-  absl::optional<bool> hit_testable = val.FindBoolKey("HitTestable");
+  absl::optional<bool> hit_testable = dict.FindBool("HitTestable");
   // If we cannot load hit_testable, we may try loading the old version, since
   // we do not record |hit_testable_without_draws_content| in the past, we use
   // |draws_content| as the value of |hit_testable|.
@@ -60,53 +59,47 @@
   if (*layer_type == "SolidColorLayer") {
     new_layer = SolidColorLayer::Create();
   } else if (*layer_type == "NinePatchLayer") {
-    const base::Value* aperture_list_value = val.FindListKey("ImageAperture");
-    if (!aperture_list_value)
+    const base::Value::List* aperture_list = dict.FindList("ImageAperture");
+    if (!aperture_list)
       return nullptr;
-    base::Value::ConstListView aperture_list =
-        aperture_list_value->GetListDeprecated();
-    if (aperture_list.size() < 4)
+    if (aperture_list->size() < 4)
       return nullptr;
 
-    absl::optional<int> aperture_x = aperture_list[0].GetIfInt();
-    absl::optional<int> aperture_y = aperture_list[1].GetIfInt();
-    absl::optional<int> aperture_width = aperture_list[2].GetIfInt();
-    absl::optional<int> aperture_height = aperture_list[3].GetIfInt();
+    absl::optional<int> aperture_x = (*aperture_list)[0].GetIfInt();
+    absl::optional<int> aperture_y = (*aperture_list)[1].GetIfInt();
+    absl::optional<int> aperture_width = (*aperture_list)[2].GetIfInt();
+    absl::optional<int> aperture_height = (*aperture_list)[3].GetIfInt();
     if (!(aperture_x.has_value() && aperture_y.has_value() &&
           aperture_width.has_value() && aperture_height.has_value()))
       return nullptr;
 
-    const base::Value* image_bounds_list_value = val.FindListKey("ImageBounds");
-    if (!image_bounds_list_value)
+    const base::Value::List* image_bounds_list = dict.FindList("ImageBounds");
+    if (!image_bounds_list)
       return nullptr;
-    base::Value::ConstListView image_bounds_list =
-        image_bounds_list_value->GetListDeprecated();
-    if (image_bounds_list.size() < 2)
+    if (image_bounds_list->size() < 2)
       return nullptr;
 
-    absl::optional<double> image_width = image_bounds_list[0].GetIfDouble();
-    absl::optional<double> image_height = image_bounds_list[1].GetIfDouble();
+    absl::optional<double> image_width = (*image_bounds_list)[0].GetIfDouble();
+    absl::optional<double> image_height = (*image_bounds_list)[1].GetIfDouble();
     if (!(image_width.has_value() && image_height.has_value()))
       return nullptr;
 
-    const base::Value* border_list_value = val.FindListKey("Border");
-    if (!border_list_value)
+    const base::Value::List* border_list = dict.FindList("Border");
+    if (!border_list)
       return nullptr;
-    base::Value::ConstListView border_list =
-        border_list_value->GetListDeprecated();
-    if (border_list.size() < 4)
+    if (border_list->size() < 4)
       return nullptr;
 
-    absl::optional<int> border_x = border_list[0].GetIfInt();
-    absl::optional<int> border_y = border_list[1].GetIfInt();
-    absl::optional<int> border_width = border_list[2].GetIfInt();
-    absl::optional<int> border_height = border_list[3].GetIfInt();
+    absl::optional<int> border_x = (*border_list)[0].GetIfInt();
+    absl::optional<int> border_y = (*border_list)[1].GetIfInt();
+    absl::optional<int> border_width = (*border_list)[2].GetIfInt();
+    absl::optional<int> border_height = (*border_list)[3].GetIfInt();
 
     if (!(border_x.has_value() && border_y.has_value() &&
           border_width.has_value() && border_height.has_value()))
       return nullptr;
 
-    absl::optional<bool> fill_center = val.FindBoolKey("FillCenter");
+    absl::optional<bool> fill_center = dict.FindBool("FillCenter");
     if (!fill_center.has_value())
       return nullptr;
 
@@ -134,26 +127,23 @@
   new_layer->SetIsDrawable(*draws_content);
   new_layer->SetHitTestable(*hit_testable);
 
-  absl::optional<double> opacity = val.FindDoubleKey("Opacity");
+  absl::optional<double> opacity = dict.FindDouble("Opacity");
   if (opacity.has_value())
     new_layer->SetOpacity(*opacity);
 
-  absl::optional<bool> contents_opaque = val.FindBoolKey("ContentsOpaque");
+  absl::optional<bool> contents_opaque = dict.FindBool("ContentsOpaque");
   if (contents_opaque.has_value())
     new_layer->SetContentsOpaque(*contents_opaque);
 
-  const base::Value* touch_region_list_value = val.FindListKey("TouchRegion");
+  const base::Value::List* touch_region_list = dict.FindList("TouchRegion");
 
-  if (touch_region_list_value) {
-    base::Value::ConstListView touch_region_list =
-        touch_region_list_value->GetListDeprecated();
-
+  if (touch_region_list) {
     TouchActionRegion touch_action_region;
-    for (size_t i = 0; i + 3 < touch_region_list.size(); i += 4) {
-      absl::optional<int> rect_x = touch_region_list[i + 0].GetIfInt();
-      absl::optional<int> rect_y = touch_region_list[i + 1].GetIfInt();
-      absl::optional<int> rect_width = touch_region_list[i + 2].GetIfInt();
-      absl::optional<int> rect_height = touch_region_list[i + 3].GetIfInt();
+    for (size_t i = 0; i + 3 < touch_region_list->size(); i += 4) {
+      absl::optional<int> rect_x = (*touch_region_list)[i + 0].GetIfInt();
+      absl::optional<int> rect_y = (*touch_region_list)[i + 1].GetIfInt();
+      absl::optional<int> rect_width = (*touch_region_list)[i + 2].GetIfInt();
+      absl::optional<int> rect_height = (*touch_region_list)[i + 3].GetIfInt();
 
       if (!(rect_x.has_value() && rect_y.has_value() &&
             rect_width.has_value() && rect_height.has_value()))
@@ -166,12 +156,10 @@
     new_layer->SetTouchActionRegion(std::move(touch_action_region));
   }
 
-  const base::Value* transform_list_value = val.FindListKey("Transform");
-  if (!transform_list_value)
+  const base::Value::List* transform_list = dict.FindList("Transform");
+  if (!transform_list)
     return nullptr;
-  base::Value::ConstListView transform_list =
-      transform_list_value->GetListDeprecated();
-  if (transform_list.size() < 16)
+  if (transform_list->size() < 16)
     return nullptr;
 
   float transform[16];
@@ -179,19 +167,19 @@
     // GetDouble can implicitly convert from either double or int; however, it's
     // not clear if "is_double" is sufficient for this check. Given that int is
     // also a valid type that can be gotten, check it here.
-    if (!(transform_list[i].is_double() || transform_list[i].is_int())) {
+    if (!((*transform_list)[i].is_double() || (*transform_list)[i].is_int())) {
       return nullptr;
     }
 
-    transform[i] = transform_list[i].GetDouble();
+    transform[i] = (*transform_list)[i].GetDouble();
   }
 
   new_layer->SetTransform(gfx::Transform::ColMajorF(transform));
 
-  const base::Value* child_list_value = val.FindListKey("Children");
-  if (!child_list_value)
+  const base::Value::List* child_list = dict.FindList("Children");
+  if (!child_list)
     return nullptr;
-  for (const auto& value : child_list_value->GetListDeprecated()) {
+  for (const auto& value : *child_list) {
     new_layer->AddChild(ParseTreeFromValue(value, content_client));
   }
 
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index 41dba5fc..6870006b 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -1161,39 +1161,15 @@
 void UpdateElasticOverscroll(
     PropertyTrees* property_trees,
     TransformNode* overscroll_elasticity_transform_node,
-    ElementId overscroll_elasticity_effect_element_id,
     const gfx::Vector2dF& elastic_overscroll,
     const ScrollNode* inner_viewport) {
-#if BUILDFLAG(IS_ANDROID)
-  // On android, elastic overscroll is implemented by stretching the content
-  // from the overscrolled edge.
-  if (!overscroll_elasticity_effect_element_id &&
-      !overscroll_elasticity_transform_node) {
+  if (!overscroll_elasticity_transform_node) {
     DCHECK(elastic_overscroll.IsZero());
     return;
   }
-  if (overscroll_elasticity_effect_element_id) {
-    if (elastic_overscroll.IsZero() || !inner_viewport) {
-      property_trees->effect_tree_mutable().OnFilterAnimated(
-          overscroll_elasticity_effect_element_id, FilterOperations());
-      return;
-    }
-    // The inner viewport container size takes into account the size change as a
-    // result of the top controls, see ScrollTree::container_bounds.
-    gfx::Size scroller_size =
-        property_trees->scroll_tree().container_bounds(inner_viewport->id);
-
-    property_trees->effect_tree_mutable().OnFilterAnimated(
-        overscroll_elasticity_effect_element_id,
-        FilterOperations(
-            std::vector<FilterOperation>({FilterOperation::CreateStretchFilter(
-                -elastic_overscroll.x() / scroller_size.width(),
-                -elastic_overscroll.y() / scroller_size.height())})));
-    return;
-  }
-
-  // If there is no overscroll elasticity effect node, we apply a stretch
-  // transform.
+#if BUILDFLAG(IS_ANDROID)
+  // On android, elastic overscroll is implemented by stretching the content
+  // from the overscrolled edge by applying a stretch transform
   overscroll_elasticity_transform_node->local.MakeIdentity();
   overscroll_elasticity_transform_node->origin.SetPoint(0.f, 0.f, 0.f);
   if (base::FeatureList::IsEnabled(
@@ -1228,11 +1204,8 @@
   }
   overscroll_elasticity_transform_node->needs_local_transform_update = true;
   property_trees->transform_tree_mutable().set_needs_update(true);
+
 #else  // BUILDFLAG(IS_ANDROID)
-  if (!overscroll_elasticity_transform_node) {
-    DCHECK(elastic_overscroll.IsZero());
-    return;
-  }
 
   // On other platforms, we modify the translation offset to match the
   // overscroll amount.
@@ -1565,11 +1538,10 @@
   UpdatePageScaleFactor(property_trees,
                         layer_tree_impl->PageScaleTransformNode(),
                         layer_tree_impl->current_page_scale_factor());
-  UpdateElasticOverscroll(
-      property_trees, layer_tree_impl->OverscrollElasticityTransformNode(),
-      layer_tree_impl->OverscrollElasticityEffectElementId(),
-      layer_tree_impl->current_elastic_overscroll(),
-      layer_tree_impl->InnerViewportScrollNode());
+  UpdateElasticOverscroll(property_trees,
+                          layer_tree_impl->OverscrollElasticityTransformNode(),
+                          layer_tree_impl->current_elastic_overscroll(),
+                          layer_tree_impl->InnerViewportScrollNode());
   // Similarly, the device viewport and device transform are shared
   // by both trees.
   property_trees->clip_tree_mutable().SetViewportClip(
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 5e03d11..4140c1e 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1529,10 +1529,6 @@
       viewport_property_ids_.overscroll_elasticity_transform);
 }
 
-ElementId LayerTreeImpl::OverscrollElasticityEffectElementId() const {
-  return viewport_property_ids_.overscroll_elasticity_effect;
-}
-
 const TransformNode* LayerTreeImpl::PageScaleTransformNode() const {
   return property_trees()->transform_tree().Node(
       viewport_property_ids_.page_scale_transform);
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 410cf51..9a1ce83 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -334,7 +334,6 @@
         const_cast<const LayerTreeImpl*>(this)
             ->OverscrollElasticityTransformNode());
   }
-  ElementId OverscrollElasticityEffectElementId() const;
   const TransformNode* PageScaleTransformNode() const;
   TransformNode* PageScaleTransformNode() {
     return const_cast<TransformNode*>(
diff --git a/cc/trees/viewport_property_ids.h b/cc/trees/viewport_property_ids.h
index 240ad011..941a010 100644
--- a/cc/trees/viewport_property_ids.h
+++ b/cc/trees/viewport_property_ids.h
@@ -12,7 +12,6 @@
 
 struct ViewportPropertyIds {
   int overscroll_elasticity_transform = kInvalidPropertyNodeId;
-  ElementId overscroll_elasticity_effect;
   int page_scale_transform = kInvalidPropertyNodeId;
   int inner_scroll = kInvalidPropertyNodeId;
   int outer_clip = kInvalidPropertyNodeId;
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index daf744aa..66f3610 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -406,7 +406,6 @@
       "//chrome/browser/android/customtabs/branding:java",
       "//chrome/browser/android/lifecycle:java",
       "//chrome/browser/android/messages:java",
-      "//chrome/browser/android/webapps/launchpad:java",
       "//chrome/browser/back_press/android:java",
       "//chrome/browser/banners/android:java",
       "//chrome/browser/battery/android:java",
@@ -1011,7 +1010,6 @@
       "//chrome/browser/android/customtabs/branding:junit",
       "//chrome/browser/android/httpclient:junit_tests",
       "//chrome/browser/android/lifecycle:java",
-      "//chrome/browser/android/webapps/launchpad:junit_tests",
       "//chrome/browser/back_press/android:java",
       "//chrome/browser/back_press/android:junit",
       "//chrome/browser/banners/android:java",
@@ -1550,7 +1548,6 @@
       "//chrome/browser/android/lifecycle:java",
       "//chrome/browser/android/metrics:ukm_java_test_support",
       "//chrome/browser/android/metrics:ukm_javatests",
-      "//chrome/browser/android/webapps/launchpad:java",
       "//chrome/browser/back_press/android:java",
       "//chrome/browser/banners/android:java",
       "//chrome/browser/browser_controls/android:java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 7181e13..f615caa 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1217,8 +1217,6 @@
   "java/src/org/chromium/chrome/browser/webapps/WebappLauncherActivity.java",
   "java/src/org/chromium/chrome/browser/webapps/WebappLocator.java",
   "java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java",
-  "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivity.java",
-  "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadUtils.java",
   "java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorActivity.java",
   "java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUSBActivity.java",
   "java/src/org/chromium/chrome/browser/webshare/ShareServiceImplementationFactory.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 9c28e46c..6bed2c3 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -561,9 +561,6 @@
   "javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenIconTest.java",
   "javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java",
   "javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java",
-  "javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java",
-  "javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPageTest.java",
-  "javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadTestUtils.java",
   "javatests/src/org/chromium/chrome/browser/webauth/AuthenticatorTest.java",
   "javatests/src/org/chromium/chrome/browser/webauth/Fido2ApiTestHelper.java",
   "javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java",
diff --git a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
index 98c3b10..1439c6a4 100644
--- a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
@@ -583,13 +583,6 @@
         <category android:name="android.intent.category.DEFAULT"/>
       </intent-filter>  # DIFF-ANCHOR: 9c5197e9
     </activity>  # DIFF-ANCHOR: aea75380
-    <activity  # DIFF-ANCHOR: 66a0be05
-        android:name="org.chromium.chrome.browser.webapps.launchpad.LaunchpadActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:theme="@style/Theme.Chromium.Activity.Fullscreen"
-        android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
-    </activity>  # DIFF-ANCHOR: 66a0be05
     <activity  # DIFF-ANCHOR: a1fac31f
         android:name="org.chromium.chrome.browser.webauth.authenticator.CableAuthenticatorActivity"
         android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
diff --git a/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
index 9793846..7d62371 100644
--- a/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
@@ -454,13 +454,6 @@
         <category android:name="android.intent.category.DEFAULT"/>
       </intent-filter>  # DIFF-ANCHOR: 9c5197e9
     </activity>  # DIFF-ANCHOR: aea75380
-    <activity  # DIFF-ANCHOR: 66a0be05
-        android:name="org.chromium.chrome.browser.webapps.launchpad.LaunchpadActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:theme="@style/Theme.Chromium.Activity.Fullscreen"
-        android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
-    </activity>  # DIFF-ANCHOR: 66a0be05
     <activity  # DIFF-ANCHOR: a1fac31f
         android:name="org.chromium.chrome.browser.webauth.authenticator.CableAuthenticatorActivity"
         android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
diff --git a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
index bcde52d..8331c17 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
@@ -556,13 +556,6 @@
         <category android:name="android.intent.category.DEFAULT"/>
       </intent-filter>  # DIFF-ANCHOR: 9c5197e9
     </activity>  # DIFF-ANCHOR: aea75380
-    <activity  # DIFF-ANCHOR: 66a0be05
-        android:name="org.chromium.chrome.browser.webapps.launchpad.LaunchpadActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:theme="@style/Theme.Chromium.Activity.Fullscreen"
-        android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
-    </activity>  # DIFF-ANCHOR: 66a0be05
     <activity  # DIFF-ANCHOR: a1fac31f
         android:name="org.chromium.chrome.browser.webauth.authenticator.CableAuthenticatorActivity"
         android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 1fc712f..f315d8b 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -688,14 +688,6 @@
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
         </activity>
 
-        <!-- Activities for app launcher. -->
-        <activity android:name="org.chromium.chrome.browser.webapps.launchpad.LaunchpadActivity"
-            android:theme="@style/Theme.Chromium.Activity.Fullscreen"
-            android:windowSoftInputMode="stateAlwaysHidden|adjustResize"
-            android:exported="false"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
-        </activity>
-
         <!--
             Activities for webapps.
             TODO(dfalcantara): Remove the aliases for the WebappActivities once we're pretty sure
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
index 3244fed3..20df202 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
@@ -19,7 +19,6 @@
 import org.chromium.chrome.browser.browser_controls.BrowserControlsMarginSupplier;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
 import org.chromium.chrome.browser.explore_sites.ExploreSitesPage;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
 import org.chromium.chrome.browser.history.HistoryManagerUtils;
 import org.chromium.chrome.browser.history.HistoryPage;
@@ -43,8 +42,6 @@
 import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.browser.ui.native_page.NativePage.NativePageType;
 import org.chromium.chrome.browser.ui.native_page.NativePageHost;
-import org.chromium.chrome.browser.webapps.launchpad.LaunchpadPage;
-import org.chromium.chrome.browser.webapps.launchpad.LaunchpadUtils;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.base.DeviceFormFactor;
@@ -215,17 +212,6 @@
                     mBrowserControlsManager);
         }
 
-        protected NativePage buildLaunchpadPage(Tab tab) {
-            if (ChromeFeatureList.isEnabled(ChromeFeatureList.APP_LAUNCHPAD)) {
-                return new LaunchpadPage(mActivity,
-                        new TabShim(tab, mBrowserControlsManager, mTabModelSelector),
-                        mWindowAndroid::getModalDialogManager, new SettingsLauncherImpl(),
-                        LaunchpadUtils.retrieveWebApks(mActivity));
-            } else {
-                return null;
-            }
-        }
-
         protected NativePage buildManagementPage(Tab tab) {
             return new ManagementPage(new TabShim(tab, mBrowserControlsManager, mTabModelSelector),
                     Profile.fromWebContents(tab.getWebContents()));
@@ -275,9 +261,6 @@
             case NativePageType.EXPLORE:
                 page = getBuilder().buildExploreSitesPage(tab);
                 break;
-            case NativePageType.LAUNCHPAD:
-                page = getBuilder().buildLaunchpadPage(tab);
-                break;
             case NativePageType.MANAGEMENT:
                 page = getBuilder().buildManagementPage(tab);
                 break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivity.java
deleted file mode 100644
index f7fe715..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivity.java
+++ /dev/null
@@ -1,53 +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.webapps.launchpad;
-
-import android.os.Bundle;
-
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.chrome.browser.SnackbarActivity;
-import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
-import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-import org.chromium.ui.modaldialog.ModalDialogManagerHolder;
-
-/**
- * Activity for displaying the app launcher.
- */
-public class LaunchpadActivity extends SnackbarActivity implements ModalDialogManagerHolder {
-    private LaunchpadCoordinator mLaunchpadCoordinator;
-    private ModalDialogManager mModalDialogManager;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mModalDialogManager = new ModalDialogManager(
-                new AppModalPresenter(this), ModalDialogManager.ModalDialogType.APP);
-
-        mLaunchpadCoordinator = new LaunchpadCoordinator(this, this::getModalDialogManager,
-                new SettingsLauncherImpl(), LaunchpadUtils.retrieveWebApks(this),
-                true /* isSeparateActivity */);
-        setContentView(mLaunchpadCoordinator.getView());
-    }
-
-    @Override
-    public ModalDialogManager getModalDialogManager() {
-        return mModalDialogManager;
-    }
-
-    @Override
-    protected void onDestroy() {
-        mLaunchpadCoordinator.destroy();
-        mLaunchpadCoordinator = null;
-        super.onDestroy();
-    }
-
-    @VisibleForTesting
-    LaunchpadCoordinator getCoordinatorForTesting() {
-        return mLaunchpadCoordinator;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadUtils.java
deleted file mode 100644
index 758c6e2..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadUtils.java
+++ /dev/null
@@ -1,93 +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.webapps.launchpad;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.Log;
-import org.chromium.base.PackageUtils;
-import org.chromium.chrome.browser.browserservices.intents.WebappInfo;
-import org.chromium.chrome.browser.webapps.WebApkIntentDataProviderFactory;
-import org.chromium.components.webapk.lib.client.WebApkValidator;
-import org.chromium.components.webapps.ShortcutSource;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- *  Utility class for launchpad.
- */
-public class LaunchpadUtils {
-    private static final String TAG = "LaunchpadUtils";
-    private static final String CATEGORY_WEBAPK_API = "android.intent.category.WEBAPK_API";
-
-    private static List<LaunchpadItem> sOverrideItemListForTesting;
-
-    private LaunchpadUtils() {}
-
-    /**
-     * Start LaunchpadActivity.
-     */
-    public static void showLaunchpadActivity(Activity activity) {
-        Intent intent = new Intent();
-        intent.setClass(activity, LaunchpadActivity.class);
-        activity.startActivity(intent);
-    }
-
-    /**
-     * Returns a list of |LaunchpadItem| with information for each installed WebAPK.
-     * This retrieves all installed WebAPKs by querying apps matches the WebAPK intent filter,
-     * and check whether it passes the signature checks.
-     */
-    public static List<LaunchpadItem> retrieveWebApks(Context context) {
-        if (sOverrideItemListForTesting != null) return sOverrideItemListForTesting;
-
-        List<LaunchpadItem> apps = new ArrayList<LaunchpadItem>();
-        PackageManager packageManager = context.getPackageManager();
-
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.addCategory(CATEGORY_WEBAPK_API);
-        for (ResolveInfo info :
-                packageManager.queryIntentServices(intent, PackageManager.MATCH_ALL)) {
-            String packageName = info.serviceInfo.packageName;
-            if (packageName != null && WebApkValidator.isValidV1WebApk(context, packageName)) {
-                if (!PackageUtils.isPackageInstalled(packageName)) {
-                    Log.e(TAG, info.serviceInfo.packageName + " doesn't exist");
-                } else {
-                    WebappInfo webApkInfo = WebappInfo.create(
-                            WebApkIntentDataProviderFactory.create(new Intent(), packageName, "",
-                                    ShortcutSource.UNKNOWN, false /* forceNavigation */,
-                                    false /* isSplashProvidedByWebApk */, null /* shareData */,
-                                    null /* shareDataActivityClassName */));
-                    if (webApkInfo != null) {
-                        LaunchpadItem item = new LaunchpadItem(webApkInfo.webApkPackageName(),
-                                webApkInfo.shortName(), webApkInfo.name(), webApkInfo.url(),
-                                webApkInfo.icon().bitmap(), webApkInfo.shortcutItems());
-                        apps.add(item);
-                    }
-                }
-            }
-        }
-
-        // Sort the list to make apps in alphabetical order.
-        Collections.sort(apps, (a, b) -> a.shortName.compareTo(b.shortName));
-        return apps;
-    }
-
-    /**
-     * Overrides the WebAPKs list for tests to avoid querying and verifying the installed WebAPKs.
-     */
-    @VisibleForTesting
-    public static void setOverrideItemListForTesting(List<LaunchpadItem> items) {
-        sOverrideItemListForTesting = items;
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java
deleted file mode 100644
index cc950f4..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java
+++ /dev/null
@@ -1,114 +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.webapps.launchpad;
-
-import android.support.test.InstrumentationRegistry;
-import android.view.View;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.ActivityTestUtils;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.test.util.RenderTestRule;
-
-import java.io.IOException;
-
-/**
- * Render tests for the LaunchpadActivity.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public final class LaunchpadActivityTest {
-    @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
-
-    @Rule
-    public RenderTestRule mRenderTestRule =
-            RenderTestRule.Builder.withPublicCorpus()
-                    .setRevision(1)
-                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_WEB_APP_INSTALLS)
-                    .build();
-
-    private LaunchpadActivity mLaunchpadActivity;
-    private LaunchpadCoordinator mLaunchpadCoordinator;
-
-    @Before
-    public void setUp() {
-        mActivityTestRule.startMainActivityOnBlankPage();
-        LaunchpadUtils.setOverrideItemListForTesting(LaunchpadTestUtils.MOCK_APP_LIST);
-    }
-
-    @After
-    public void tearDown() {
-        if (mLaunchpadActivity != null) {
-            mLaunchpadActivity.finish();
-        }
-    }
-
-    private void openLaunchpadActivity() {
-        mLaunchpadActivity = ActivityTestUtils.waitForActivity(
-                InstrumentationRegistry.getInstrumentation(), LaunchpadActivity.class, () -> {
-                    TestThreadUtils.runOnUiThreadBlocking(
-                            ()
-                                    -> LaunchpadUtils.showLaunchpadActivity(
-                                            mActivityTestRule.getActivity()));
-                });
-        mLaunchpadCoordinator = mLaunchpadActivity.getCoordinatorForTesting();
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"RenderTest"})
-    public void testShowLaunchpadActivity() throws IOException {
-        openLaunchpadActivity();
-        mRenderTestRule.render(mLaunchpadCoordinator.getView(), "launchpad_activity");
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"RenderTest"})
-    public void testAppManagementMenuWithoutShortcut() throws IOException {
-        openLaunchpadActivity();
-        View dialogView = LaunchpadTestUtils.openAppManagementMenu(mLaunchpadCoordinator,
-                mLaunchpadActivity.getModalDialogManager(), 1 /* itemIndex */);
-
-        mRenderTestRule.render(dialogView, "launchpad_management_menu");
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"RenderTest"})
-    public void testAppManagementMenuWithPermissions() throws IOException {
-        openLaunchpadActivity();
-        LaunchpadTestUtils.setPermissionDefaults(LaunchpadTestUtils.APP_URL_2);
-
-        View dialogView = LaunchpadTestUtils.openAppManagementMenu(mLaunchpadCoordinator,
-                mLaunchpadActivity.getModalDialogManager(), 1 /* itemIndex */);
-
-        mRenderTestRule.render(dialogView, "launchpad_management_menu_with_permissions");
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"RenderTest"})
-    public void testAppManagementMenuWithShortcut() throws IOException {
-        openLaunchpadActivity();
-        View dialogView = LaunchpadTestUtils.openAppManagementMenu(mLaunchpadCoordinator,
-                mLaunchpadActivity.getModalDialogManager(), 0 /* itemIndex */);
-
-        mRenderTestRule.render(dialogView, "launchpad_management_menu_shortcuts");
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPageTest.java
deleted file mode 100644
index 0c5bb326..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPageTest.java
+++ /dev/null
@@ -1,276 +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.webapps.launchpad;
-
-import static androidx.test.espresso.intent.Intents.intended;
-import static androidx.test.espresso.intent.Intents.intending;
-import static androidx.test.espresso.intent.Intents.times;
-import static androidx.test.espresso.intent.matcher.IntentMatchers.hasData;
-import static androidx.test.espresso.intent.matcher.IntentMatchers.hasPackage;
-
-import static org.hamcrest.Matchers.allOf;
-
-import android.app.Activity;
-import android.app.Instrumentation.ActivityResult;
-import android.graphics.drawable.BitmapDrawable;
-import android.support.test.InstrumentationRegistry;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import androidx.appcompat.content.res.AppCompatResources;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.test.espresso.intent.Intents;
-import androidx.test.filters.MediumTest;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.settings.SettingsActivity;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.ActivityTestUtils;
-import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.browser_ui.site_settings.SingleWebsiteSettings;
-import org.chromium.components.browser_ui.widget.tile.TileView;
-import org.chromium.components.embedder_support.util.UrlConstants;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.content_public.browser.test.util.TouchCommon;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-
-/**
- * Tests for the Launchpad page.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@Features.EnableFeatures({ChromeFeatureList.APP_LAUNCHPAD})
-public class LaunchpadPageTest {
-    @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
-
-    private LaunchpadCoordinator mLaunchpadCoordinator;
-
-    private RecyclerView mItemContainer;
-
-    @Before
-    public void setUp() {
-        mActivityTestRule.startMainActivityOnBlankPage();
-        LaunchpadUtils.setOverrideItemListForTesting(LaunchpadTestUtils.MOCK_APP_LIST);
-    }
-
-    private void openLaunchpadPage() {
-        mActivityTestRule.loadUrl(UrlConstants.LAUNCHPAD_URL);
-        mLaunchpadCoordinator =
-                ((LaunchpadPage) mActivityTestRule.getActivity().getActivityTab().getNativePage())
-                        .getCoordinatorForTesting();
-        mItemContainer = mLaunchpadCoordinator.getView().findViewById(R.id.launchpad_recycler);
-    }
-
-    @Test
-    @SmallTest
-    public void testOpenLaunchpad() {
-        openLaunchpadPage();
-
-        Assert.assertEquals(2, mItemContainer.getAdapter().getItemCount());
-
-        TileView app1 = (TileView) mItemContainer.getChildAt(0);
-        TextView title1 = (TextView) app1.findViewById(R.id.tile_view_title);
-        ImageView icon1 = (ImageView) app1.findViewById(R.id.tile_view_icon);
-        Assert.assertEquals(LaunchpadTestUtils.APP_SHORT_NAME_1, title1.getText());
-        Assert.assertEquals(1, title1.getLineCount());
-        Assert.assertEquals(
-                LaunchpadTestUtils.TEST_ICON, ((BitmapDrawable) icon1.getDrawable()).getBitmap());
-
-        TileView app2 = (TileView) mItemContainer.getChildAt(1);
-        TextView title2 = (TextView) app2.findViewById(R.id.tile_view_title);
-        ImageView icon2 = (ImageView) app2.findViewById(R.id.tile_view_icon);
-        Assert.assertEquals(LaunchpadTestUtils.APP_SHORT_NAME_2, title2.getText());
-        Assert.assertEquals(1, title2.getLineCount());
-        Assert.assertEquals(
-                LaunchpadTestUtils.TEST_ICON, ((BitmapDrawable) icon2.getDrawable()).getBitmap());
-    }
-
-    @Test
-    @SmallTest
-    public void testLaunchWebApk() {
-        openLaunchpadPage();
-
-        Intents.init();
-        intending(allOf(hasPackage(LaunchpadTestUtils.APP_PACKAGE_NAME_1)))
-                .respondWith(new ActivityResult(Activity.RESULT_OK, null));
-
-        View item = mItemContainer.getChildAt(0);
-        TestThreadUtils.runOnUiThreadBlocking(() -> TouchCommon.singleClickView(item));
-
-        intended(allOf(hasPackage(LaunchpadTestUtils.APP_PACKAGE_NAME_1),
-                         hasData(LaunchpadTestUtils.APP_URL_1)),
-                times(1));
-        Intents.release();
-    }
-
-    @Test
-    @MediumTest
-    public void testManagementMenuHeaderProperties() {
-        openLaunchpadPage();
-        View dialogView = LaunchpadTestUtils.openAppManagementMenu(mLaunchpadCoordinator,
-                mActivityTestRule.getActivity().getModalDialogManager(), 1 /* itemIndex */);
-
-        Assert.assertEquals(LaunchpadTestUtils.APP_NAME_2,
-                ((TextView) dialogView.findViewById(R.id.menu_header_title)).getText());
-        Assert.assertEquals(LaunchpadTestUtils.APP_URL_2,
-                ((TextView) dialogView.findViewById(R.id.menu_header_url)).getText());
-        ImageView icon = (ImageView) dialogView.findViewById(R.id.menu_header_image);
-        Assert.assertEquals(
-                LaunchpadTestUtils.TEST_ICON, ((BitmapDrawable) icon.getDrawable()).getBitmap());
-    }
-
-    @Test
-    @MediumTest
-    @DisabledTest(message = "https://crbug.com/1271233")
-    public void testManagementMenuAppPermissions() {
-        LaunchpadTestUtils.setPermissionDefaults(LaunchpadTestUtils.APP_URL_2);
-        openLaunchpadPage();
-        View dialogView = LaunchpadTestUtils.openAppManagementMenu(mLaunchpadCoordinator,
-                mActivityTestRule.getActivity().getModalDialogManager(), 1 /* itemIndex */);
-
-        // Icons for permissions that set to "ALLOW" or "BLOCK" are enabled.
-        int[] enabledIcons = {R.id.notifications_button, R.id.mic_button, R.id.camera_button};
-        for (int id : enabledIcons) {
-            ImageView icon = (ImageView) dialogView.findViewById(id);
-            Assert.assertEquals(
-                    AppCompatResources.getColorStateList(
-                            mActivityTestRule.getActivity(), R.color.default_icon_color_tint_list),
-                    icon.getImageTintList());
-            Assert.assertTrue(icon.isEnabled());
-        }
-
-        ImageView locationIcon = (ImageView) dialogView.findViewById(R.id.location_button);
-        Assert.assertEquals(AppCompatResources.getColorStateList(mActivityTestRule.getActivity(),
-                                    R.color.default_icon_color_disabled),
-                locationIcon.getImageTintList());
-        Assert.assertFalse(locationIcon.isEnabled());
-    }
-
-    @Test
-    @MediumTest
-    @DisabledTest(message = "crbug.com/1306215")
-    public void testManagementMenuAppShortcutsProperties() {
-        openLaunchpadPage();
-        View dialogView = LaunchpadTestUtils.openAppManagementMenu(mLaunchpadCoordinator,
-                mActivityTestRule.getActivity().getModalDialogManager(), 0 /* itemIndex */);
-
-        ListView listView = dialogView.findViewById(R.id.shortcuts_list_view);
-
-        // The test app has 2 shortcut and 2 other menu item: uninstall and site settings.
-        Assert.assertEquals(4, listView.getChildCount());
-
-        // Assert icon and name in shortcut item view is set correctly.
-        View shortcut1 = listView.getChildAt(0);
-        Assert.assertEquals(LaunchpadTestUtils.SHORTCUT_NAME_1,
-                ((TextView) shortcut1.findViewById(R.id.shortcut_name)).getText());
-        ImageView icon = (ImageView) shortcut1.findViewById(R.id.shortcut_icon);
-        Assert.assertEquals(
-                LaunchpadTestUtils.TEST_ICON, ((BitmapDrawable) icon.getDrawable()).getBitmap());
-        Assert.assertEquals(View.VISIBLE, icon.getVisibility());
-
-        View shortcut2 = listView.getChildAt(1);
-        Assert.assertEquals(LaunchpadTestUtils.SHORTCUT_NAME_2,
-                ((TextView) shortcut2.findViewById(R.id.shortcut_name)).getText());
-        icon = (ImageView) shortcut2.findViewById(R.id.shortcut_icon);
-        Assert.assertEquals(
-                LaunchpadTestUtils.TEST_ICON, ((BitmapDrawable) icon.getDrawable()).getBitmap());
-        Assert.assertEquals(View.VISIBLE, icon.getVisibility());
-    }
-
-    @Test
-    @MediumTest
-    @DisabledTest(message = "crbug.com/1306215")
-    public void testLaunchAppShortcuts() {
-        openLaunchpadPage();
-        ModalDialogManager modalDialogManager =
-                mActivityTestRule.getActivity().getModalDialogManager();
-        View dialogView = LaunchpadTestUtils.openAppManagementMenu(
-                mLaunchpadCoordinator, modalDialogManager, 0 /* itemIndex */);
-
-        ListView listView = dialogView.findViewById(R.id.shortcuts_list_view);
-        View shortcut = listView.getChildAt(0);
-
-        Intents.init();
-        intending(allOf(hasPackage(LaunchpadTestUtils.APP_PACKAGE_NAME_1)))
-                .respondWith(new ActivityResult(Activity.RESULT_OK, null));
-        TestThreadUtils.runOnUiThreadBlocking(() -> TouchCommon.singleClickView(shortcut));
-        intended(allOf(hasPackage(LaunchpadTestUtils.APP_PACKAGE_NAME_1),
-                         hasData(LaunchpadTestUtils.SHORTCUT_URL_1)),
-                times(1));
-        Intents.release();
-
-        // Assert dialog is dismissed.
-        Assert.assertNull(modalDialogManager.getCurrentDialogForTest());
-    }
-
-    @Test
-    @MediumTest
-    @DisabledTest(message = "crbug.com/1306215")
-    public void testManagementMenuOtherMenuItemProperties() {
-        openLaunchpadPage();
-        View dialogView = LaunchpadTestUtils.openAppManagementMenu(mLaunchpadCoordinator,
-                mActivityTestRule.getActivity().getModalDialogManager(), 1 /* itemIndex */);
-
-        ListView listView = dialogView.findViewById(R.id.shortcuts_list_view);
-
-        // The test app has no shortcut but 2 other menu items: uninstall and site settings.
-        Assert.assertEquals(2, listView.getChildCount());
-
-        // Assert the uninstall item has correct name, and icon visibility is GONE.
-        View item1 = listView.getChildAt(0);
-        Assert.assertEquals(mActivityTestRule.getActivity().getResources().getString(
-                                    R.string.launchpad_menu_uninstall),
-                ((TextView) item1.findViewById(R.id.shortcut_name)).getText());
-        Assert.assertEquals(View.GONE, item1.findViewById(R.id.shortcut_icon).getVisibility());
-
-        // Assert the site settings item has correct name, and icon visibility is GONE.
-        View item2 = listView.getChildAt(1);
-        Assert.assertEquals(mActivityTestRule.getActivity().getResources().getString(
-                                    R.string.launchpad_menu_site_settings),
-                ((TextView) item2.findViewById(R.id.shortcut_name)).getText());
-        Assert.assertEquals(View.GONE, item2.findViewById(R.id.shortcut_icon).getVisibility());
-    }
-
-    @Test
-    @MediumTest
-    @DisabledTest(message = "crbug.com/1306215")
-    public void testLaunchAppSetting_whenSiteSettingsMenuItemClicked() {
-        openLaunchpadPage();
-        ModalDialogManager modalDialogManager =
-                mActivityTestRule.getActivity().getModalDialogManager();
-        View dialogView = LaunchpadTestUtils.openAppManagementMenu(
-                mLaunchpadCoordinator, modalDialogManager, 1 /* itemIndex */);
-        ListView listView = dialogView.findViewById(R.id.shortcuts_list_view);
-
-        // Click the menu item and assert site setting page is opened.
-        SettingsActivity activity =
-                ActivityTestUtils.waitForActivity(InstrumentationRegistry.getInstrumentation(),
-                        SettingsActivity.class, new Runnable() {
-                            @Override
-                            public void run() {
-                                TouchCommon.singleClickView(listView.getChildAt(1));
-                            }
-                        });
-        Assert.assertNotNull(activity);
-        SingleWebsiteSettings fragment =
-                ActivityTestUtils.waitForFragmentToAttach(activity, SingleWebsiteSettings.class);
-        Assert.assertNotNull(fragment);
-        activity.finish();
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadTestUtils.java
deleted file mode 100644
index cfb5009..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadTestUtils.java
+++ /dev/null
@@ -1,103 +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.webapps.launchpad;
-
-import static org.junit.Assert.assertNotNull;
-
-import android.graphics.Bitmap;
-import android.view.View;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.chromium.chrome.browser.browserservices.intents.WebApkExtras.ShortcutItem;
-import org.chromium.chrome.browser.browserservices.intents.WebappIcon;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.components.browser_ui.site_settings.PermissionInfo;
-import org.chromium.components.content_settings.ContentSettingValues;
-import org.chromium.components.content_settings.ContentSettingsType;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.content_public.browser.test.util.TouchCommon;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-import org.chromium.ui.modaldialog.ModalDialogProperties;
-import org.chromium.ui.modelutil.PropertyModel;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Utility class for Launchpad instrumentation tests.
- */
-public class LaunchpadTestUtils {
-    public static final String APP_PACKAGE_NAME_1 = "package.name.1";
-    public static final String APP_NAME_1 = "App Name 1";
-    public static final String APP_SHORT_NAME_1 = "App 1";
-    public static final String APP_URL_1 = "https://example1.com/";
-
-    public static final String APP_PACKAGE_NAME_2 = "package.name.2";
-    public static final String APP_NAME_2 = "App Name 2";
-    public static final String APP_SHORT_NAME_2 = "App 2 with long short name";
-    public static final String APP_URL_2 = "https://example2.com/";
-    public static final Bitmap TEST_ICON = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_8888);
-
-    public static final String SHORTCUT_NAME_1 = "Test Shortcut 1";
-    public static final String SHORTCUT_URL_1 = "https://example1.com/11";
-    public static final String SHORTCUT_NAME_2 = "Test Shortcut 2";
-    public static final String SHORTCUT_URL_2 = "https://example1.com/22";
-
-    public static final List<ShortcutItem> MOCK_SHORTCUTS = new ArrayList<>(
-            Arrays.asList(new ShortcutItem(SHORTCUT_NAME_1, SHORTCUT_NAME_1, SHORTCUT_URL_1,
-                                  "iconUrl", "iconHash", new WebappIcon(TEST_ICON)),
-                    new ShortcutItem(SHORTCUT_NAME_2, SHORTCUT_NAME_2, SHORTCUT_URL_2, "iconUrl",
-                            "iconHash", new WebappIcon(TEST_ICON))));
-    // LaunchpadItem 1 with shortcuts.
-    public static final LaunchpadItem LAUNCHPAD_ITEM_1 = new LaunchpadItem(
-            APP_PACKAGE_NAME_1, APP_SHORT_NAME_1, APP_NAME_1, APP_URL_1, TEST_ICON, MOCK_SHORTCUTS);
-    // LaunchpadItem 2 with no shortcuts.
-    public static final LaunchpadItem LAUNCHPAD_ITEM_2 = new LaunchpadItem(APP_PACKAGE_NAME_2,
-            APP_SHORT_NAME_2, APP_NAME_2, APP_URL_2, TEST_ICON, new ArrayList<ShortcutItem>());
-
-    // A mock app list, 1st item includes a list of ShortcutItems, 2nd item has no shortcuts.
-    public static final List<LaunchpadItem> MOCK_APP_LIST =
-            new ArrayList<>(Arrays.asList(LAUNCHPAD_ITEM_1, LAUNCHPAD_ITEM_2));
-
-    private LaunchpadTestUtils() {}
-
-    /**
-     * Set permissions to run the test. Notifications set to Block, Mic set to Allow,
-     * Camera set to Allow, Location set to ASK.
-     */
-    public static void setPermissionDefaults(String url) {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            Profile profile = Profile.getLastUsedRegularProfile();
-            PermissionInfo notifications = new PermissionInfo(ContentSettingsType.NOTIFICATIONS,
-                    url, null /* embedder */, false /* isEmbargoed */);
-            notifications.setContentSetting(profile, ContentSettingValues.BLOCK);
-            PermissionInfo mic = new PermissionInfo(ContentSettingsType.MEDIASTREAM_MIC, url,
-                    null /* embedder */, false /* isEmbargoed */);
-            mic.setContentSetting(profile, ContentSettingValues.ALLOW);
-            PermissionInfo camera = new PermissionInfo(ContentSettingsType.MEDIASTREAM_CAMERA, url,
-                    null /* embedder */, false /* isEmbargoed */);
-            camera.setContentSetting(profile, ContentSettingValues.ALLOW);
-            PermissionInfo location = new PermissionInfo(ContentSettingsType.GEOLOCATION, url,
-                    null /* embedder */, false /* isEmbargoed */);
-            location.setContentSetting(profile, ContentSettingValues.ASK);
-        });
-    }
-
-    /**
-     * Helper function for opening the Management Menu for the LaunchpadItem at {@link itemIndex}
-     * and return the dialog view.
-     */
-    public static View openAppManagementMenu(
-            LaunchpadCoordinator coordinator, ModalDialogManager dialogManager, int itemIndex) {
-        View item = ((RecyclerView) coordinator.getView().findViewById(R.id.launchpad_recycler))
-                            .getChildAt(itemIndex);
-        TouchCommon.longPressView(item);
-        PropertyModel dialogModel = dialogManager.getCurrentDialogForTest();
-        assertNotNull(dialogModel);
-        return dialogModel.get(ModalDialogProperties.CUSTOM_VIEW);
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/OWNER b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/OWNER
deleted file mode 100644
index 16cbd4b..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/OWNER
+++ /dev/null
@@ -1,2 +0,0 @@
-peconn@chromium.org
-eirage@chromium.org
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index d84a12e..d96ccd44 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-109.0.5414.14_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-109.0.5414.16_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index b14654ad..d83d553 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -1282,7 +1282,10 @@
     Light background
   </message>
   <message name="IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_BACKGROUND_SHADING_DESCRIPTION" desc="In the Select-to-speak settings subpage, the label for option to fade the background outside of the focus ring to improve focus on what is being spoken.">
-    Shade background content
+    Dim background content
+  </message>
+  <message name="IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_SAMPLE_TEXT" desc="In the Select-to-speak settings subpage, the sample text around which will be drawn a Select to Speak visual preview. This should be less than one line long.">
+    The quick brown fox jumped over the lazy dog.
   </message>
   <message name="IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_NAVIGATION_CONTROLS_DESCRIPTION" desc="In the Select-to-speak settings subpage, the label for option to show navigation controls, such as going to the previous or next paragraph, when Select-to-speak is activated.">
     Enable navigation controls
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_BACKGROUND_SHADING_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_BACKGROUND_SHADING_DESCRIPTION.png.sha1
index c9fee82..07596f8 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_BACKGROUND_SHADING_DESCRIPTION.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_BACKGROUND_SHADING_DESCRIPTION.png.sha1
@@ -1 +1 @@
-899332c6af7adcbee0b27a07173c8ad457f0df77
\ No newline at end of file
+e67a810008b7b0a19648d6606c85e1aec158c9ff
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_SAMPLE_TEXT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_SAMPLE_TEXT.png.sha1
new file mode 100644
index 0000000..5426d01
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_SAMPLE_TEXT.png.sha1
@@ -0,0 +1 @@
+54d146f6ac515a67505495592e6c0f13b6d29291
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 670e256..3def3cc 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -431,18 +431,6 @@
 #endif  // ENABLE_VR
 
 #if BUILDFLAG(IS_ANDROID)
-const FeatureEntry::FeatureParam kElasticOverscrollFilterType[] = {
-    {features::kElasticOverscrollType, features::kElasticOverscrollTypeFilter}};
-const FeatureEntry::FeatureParam kElasticOverscrollTransformType[] = {
-    {features::kElasticOverscrollType,
-     features::kElasticOverscrollTypeTransform}};
-
-const FeatureEntry::FeatureVariation kElasticOverscrollVariations[] = {
-    {"Pixel shader stretch", kElasticOverscrollFilterType,
-     std::size(kElasticOverscrollFilterType), nullptr},
-    {"Transform stretch", kElasticOverscrollTransformType,
-     std::size(kElasticOverscrollTransformType), nullptr}};
-
 const FeatureEntry::FeatureParam kCCTResizablePolicyParamUseAllowlist[] = {
     {"default_policy", "use-allowlist"}};
 const FeatureEntry::FeatureParam kCCTResizablePolicyParamUseDenylist[] = {
@@ -7122,16 +7110,10 @@
      flag_descriptions::kScrollUnificationDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kScrollUnification)},
 
-#if BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
     {"elastic-overscroll", flag_descriptions::kElasticOverscrollName,
      flag_descriptions::kElasticOverscrollDescription, kOsWin | kOsAndroid,
      FEATURE_VALUE_TYPE(features::kElasticOverscroll)},
-#elif BUILDFLAG(IS_ANDROID)
-    {"elastic-overscroll", flag_descriptions::kElasticOverscrollName,
-     flag_descriptions::kElasticOverscrollDescription, kOsWin | kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(features::kElasticOverscroll,
-                                    kElasticOverscrollVariations,
-                                    "ElasticOverscroll")},
 #endif
 
     {"device-posture", flag_descriptions::kDevicePostureName,
diff --git a/chrome/browser/android/webapps/launchpad/BUILD.gn b/chrome/browser/android/webapps/launchpad/BUILD.gn
deleted file mode 100644
index 63a0563..0000000
--- a/chrome/browser/android/webapps/launchpad/BUILD.gn
+++ /dev/null
@@ -1,98 +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("//build/config/android/rules.gni")
-
-android_library("java") {
-  sources = [
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppListCoordinator.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppListMediator.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinator.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderProperties.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderViewBinder.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinator.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsMediator.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsProperties.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsView.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsViewBinder.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinator.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadItem.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPage.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/ShortcutItemProperties.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/ShortcutItemViewBinder.java",
-  ]
-
-  deps = [
-    ":java_resources",
-    "//base:base_java",
-    "//chrome/android/webapk/libs/client:client_java",
-    "//chrome/browser/android/browserservices/intents:java",
-    "//chrome/browser/notifications:java",
-    "//chrome/browser/profiles/android:java",
-    "//chrome/browser/ui/android/native_page:java",
-    "//components/browser_ui/settings/android:java",
-    "//components/browser_ui/site_settings/android:java",
-    "//components/browser_ui/widget/android:java",
-    "//components/content_settings/android:content_settings_enums_java",
-    "//components/embedder_support/android:util_java",
-    "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_appcompat_appcompat_java",
-    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
-    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
-    "//ui/android:ui_full_java",
-  ]
-  resources_package = "org.chromium.chrome.browser.webapps.launchpad"
-}
-
-android_resources("java_resources") {
-  sources = [
-    "java/res/drawable/gm_filled_location_off_24.xml",
-    "java/res/drawable/gm_filled_mic_off_24.xml",
-    "java/res/drawable/gm_filled_notifications_off_24.xml",
-    "java/res/drawable/gm_filled_videocam_off_24.xml",
-    "java/res/layout/launchpad_app_menu_header.xml",
-    "java/res/layout/launchpad_app_menu_permissions.xml",
-    "java/res/layout/launchpad_menu_dialog_layout.xml",
-    "java/res/layout/launchpad_menu_dialog_layout.xml",
-    "java/res/layout/launchpad_page_layout.xml",
-    "java/res/layout/launchpad_shortcut_item_view.xml",
-    "java/res/layout/launchpad_tile_view.xml",
-    "java/res/menu/launchpad_action_bar_menu.xml",
-    "java/res/values/dimens.xml",
-  ]
-
-  deps = [
-    "//chrome/browser/ui/android/strings:ui_strings_grd",
-    "//components/browser_ui/widget/android:java_resources",
-    "//components/permissions/android:java_resources",
-  ]
-}
-
-robolectric_library("junit_tests") {
-  sources = [
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinatorTest.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinatorTest.java",
-    "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinatorTest.java",
-  ]
-
-  deps = [
-    ":java",
-    ":java_resources",
-    "//base:base_java",
-    "//base:base_java_test_support",
-    "//base:base_junit_test_support",
-    "//chrome/browser/android/browserservices/intents:java",
-    "//chrome/browser/profiles/android:java",
-    "//chrome/test/android:chrome_java_unit_test_support",
-    "//components/browser_ui/settings/android:java",
-    "//components/browser_ui/site_settings/android:java",
-    "//components/content_settings/android:content_settings_enums_java",
-    "//components/embedder_support/android:util_java",
-    "//content/public/android:content_full_java",
-    "//third_party/androidx:androidx_appcompat_appcompat_java",
-    "//third_party/junit",
-    "//third_party/mockito:mockito_java",
-    "//ui/android:ui_full_java",
-  ]
-}
diff --git a/chrome/browser/android/webapps/launchpad/DEPS b/chrome/browser/android/webapps/launchpad/DEPS
deleted file mode 100644
index b22da55..0000000
--- a/chrome/browser/android/webapps/launchpad/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-include_rules = [
-  "+chrome/android/webapk/libs/client",
-  "+chrome/browser/ui/android",
-  "+components/embedder_support/android",
-]
diff --git a/chrome/browser/android/webapps/launchpad/OWNERS b/chrome/browser/android/webapps/launchpad/OWNERS
deleted file mode 100644
index 16cbd4b..0000000
--- a/chrome/browser/android/webapps/launchpad/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-peconn@chromium.org
-eirage@chromium.org
diff --git a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_location_off_24.xml b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_location_off_24.xml
deleted file mode 100644
index 2001e0dc..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_location_off_24.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:tint="?attr/colorControlNormal">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M12,6.88c1.38,0 2.5,1.12 2.5,2.5c0,0.64 -0.25,1.21 -0.64,1.65l3.38,3.38C18.24,12.95 19,11.39 19,9.13c0,-3.87 -3.13,-7 -7,-7c-1.94,0 -3.7,0.79 -4.97,2.07l3.32,3.32C10.79,7.13 11.36,6.88 12,6.88z"/>
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M2.81,2.81L1.39,4.22l3.72,3.72C5.04,8.33 5,8.72 5,9.13c0,5.34 4.21,6.79 6.03,12.28c0.14,0.42 0.52,0.72 0.97,0.72s0.83,-0.3 0.97,-0.72c0.49,-1.49 1.17,-2.68 1.88,-3.74l4.93,4.93l1.41,-1.41L2.81,2.81z"/>
-</vector>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_mic_off_24.xml b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_mic_off_24.xml
deleted file mode 100644
index 3cf45ebd..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_mic_off_24.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:tint="?attr/colorControlNormal">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M19,11h-2c0,0.91 -0.25,1.76 -0.68,2.49l1.45,1.45C18.54,13.82 19,12.47 19,11zM2.81,2.81L1.39,4.22l11.66,11.66C12.71,15.96 12.36,16 12,16c-2.76,0 -5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c0.57,-0.08 1.12,-0.24 1.64,-0.45l5.14,5.14l1.41,-1.41L2.81,2.81zM15,11V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v1.17l5.81,5.81C14.92,11.67 15,11.35 15,11z"/>
-</vector>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_notifications_off_24.xml b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_notifications_off_24.xml
deleted file mode 100644
index a62f2bc..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_notifications_off_24.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:tint="?attr/colorControlNormal">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M18,10c0,-2.79 -1.91,-5.14 -4.5,-5.8V3.5C13.5,2.67 12.83,2 12,2s-1.5,0.67 -1.5,1.5v0.7C9.64,4.42 8.87,4.84 8.21,5.38L18,15.17V10zM12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22zM2.81,2.81L1.39,4.22L6.1,8.93C6.04,9.28 6,9.63 6,10v7H4v2h12.17l3.61,3.61l1.41,-1.41L2.81,2.81z"/>
-</vector>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_videocam_off_24.xml b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_videocam_off_24.xml
deleted file mode 100644
index 659fe8bb..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_videocam_off_24.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:tint="?attr/colorControlNormal">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M18,15.17v-1.65l4,3.98v-11l-4,3.98V6c0,-1.1 -0.9,-2 -2,-2H6.83L18,15.17z"/>
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M18,18L2.1,2.1L0.69,3.51l1.56,1.56C2.09,5.35 2,5.66 2,6v12c0,1.1 0.9,2 2,2h12c0.34,0 0.65,-0.09 0.93,-0.24l3.56,3.56l1.41,-1.41L18,18L18,18z"/>
-</vector>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_app_menu_header.xml b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_app_menu_header.xml
deleted file mode 100644
index 9963eb1..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_app_menu_header.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingTop="16dp"
-    android:paddingBottom="8dp"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp"
-    android:orientation="horizontal">
-
-  <ImageView
-      android:id="@+id/menu_header_image"
-      android:layout_width="@dimen/launchpad_menu_header_image_size"
-      android:layout_height="@dimen/launchpad_menu_header_image_size"
-      android:layout_marginEnd="16dp"
-      android:importantForAccessibility="no"
-      android:scaleType="centerInside"
-      android:src="@color/thumbnail_placeholder_on_primary_bg" />
-
-  <LinearLayout
-      android:id="@+id/title_and_url"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:layout_gravity="center_vertical"
-      android:orientation="vertical">
-
-    <org.chromium.ui.widget.TextViewWithLeading
-        android:id="@+id/menu_header_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:ellipsize="end"
-        android:maxLines="1"
-        android:textAppearance="@style/TextAppearance.TextSmall.Primary"
-        app:leading="@dimen/text_size_small_leading" />
-
-    <org.chromium.ui.widget.TextViewWithLeading
-        android:id="@+id/menu_header_url"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:ellipsize="end"
-        android:maxLines="1"
-        android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
-        app:leading="@dimen/text_size_small_leading" />
-  </LinearLayout>
-</LinearLayout>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_app_menu_permissions.xml b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_app_menu_permissions.xml
deleted file mode 100644
index 080dda0..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_app_menu_permissions.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<org.chromium.chrome.browser.webapps.launchpad.AppManagementMenuPermissionsView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp"
-    android:orientation="horizontal">
-
-  <org.chromium.ui.widget.ChromeImageButton
-      android:id="@+id/notifications_button"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:background="@null"
-      android:src="@drawable/gm_filled_notifications_24"
-      android:contentDescription="@string/push_notifications_permission_title" />
-  <Space
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_weight="1" />
-  <org.chromium.ui.widget.ChromeImageButton
-      android:id="@+id/mic_button"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:background="@null"
-      android:src="@drawable/gm_filled_mic_24"
-      android:contentDescription="@string/website_settings_use_mic" />
-  <Space
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_weight="1" />
-  <org.chromium.ui.widget.ChromeImageButton
-      android:id="@+id/camera_button"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:background="@null"
-      android:src="@drawable/gm_filled_videocam_24"
-      android:contentDescription="@string/website_settings_use_camera" />
-  <Space
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_weight="1" />
-  <org.chromium.ui.widget.ChromeImageButton
-      android:id="@+id/location_button"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:background="@null"
-      android:src="@drawable/gm_filled_location_on_24"
-      android:contentDescription="@string/website_settings_device_location" />
-</org.chromium.chrome.browser.webapps.launchpad.AppManagementMenuPermissionsView>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_menu_dialog_layout.xml b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_menu_dialog_layout.xml
deleted file mode 100644
index f83eddc1..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_menu_dialog_layout.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:padding="0dp"
-    android:paddingStart="0dp"
-    android:paddingEnd="0dp"
-    android:orientation="vertical">
-
-  <include
-      android:id="@+id/dialog_header"
-      layout="@layout/launchpad_app_menu_header" />
-
-  <include
-      android:id="@+id/permissions"
-      layout="@layout/launchpad_app_menu_permissions" />
-
-  <View
-      style="@style/HorizontalDivider"/>
-
-  <ListView
-      android:id="@+id/shortcuts_list_view"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:paddingBottom="8dp"
-      android:clipToPadding="false"
-      android:divider="@null"
-      android:listSelector="@android:color/transparent"
-      android:textAppearance="@style/TextAppearance.Headline.Primary" />
-</LinearLayout>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_page_layout.xml b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_page_layout.xml
deleted file mode 100644
index b80b3894..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_page_layout.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/launchpad_page"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <androidx.appcompat.widget.Toolbar
-        android:id="@+id/toolbar"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/selectable_list_toolbar_height"
-        style="@style/ModernToolbar" />
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/launchpad_recycler"
-        android:background="@macro/default_bg_color"
-        android:scrollbars="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/selectable_list_toolbar_height" />
-
-
-    <org.chromium.components.browser_ui.widget.FadingShadowView
-        android:id="@+id/shadow"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/action_bar_shadow_height"
-        android:layout_marginTop="@dimen/selectable_list_toolbar_height" />
-</FrameLayout>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_shortcut_item_view.xml b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_shortcut_item_view.xml
deleted file mode 100644
index 1616ba7..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_shortcut_item_view.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp"
-    android:orientation="horizontal">
-
-  <org.chromium.ui.widget.ChromeImageView
-      android:id="@+id/shortcut_icon"
-      style="@style/ListItemStartIcon"
-      android:scaleType="centerInside" />
-
-  <org.chromium.ui.widget.TextViewWithLeading
-      android:id="@+id/shortcut_name"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:ellipsize="end"
-      android:gravity="center_vertical"
-      android:maxLines="1"
-      android:minHeight="@dimen/min_touch_target_size"
-      android:textAppearance="@style/TextAppearance.TextMedium.Primary"
-      app:leading="@dimen/text_size_small_leading" />
-</LinearLayout>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_tile_view.xml b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_tile_view.xml
deleted file mode 100644
index eb0343f..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_tile_view.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<org.chromium.components.browser_ui.widget.tile.TileView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/tile_view_width"
-    android:layout_height="wrap_content" >
-    <include
-        layout="@layout/tile_view_modern"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-  </org.chromium.components.browser_ui.widget.tile.TileView>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/menu/launchpad_action_bar_menu.xml b/chrome/browser/android/webapps/launchpad/java/res/menu/launchpad_action_bar_menu.xml
deleted file mode 100644
index ed31fac..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/menu/launchpad_action_bar_menu.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-  <item
-      android:id="@+id/close_menu_id"
-      android:icon="@drawable/btn_close"
-      android:title="@string/close"
-      app:showAsAction="ifRoom"
-      app:iconTint="@color/default_icon_color_tint_list" />
-
-</menu>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/values/dimens.xml b/chrome/browser/android/webapps/launchpad/java/res/values/dimens.xml
deleted file mode 100644
index 4eba32b..0000000
--- a/chrome/browser/android/webapps/launchpad/java/res/values/dimens.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<resources xmlns:tools="http://schemas.android.com/tools">
-  <dimen name="launchpad_menu_header_image_size">60dp</dimen>
-</resources>
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppListCoordinator.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppListCoordinator.java
deleted file mode 100644
index e119818..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppListCoordinator.java
+++ /dev/null
@@ -1,80 +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.webapps.launchpad;
-
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.chromium.components.browser_ui.widget.tile.TileView;
-import org.chromium.components.browser_ui.widget.tile.TileViewBinder;
-import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
-import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
-
-import java.util.List;
-
-/**
- * The coordinator to display the list of installed web apps in launchpad.
- */
-class AppListCoordinator {
-    /** The Mediator responsible for managing the list of apps to be displayed. */
-    private AppListMediator mMediator;
-
-    /** The coodinaator for showing the app management menu. */
-    private AppManagementMenuCoordinator mMenuCoordinator;
-
-    /**
-     * The number of columns to show.
-     * TODO(eirage): The number of column should not be fixed, but change with screen size.
-     */
-    private final int mColumns = 4;
-
-    /**
-     * The SimpleRecyclerViewAdapter expects each tile to have a unique id, but
-     * in our case there is only one type.
-     */
-    static final int DEFAULT_TILE_TYPE = 0;
-
-    /**
-     * Creates and initialize the AppListCoordinator. It set up the adapter for the RecyclerView
-     * and creates the AppListMediator for creating and updating the app list.
-     * @param view The RecyclerView to hold the list of web apps.
-     * @param items The list of LaunchpadItems to be displayed.
-     */
-    AppListCoordinator(RecyclerView view, AppManagementMenuCoordinator menuCoordinator,
-            List<LaunchpadItem> items) {
-        GridLayoutManager layoutManager = new GridLayoutManager(view.getContext(), mColumns);
-        view.setLayoutManager(layoutManager);
-
-        ModelList modelList = new ModelList();
-        SimpleRecyclerViewAdapter adapter = new SimpleRecyclerViewAdapter(modelList);
-        adapter.registerType(
-                DEFAULT_TILE_TYPE, AppListCoordinator::buildTile, TileViewBinder::bind);
-        view.setAdapter(adapter);
-
-        mMediator = new AppListMediator(view.getContext(), this, modelList, items);
-        mMenuCoordinator = menuCoordinator;
-    }
-
-    void destroy() {
-        mMediator.destroy();
-        mMediator = null;
-    }
-
-    boolean showMenu(LaunchpadItem item) {
-        mMenuCoordinator.show(item);
-        return true;
-    }
-
-    private static TileView buildTile(ViewGroup parent) {
-        TileView tile =
-                (TileView) LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.launchpad_tile_view, parent, false /* attachToRoot */);
-        tile.setClickable(true);
-        return tile;
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppListMediator.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppListMediator.java
deleted file mode 100644
index 4ef7f43..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppListMediator.java
+++ /dev/null
@@ -1,68 +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.webapps.launchpad;
-
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.BitmapDrawable;
-
-import org.chromium.components.browser_ui.widget.tile.TileViewProperties;
-import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
-import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
-import org.chromium.ui.modelutil.PropertyModel;
-import org.chromium.ui.widget.Toast;
-import org.chromium.webapk.lib.client.WebApkNavigationClient;
-
-import java.util.List;
-
-/**
- * Mediator class for launchpad app list. Handles updating the list model for current
- * app list.
- */
-class AppListMediator {
-    private final Context mContext;
-    private final AppListCoordinator mCoordinator;
-    private final ModelList mListModel;
-    private final List<LaunchpadItem> mLaunchpadItems;
-
-    AppListMediator(Context context, AppListCoordinator coordinator, ModelList listModel,
-            List<LaunchpadItem> items) {
-        mContext = context;
-        mCoordinator = coordinator;
-        mListModel = listModel;
-        mLaunchpadItems = items;
-
-        populateData();
-    }
-
-    void destroy() {
-        mLaunchpadItems.clear();
-    }
-
-    private void populateData() {
-        for (LaunchpadItem item : mLaunchpadItems) {
-            PropertyModel tileModel = new PropertyModel(TileViewProperties.ALL_KEYS);
-            tileModel.set(TileViewProperties.TITLE, item.shortName);
-            tileModel.set(TileViewProperties.TITLE_LINES, 1);
-            tileModel.set(TileViewProperties.CONTENT_DESCRIPTION, item.shortName);
-            tileModel.set(TileViewProperties.ICON, new BitmapDrawable(item.icon));
-            tileModel.set(TileViewProperties.ON_CLICK, v -> clickItem(item));
-            tileModel.set(TileViewProperties.ON_LONG_CLICK, v -> mCoordinator.showMenu(item));
-
-            mListModel.add(new ListItem(AppListCoordinator.DEFAULT_TILE_TYPE, tileModel));
-        }
-    }
-
-    private void clickItem(LaunchpadItem item) {
-        Intent launchIntent = WebApkNavigationClient.createLaunchWebApkIntent(
-                item.packageName, item.url, false /* forceNavigation */);
-        try {
-            mContext.startActivity(launchIntent);
-        } catch (ActivityNotFoundException e) {
-            Toast.makeText(mContext, R.string.open_webapk_failed, Toast.LENGTH_SHORT).show();
-        }
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinator.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinator.java
deleted file mode 100644
index cb542d9..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinator.java
+++ /dev/null
@@ -1,185 +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.webapps.launchpad;
-
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ListView;
-
-import org.chromium.base.Log;
-import org.chromium.base.PackageUtils;
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.browserservices.intents.WebApkExtras.ShortcutItem;
-import org.chromium.components.browser_ui.settings.SettingsLauncher;
-import org.chromium.components.browser_ui.site_settings.SingleWebsiteSettings;
-import org.chromium.ui.modaldialog.DialogDismissalCause;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-import org.chromium.ui.modaldialog.ModalDialogProperties;
-import org.chromium.ui.modelutil.LayoutViewBuilder;
-import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
-import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
-import org.chromium.ui.modelutil.ModelListAdapter;
-import org.chromium.ui.modelutil.PropertyModel;
-import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
-import org.chromium.ui.widget.Toast;
-import org.chromium.webapk.lib.client.WebApkNavigationClient;
-
-/**
- * Coordinator for displaying the app management menu.
- */
-class AppManagementMenuCoordinator implements ModalDialogProperties.Controller {
-    private static final String TAG = "LaunchpadManageMenu";
-
-    private final Context mContext;
-    private final Supplier<ModalDialogManager> mModalDialogManagerSupplier;
-    private final SettingsLauncher mSettingsLauncher;
-
-    private PropertyModel mDialogModel;
-
-    private ListView mShortcutList;
-
-    public @interface ListItemType {
-        // The type for each shortcut of the WebAPK.
-        int SHORTCUT_ITEM = 0;
-        // The type for each menu item all apps have (Uninstall, Settings, etc).
-        int MENU_ITEM = 1;
-    }
-
-    private AppManagementMenuPermissionsCoordinator mPermissionsCoordinator;
-
-    AppManagementMenuCoordinator(Context context,
-            Supplier<ModalDialogManager> modalDialogManagerSupplier,
-            SettingsLauncher settingsLauncher) {
-        mContext = context;
-        mModalDialogManagerSupplier = modalDialogManagerSupplier;
-        mSettingsLauncher = settingsLauncher;
-    }
-
-    void destroy() {
-        mModalDialogManagerSupplier.get().dismissDialog(
-                mDialogModel, DialogDismissalCause.TAB_DESTROYED);
-    }
-
-    @Override
-    public void onClick(PropertyModel model, int buttonType) {}
-
-    @Override
-    public void onDismiss(PropertyModel model, int dismissalCause) {
-        mDialogModel = null;
-        if (mPermissionsCoordinator != null) {
-            mPermissionsCoordinator.destroy();
-            mPermissionsCoordinator = null;
-        }
-    }
-
-    void show(LaunchpadItem item) {
-        mDialogModel = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
-                               .with(ModalDialogProperties.CONTROLLER, this)
-                               .with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true)
-                               .with(ModalDialogProperties.CUSTOM_VIEW, createDialogView(item))
-                               .build();
-
-        mModalDialogManagerSupplier.get().showDialog(
-                mDialogModel, ModalDialogManager.ModalDialogType.APP);
-    }
-
-    private View createDialogView(LaunchpadItem item) {
-        View dialogView = LayoutInflater.from(mContext).inflate(
-                R.layout.launchpad_menu_dialog_layout, null, false);
-
-        View headerView = dialogView.findViewById(R.id.dialog_header);
-        PropertyModel headerModel = AppManagementMenuHeaderProperties.buildHeader(item);
-        PropertyModelChangeProcessor.create(
-                headerModel, headerView, new AppManagementMenuHeaderViewBinder());
-
-        mPermissionsCoordinator = new AppManagementMenuPermissionsCoordinator(mContext,
-                (AppManagementMenuPermissionsView) dialogView.findViewById(R.id.permissions), item);
-
-        mShortcutList = dialogView.findViewById(R.id.shortcuts_list_view);
-
-        ModelList listItems = buildShortcutsList(item);
-        listItems.add(buildMenuItem(R.string.launchpad_menu_uninstall,
-                (v) -> onUninstallMenuItemClicked(item.packageName)));
-        listItems.add(buildMenuItem(R.string.launchpad_menu_site_settings,
-                (v) -> onSiteSettingMenuItemClick(item.url)));
-
-        ModelListAdapter adapter = new ModelListAdapter(listItems);
-        mShortcutList.setAdapter(adapter);
-        adapter.registerType(ListItemType.SHORTCUT_ITEM,
-                new LayoutViewBuilder(R.layout.launchpad_shortcut_item_view),
-                ShortcutItemViewBinder::bind);
-        adapter.registerType(ListItemType.MENU_ITEM,
-                new LayoutViewBuilder(R.layout.launchpad_shortcut_item_view),
-                ShortcutItemViewBinder::bind);
-        return dialogView;
-    }
-
-    private ModelList buildShortcutsList(LaunchpadItem item) {
-        ModelList list = new ModelList();
-        if (item.shortcutItems == null) return list;
-
-        for (ShortcutItem shortcutItem : item.shortcutItems) {
-            PropertyModel model =
-                    new PropertyModel.Builder(ShortcutItemProperties.ALL_KEYS)
-                            .with(ShortcutItemProperties.NAME, shortcutItem.name)
-                            .with(ShortcutItemProperties.LAUNCH_URL, shortcutItem.launchUrl)
-                            .with(ShortcutItemProperties.SHORTCUT_ICON, shortcutItem.icon.bitmap())
-                            .with(ShortcutItemProperties.ON_CLICK,
-                                    v
-                                    -> onShortcutClicked(item.packageName, shortcutItem.launchUrl))
-                            .build();
-
-            list.add(new ListItem(ListItemType.SHORTCUT_ITEM, model));
-        }
-        return list;
-    }
-
-    private ListItem buildMenuItem(int name, View.OnClickListener onClick) {
-        PropertyModel model = new PropertyModel.Builder(ShortcutItemProperties.ALL_KEYS)
-                                      .with(ShortcutItemProperties.NAME, mContext.getString(name))
-                                      .with(ShortcutItemProperties.HIDE_ICON, true)
-                                      .with(ShortcutItemProperties.ON_CLICK, onClick)
-                                      .build();
-        return new ListItem(ListItemType.MENU_ITEM, model);
-    }
-
-    private void onShortcutClicked(String packageName, String launchUrl) {
-        Intent launchIntent =
-                WebApkNavigationClient.createLaunchWebApkIntent(packageName, launchUrl, false);
-        try {
-            mContext.startActivity(launchIntent);
-        } catch (ActivityNotFoundException e) {
-            Toast.makeText(mContext, R.string.open_webapk_failed, Toast.LENGTH_SHORT).show();
-        }
-
-        mModalDialogManagerSupplier.get().dismissDialog(
-                mDialogModel, DialogDismissalCause.ACTION_ON_CONTENT);
-    }
-
-    private void onUninstallMenuItemClicked(String packageName) {
-        if (!PackageUtils.isPackageInstalled(packageName)) {
-            Log.e(TAG, "WebApk not found:" + packageName);
-            return;
-        }
-
-        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        intent.setData(Uri.parse("package:" + packageName));
-        mContext.startActivity(intent);
-        mModalDialogManagerSupplier.get().dismissDialog(
-                mDialogModel, DialogDismissalCause.ACTION_ON_CONTENT);
-    }
-
-    private void onSiteSettingMenuItemClick(String url) {
-        Bundle args = SingleWebsiteSettings.createFragmentArgsForSite(url);
-        mSettingsLauncher.launchSettingsActivity(mContext, SingleWebsiteSettings.class, args);
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinatorTest.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinatorTest.java
deleted file mode 100644
index 0b07ef5..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinatorTest.java
+++ /dev/null
@@ -1,229 +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.webapps.launchpad;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.app.Activity;
-import android.content.pm.PackageInfo;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-import org.robolectric.Robolectric;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowPackageManager;
-
-import org.chromium.base.supplier.ObservableSupplierImpl;
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.browserservices.intents.WebApkExtras.ShortcutItem;
-import org.chromium.chrome.browser.browserservices.intents.WebappIcon;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.components.browser_ui.settings.SettingsLauncher;
-import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
-import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridgeJni;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-import org.chromium.ui.modaldialog.ModalDialogProperties;
-import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
-import org.chromium.ui.modelutil.PropertyModel;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Tests for {@link AppManagementMenuCoordinator}.
- */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class AppManagementMenuCoordinatorTest {
-    public static final String APP_PACKAGE_NAME = "package.name";
-    public static final String APP_SHORT_NAME = "App";
-    public static final String APP_NAME = "App Name";
-    public static final String APP_URL = "https://example.com/";
-    public final Bitmap APP_ICON = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
-    public final LaunchpadItem MOCK_ITEM = new LaunchpadItem(APP_PACKAGE_NAME, APP_SHORT_NAME,
-            APP_NAME, APP_URL, APP_ICON, new ArrayList<ShortcutItem>());
-
-    public static final String SHORTCUT_NAME_1 = "Shortcut 1";
-    public static final String SHORTCUT_URL_1 = "https://example.com/11";
-    public static final String SHORTCUT_NAME_2 = "Shortcut 2";
-    public static final String SHORTCUT_URL_2 = "https://example.com/22";
-    public final Bitmap SHORTCUT_ICON = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
-    public final ShortcutItem SHORTCUT_ITEM_1 = new ShortcutItem(SHORTCUT_NAME_1, SHORTCUT_NAME_1,
-            SHORTCUT_URL_1, "iconUrl", "iconHash", new WebappIcon(SHORTCUT_ICON));
-    public final ShortcutItem SHORTCUT_ITEM_2 = new ShortcutItem(SHORTCUT_NAME_2, SHORTCUT_NAME_2,
-            SHORTCUT_URL_2, "iconUrl", "iconHash", new WebappIcon(SHORTCUT_ICON));
-
-    @Rule
-    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
-
-    @Rule
-    public JniMocker mocker = new JniMocker();
-
-    @Mock
-    private WebsitePreferenceBridge.Natives mWebsitePreferenceBridgeJniMock;
-
-    @Mock
-    private SettingsLauncher mSettingsLauncher;
-
-    private Activity mActivity;
-    private AppManagementMenuCoordinator mCoordinator;
-    private ModalDialogManager mModalDialogManager;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mocker.mock(WebsitePreferenceBridgeJni.TEST_HOOKS, mWebsitePreferenceBridgeJniMock);
-        Profile.setLastUsedProfileForTesting(Mockito.mock(Profile.class));
-
-        mModalDialogManager =
-                new ModalDialogManager(Mockito.mock(ModalDialogManager.Presenter.class), 0);
-        ObservableSupplierImpl<ModalDialogManager> modalDialogManagerSupplier =
-                new ObservableSupplierImpl<>();
-        modalDialogManagerSupplier.set(mModalDialogManager);
-
-        mActivity = Mockito.spy(Robolectric.buildActivity(Activity.class).setup().get());
-
-        mCoordinator = new AppManagementMenuCoordinator(
-                mActivity, modalDialogManagerSupplier, mSettingsLauncher);
-    }
-
-    @Test
-    public void testShowDialog() {
-        mCoordinator.show(MOCK_ITEM);
-
-        PropertyModel dialogModel = mModalDialogManager.getCurrentDialogForTest();
-        assertNotNull(dialogModel);
-
-        View dialogView = dialogModel.get(ModalDialogProperties.CUSTOM_VIEW);
-
-        // Assert header view.
-        assertEquals(
-                APP_NAME, ((TextView) dialogView.findViewById(R.id.menu_header_title)).getText());
-        assertEquals(APP_URL, ((TextView) dialogView.findViewById(R.id.menu_header_url)).getText());
-        ImageView icon = (ImageView) dialogView.findViewById(R.id.menu_header_image);
-        assertEquals(APP_ICON, ((BitmapDrawable) icon.getDrawable()).getBitmap());
-
-        // Assert permissions view.
-        assertNotNull(dialogView.findViewById(R.id.notifications_button));
-        assertNotNull(dialogView.findViewById(R.id.mic_button));
-        assertNotNull(dialogView.findViewById(R.id.camera_button));
-        assertNotNull(dialogView.findViewById(R.id.location_button));
-    }
-
-    @Test
-    public void testShowDialogWithShortcuts() {
-        List<ShortcutItem> shortcuts = new ArrayList<>();
-        shortcuts.add(SHORTCUT_ITEM_1);
-        shortcuts.add(SHORTCUT_ITEM_2);
-        LaunchpadItem item = new LaunchpadItem(
-                APP_PACKAGE_NAME, APP_SHORT_NAME, APP_NAME, APP_URL, APP_ICON, shortcuts);
-
-        ListView shortcutsView = openDialogAndGetShortcutsListView(item);
-        assertNotNull(shortcutsView);
-
-        assertEquals(4, shortcutsView.getAdapter().getCount());
-
-        // Assert the shortcuts list model are set correctly.
-        ListItem listItem = (ListItem) shortcutsView.getAdapter().getItem(0);
-        assertEquals(SHORTCUT_NAME_1, listItem.model.get(ShortcutItemProperties.NAME));
-        assertEquals(SHORTCUT_URL_1, listItem.model.get(ShortcutItemProperties.LAUNCH_URL));
-        assertEquals(SHORTCUT_ICON, listItem.model.get(ShortcutItemProperties.SHORTCUT_ICON));
-        assertNotNull(listItem.model.get(ShortcutItemProperties.ON_CLICK));
-
-        ListItem listItem2 = (ListItem) shortcutsView.getAdapter().getItem(1);
-        assertEquals(SHORTCUT_NAME_2, listItem2.model.get(ShortcutItemProperties.NAME));
-        assertEquals(SHORTCUT_URL_2, listItem2.model.get(ShortcutItemProperties.LAUNCH_URL));
-        assertEquals(SHORTCUT_ICON, listItem2.model.get(ShortcutItemProperties.SHORTCUT_ICON));
-        assertNotNull(listItem2.model.get(ShortcutItemProperties.ON_CLICK));
-    }
-
-    @Test
-    public void testUninstallMenuItem() {
-        ListView shortcutsView = openDialogAndGetShortcutsListView(MOCK_ITEM);
-
-        assertEquals(2, shortcutsView.getAdapter().getCount());
-        ListItem uninstallItem = (ListItem) shortcutsView.getAdapter().getItem(0);
-        assertEquals(AppManagementMenuCoordinator.ListItemType.MENU_ITEM, uninstallItem.type);
-        assertEquals(mActivity.getResources().getString(R.string.launchpad_menu_uninstall),
-                uninstallItem.model.get(ShortcutItemProperties.NAME));
-        assertNull(uninstallItem.model.get(ShortcutItemProperties.LAUNCH_URL));
-        assertNull(uninstallItem.model.get(ShortcutItemProperties.SHORTCUT_ICON));
-        assertTrue(uninstallItem.model.get(ShortcutItemProperties.HIDE_ICON));
-        assertNotNull(uninstallItem.model.get(ShortcutItemProperties.ON_CLICK));
-    }
-
-    @Test
-    public void testSiteSettingMenuItem() {
-        ListView shortcutsView = openDialogAndGetShortcutsListView(MOCK_ITEM);
-
-        assertEquals(2, shortcutsView.getAdapter().getCount());
-        ListItem settingItem = (ListItem) shortcutsView.getAdapter().getItem(1);
-        assertEquals(AppManagementMenuCoordinator.ListItemType.MENU_ITEM, settingItem.type);
-        assertEquals(mActivity.getResources().getString(R.string.launchpad_menu_site_settings),
-                settingItem.model.get(ShortcutItemProperties.NAME));
-        assertNull(settingItem.model.get(ShortcutItemProperties.LAUNCH_URL));
-        assertNull(settingItem.model.get(ShortcutItemProperties.SHORTCUT_ICON));
-        assertTrue(settingItem.model.get(ShortcutItemProperties.HIDE_ICON));
-        assertNotNull(settingItem.model.get(ShortcutItemProperties.ON_CLICK));
-    }
-
-    @Test
-    public void testClickUninstallMenuItem() {
-        ListView shortcutsView = openDialogAndGetShortcutsListView(MOCK_ITEM);
-        ListItem uninstallItem = (ListItem) shortcutsView.getAdapter().getItem(0);
-
-        ShadowPackageManager pm =
-                Shadows.shadowOf(RuntimeEnvironment.getApplication().getPackageManager());
-        PackageInfo packageInfo = new PackageInfo();
-        packageInfo.packageName = APP_PACKAGE_NAME;
-        pm.installPackage(packageInfo);
-
-        uninstallItem.model.get(ShortcutItemProperties.ON_CLICK)
-                .onClick(shortcutsView.getChildAt(0));
-
-        verify(mActivity).startActivity(notNull());
-    }
-
-    @Test
-    public void testClickUninstallMenuItem_appNotExist() {
-        ListView shortcutsView = openDialogAndGetShortcutsListView(MOCK_ITEM);
-        ListItem uninstallItem = (ListItem) shortcutsView.getAdapter().getItem(0);
-
-        uninstallItem.model.get(ShortcutItemProperties.ON_CLICK)
-                .onClick(shortcutsView.getChildAt(0));
-
-        verify(mActivity, never()).startActivity(notNull());
-    }
-
-    public ListView openDialogAndGetShortcutsListView(LaunchpadItem item) {
-        mCoordinator.show(item);
-        View dialogView = mModalDialogManager.getCurrentDialogForTest().get(
-                ModalDialogProperties.CUSTOM_VIEW);
-        return dialogView.findViewById(R.id.shortcuts_list_view);
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderProperties.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderProperties.java
deleted file mode 100644
index ba3c554..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderProperties.java
+++ /dev/null
@@ -1,32 +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.webapps.launchpad;
-
-import android.graphics.Bitmap;
-
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel;
-import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
-
-/** Contains all the properties for app management menu header. */
-class AppManagementMenuHeaderProperties {
-    private AppManagementMenuHeaderProperties() {}
-
-    public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
-    public static final WritableObjectPropertyKey<CharSequence> URL =
-            new WritableObjectPropertyKey<>();
-    public static final WritableObjectPropertyKey<Bitmap> ICON = new WritableObjectPropertyKey<>();
-
-    public static final PropertyKey[] ALL_KEYS = {TITLE, URL, ICON};
-
-    /** Create the {@link PropertyModel} for menu header. */
-    public static PropertyModel buildHeader(LaunchpadItem item) {
-        return new PropertyModel.Builder(ALL_KEYS)
-                .with(TITLE, item.name)
-                .with(URL, item.url)
-                .with(ICON, item.icon)
-                .build();
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderViewBinder.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderViewBinder.java
deleted file mode 100644
index a854f76..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderViewBinder.java
+++ /dev/null
@@ -1,36 +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.webapps.launchpad;
-
-import android.graphics.Bitmap;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel;
-import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
-
-/**
- *  A {@link ViewBinder} responsible for gluing {@link AppManagementMenuHeaderProperties} to the
- *  view.
- */
-class AppManagementMenuHeaderViewBinder implements ViewBinder<PropertyModel, View, PropertyKey> {
-    @Override
-    public void bind(PropertyModel model, View view, PropertyKey propertyKey) {
-        if (propertyKey == AppManagementMenuHeaderProperties.TITLE) {
-            TextView titleText = view.findViewById(R.id.menu_header_title);
-            titleText.setText(model.get(AppManagementMenuHeaderProperties.TITLE));
-        } else if (propertyKey == AppManagementMenuHeaderProperties.URL) {
-            TextView urlText = view.findViewById(R.id.menu_header_url);
-            urlText.setText(model.get(AppManagementMenuHeaderProperties.URL));
-        } else if (propertyKey == AppManagementMenuHeaderProperties.ICON) {
-            Bitmap bitmap = model.get(AppManagementMenuHeaderProperties.ICON);
-            if (bitmap != null) {
-                ImageView imageView = view.findViewById(R.id.menu_header_image);
-                imageView.setImageBitmap(bitmap);
-            }
-        }
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinator.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinator.java
deleted file mode 100644
index 1debef1..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinator.java
+++ /dev/null
@@ -1,44 +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.webapps.launchpad;
-
-import android.content.Context;
-
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.components.embedder_support.util.Origin;
-import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
-
-/**
- * Coordinator for displaying the selected app's site permission in the management menu.
- */
-class AppManagementMenuPermissionsCoordinator {
-    private final AppManagementMenuPermissionsView mView;
-    AppManagementMenuPermissionsMediator mMediator;
-
-    /**
-     * Creates a new AppManagementMenuPermissionsCoordinator.
-     * @param view The associated AppManagementMenuPermissionsView..
-     * @param item The LaunchpadItem that is displaying in the management menu.
-     */
-    AppManagementMenuPermissionsCoordinator(
-            Context context, AppManagementMenuPermissionsView view, LaunchpadItem item) {
-        mView = view;
-
-        mMediator = new AppManagementMenuPermissionsMediator(
-                context, item.packageName, Origin.create(item.url));
-        PropertyModelChangeProcessor.create(
-                mMediator.getModel(), mView, AppManagementMenuPermissionsViewBinder::bind);
-    }
-
-    @VisibleForTesting
-    AppManagementMenuPermissionsMediator getMediatorForTesting() {
-        return mMediator;
-    }
-
-    void destroy() {
-        mMediator = null;
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinatorTest.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinatorTest.java
deleted file mode 100644
index cd1e8c3..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinatorTest.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.webapps.launchpad;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.os.Build;
-import android.view.LayoutInflater;
-
-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.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
-import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridgeJni;
-import org.chromium.components.content_settings.ContentSettingValues;
-import org.chromium.components.content_settings.ContentSettingsType;
-import org.chromium.components.embedder_support.util.Origin;
-import org.chromium.content_public.browser.BrowserContextHandle;
-import org.chromium.ui.modelutil.PropertyModel;
-
-/**
- * Tests for {@link AppManagementMenuPermissionsCoordinator}.
- */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class AppManagementMenuPermissionsCoordinatorTest {
-    private static final String APP_PACKAGE_NAME = "package.name";
-    private static final String APP_SHORT_NAME = "App";
-    private static final String APP_NAME = "App Name";
-    private static final String APP_URL = "https://example.com/123";
-    private static final String ORIGIN = "https://example.com";
-
-    private static final LaunchpadItem MOCK_ITEM =
-            new LaunchpadItem(APP_PACKAGE_NAME, APP_SHORT_NAME, APP_NAME, APP_URL, null, null);
-
-
-    @Rule
-    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
-
-    @Rule
-    public JniMocker mocker = new JniMocker();
-
-    @Mock
-    WebsitePreferenceBridge.Natives mWebsitePreferenceBridgeJniMock;
-
-    private Activity mActivity;
-    private AppManagementMenuPermissionsCoordinator mCoordinator;
-    private AppManagementMenuPermissionsView mView;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mocker.mock(WebsitePreferenceBridgeJni.TEST_HOOKS, mWebsitePreferenceBridgeJniMock);
-
-        Profile.setLastUsedProfileForTesting(mock(Profile.class));
-
-        setPermission(ContentSettingsType.NOTIFICATIONS, ContentSettingValues.ASK);
-        setPermission(ContentSettingsType.MEDIASTREAM_MIC, ContentSettingValues.ASK);
-        setPermission(ContentSettingsType.MEDIASTREAM_CAMERA, ContentSettingValues.ASK);
-        setPermission(ContentSettingsType.GEOLOCATION, ContentSettingValues.ASK);
-
-        mActivity = spy(Robolectric.buildActivity(Activity.class).setup().get());
-        mView = (AppManagementMenuPermissionsView) LayoutInflater.from(mActivity).inflate(
-                R.layout.launchpad_app_menu_permissions, null);
-        mCoordinator = new AppManagementMenuPermissionsCoordinator(mActivity, mView, MOCK_ITEM);
-    }
-
-    private void setPermission(@ContentSettingsType int type, @ContentSettingValues int value) {
-        when(mWebsitePreferenceBridgeJniMock.getPermissionSettingForOrigin(
-                     any(BrowserContextHandle.class), eq(type), anyString(), anyString()))
-                .thenReturn(value);
-    }
-
-    @Test
-    public void testMediatorInitialization() {
-        setPermission(ContentSettingsType.NOTIFICATIONS, ContentSettingValues.BLOCK);
-        setPermission(ContentSettingsType.MEDIASTREAM_MIC, ContentSettingValues.ALLOW);
-        setPermission(ContentSettingsType.MEDIASTREAM_CAMERA, ContentSettingValues.ASK);
-        setPermission(ContentSettingsType.GEOLOCATION, ContentSettingValues.ASK);
-
-        mCoordinator = new AppManagementMenuPermissionsCoordinator(mActivity, mView, MOCK_ITEM);
-        AppManagementMenuPermissionsMediator mediator = mCoordinator.getMediatorForTesting();
-        PropertyModel model = mediator.getModel();
-        assertEquals(ContentSettingValues.BLOCK,
-                model.get(AppManagementMenuPermissionsProperties.NOTIFICATIONS));
-        assertEquals(
-                ContentSettingValues.ALLOW, model.get(AppManagementMenuPermissionsProperties.MIC));
-        assertEquals(
-                ContentSettingValues.ASK, model.get(AppManagementMenuPermissionsProperties.CAMERA));
-        assertEquals(ContentSettingValues.ASK,
-                model.get(AppManagementMenuPermissionsProperties.LOCATION));
-        assertNotNull(model.get(AppManagementMenuPermissionsProperties.ON_CLICK));
-    }
-
-    @Test
-    public void testClickListener() {
-        AppManagementMenuPermissionsMediator mediator = mCoordinator.getMediatorForTesting();
-        AppManagementMenuPermissionsView.OnButtonClickListener mockPermissionButtonListener =
-                mock(AppManagementMenuPermissionsView.OnButtonClickListener.class);
-        mediator.getModel().set(
-                AppManagementMenuPermissionsProperties.ON_CLICK, mockPermissionButtonListener);
-
-        mView.findViewById(R.id.location_button).callOnClick();
-        verify(mockPermissionButtonListener, times(1))
-                .onButtonClick(AppManagementMenuPermissionsProperties.LOCATION);
-
-        mView.findViewById(R.id.camera_button).callOnClick();
-        verify(mockPermissionButtonListener, times(1))
-                .onButtonClick(AppManagementMenuPermissionsProperties.CAMERA);
-    }
-
-    @Test
-    public void testSetPermissions() {
-        setPermission(ContentSettingsType.MEDIASTREAM_MIC, ContentSettingValues.ALLOW);
-        setPermission(ContentSettingsType.MEDIASTREAM_CAMERA, ContentSettingValues.BLOCK);
-
-        mCoordinator = new AppManagementMenuPermissionsCoordinator(mActivity, mView, MOCK_ITEM);
-        AppManagementMenuPermissionsMediator mediator = mCoordinator.getMediatorForTesting();
-        AppManagementMenuPermissionsView.OnButtonClickListener listener =
-                mediator.getModel().get(AppManagementMenuPermissionsProperties.ON_CLICK);
-
-        listener.onButtonClick(AppManagementMenuPermissionsProperties.MIC);
-        verify(mWebsitePreferenceBridgeJniMock, times(1))
-                .setPermissionSettingForOrigin(any(), eq(ContentSettingsType.MEDIASTREAM_MIC),
-                        eq(ORIGIN), eq(ORIGIN), eq(ContentSettingValues.BLOCK));
-
-        listener.onButtonClick(AppManagementMenuPermissionsProperties.CAMERA);
-        verify(mWebsitePreferenceBridgeJniMock, times(1))
-                .setPermissionSettingForOrigin(any(), eq(ContentSettingsType.MEDIASTREAM_CAMERA),
-                        eq(ORIGIN), eq(ORIGIN), eq(ContentSettingValues.ALLOW));
-    }
-
-    // Test the click listener on no-clickable icon will not set any permission, and will trigger
-    // the assertion error.
-    @Test(expected = AssertionError.class)
-    public void testSetPermissions_noPreviousPermission() {
-        setPermission(ContentSettingsType.GEOLOCATION, ContentSettingValues.ASK);
-
-        mCoordinator = new AppManagementMenuPermissionsCoordinator(mActivity, mView, MOCK_ITEM);
-        AppManagementMenuPermissionsMediator mediator = mCoordinator.getMediatorForTesting();
-        AppManagementMenuPermissionsView.OnButtonClickListener listener =
-                mediator.getModel().get(AppManagementMenuPermissionsProperties.ON_CLICK);
-
-        listener.onButtonClick(AppManagementMenuPermissionsProperties.LOCATION);
-        verify(mWebsitePreferenceBridgeJniMock, never())
-                .setPermissionSettingForOrigin(
-                        any(), eq(ContentSettingsType.GEOLOCATION), eq(ORIGIN), eq(ORIGIN), any());
-    }
-
-    // Test click icon to set notification permission on pre-O devices will set notifications
-    // permission directly.
-    @Test
-    @Config(sdk = Build.VERSION_CODES.N_MR1)
-    public void testSetPermissions_notificationsPreO() {
-        setPermission(ContentSettingsType.NOTIFICATIONS, ContentSettingValues.ALLOW);
-
-        mCoordinator = new AppManagementMenuPermissionsCoordinator(mActivity, mView, MOCK_ITEM);
-        AppManagementMenuPermissionsMediator mediator = mCoordinator.getMediatorForTesting();
-        AppManagementMenuPermissionsView.OnButtonClickListener listener =
-                mediator.getModel().get(AppManagementMenuPermissionsProperties.ON_CLICK);
-
-        listener.onButtonClick(AppManagementMenuPermissionsProperties.NOTIFICATIONS);
-        verify(mWebsitePreferenceBridgeJniMock, times(1))
-                .setPermissionSettingForOrigin(any(), eq(ContentSettingsType.NOTIFICATIONS),
-                        eq(ORIGIN), eq(ORIGIN), eq(ContentSettingValues.BLOCK));
-        verify(mActivity, never()).startActivity(any());
-    }
-
-    // Test click icon to set notification permission on O+ devices will open the notification
-    // channel setting.
-    @Test
-    @Config(sdk = Build.VERSION_CODES.O)
-    public void testSetPermissions_notificationsChannel() {
-        setPermission(ContentSettingsType.NOTIFICATIONS, ContentSettingValues.ALLOW);
-
-        mCoordinator = new AppManagementMenuPermissionsCoordinator(mActivity, mView, MOCK_ITEM);
-        AppManagementMenuPermissionsMediator mediator = mCoordinator.getMediatorForTesting();
-        AppManagementMenuPermissionsView.OnButtonClickListener listener =
-                mediator.getModel().get(AppManagementMenuPermissionsProperties.ON_CLICK);
-
-        listener.onButtonClick(AppManagementMenuPermissionsProperties.NOTIFICATIONS);
-        verify(mWebsitePreferenceBridgeJniMock, never())
-                .setPermissionSettingForOrigin(any(), eq(ContentSettingsType.NOTIFICATIONS),
-                        eq(ORIGIN), eq(ORIGIN), eq(ContentSettingValues.BLOCK));
-        verify(mActivity, times(1)).startActivity(any());
-    }
-
-    // Test open management menu for WebAPK with invalid start URL will NOT fetch permissions. The
-    // permission icons are not clickable.
-    @Test
-    public void testInvalidUrl() {
-        String invalidUrl = "notUrl";
-        assertNull(Origin.create(invalidUrl));
-        LaunchpadItem item = new LaunchpadItem(
-                APP_PACKAGE_NAME, APP_SHORT_NAME, APP_NAME, invalidUrl, null, null);
-        mCoordinator = new AppManagementMenuPermissionsCoordinator(mActivity, mView, item);
-        AppManagementMenuPermissionsMediator mediator = mCoordinator.getMediatorForTesting();
-
-        assertEquals(
-                0, mediator.getModel().get(AppManagementMenuPermissionsProperties.NOTIFICATIONS));
-        assertEquals(0, mediator.getModel().get(AppManagementMenuPermissionsProperties.MIC));
-        assertEquals(0, mediator.getModel().get(AppManagementMenuPermissionsProperties.CAMERA));
-        assertEquals(0, mediator.getModel().get(AppManagementMenuPermissionsProperties.LOCATION));
-        assertNull(mediator.getModel().get(AppManagementMenuPermissionsProperties.ON_CLICK));
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsMediator.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsMediator.java
deleted file mode 100644
index 0e6b0ac..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsMediator.java
+++ /dev/null
@@ -1,132 +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.webapps.launchpad;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.provider.Settings;
-
-import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.components.browser_ui.site_settings.PermissionInfo;
-import org.chromium.components.content_settings.ContentSettingValues;
-import org.chromium.components.content_settings.ContentSettingsType;
-import org.chromium.components.embedder_support.util.Origin;
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel;
-
-/**
- * Mediator class for fetching and updating app's permissions in Launchpad management menu.
- */
-class AppManagementMenuPermissionsMediator {
-    private final Context mContext;
-    private final String mPackageName;
-    private final Origin mOrigin;
-    private final Profile mProfile;
-    private PropertyModel mModel;
-
-    /**
-     * Creates a new AppManagementMenuPermissionsMediator.
-     * @param context The associated Context.
-     * @param packageName The package name of the WebAPK that is showing in the management menu.
-     * @param origin The origin of the WebAPK that is showing in the management menu. Use for
-     * getting and setting site permissionss and open notification channel setting.
-     */
-    AppManagementMenuPermissionsMediator(Context context, String packageName, Origin origin) {
-        mContext = context;
-        mPackageName = packageName;
-        mOrigin = origin;
-        mProfile = Profile.getLastUsedRegularProfile();
-        if (mOrigin == null) {
-            // If the WebAPK does not have valid origin, set the PropertyModel to an empty one so no
-            // permission will be updated.
-            mModel = new PropertyModel.Builder(AppManagementMenuPermissionsProperties.ALL_KEYS)
-                             .build();
-        } else {
-            mModel = buildModel();
-        }
-    }
-
-    PropertyModel getModel() {
-        return mModel;
-    }
-
-    private PropertyModel buildModel() {
-        return new PropertyModel.Builder(AppManagementMenuPermissionsProperties.ALL_KEYS)
-                .with(AppManagementMenuPermissionsProperties.NOTIFICATIONS,
-                        getContentSetting(toContentSettingsType(
-                                AppManagementMenuPermissionsProperties.NOTIFICATIONS)))
-                .with(AppManagementMenuPermissionsProperties.MIC,
-                        getContentSetting(
-                                toContentSettingsType(AppManagementMenuPermissionsProperties.MIC)))
-                .with(AppManagementMenuPermissionsProperties.CAMERA,
-                        getContentSetting(toContentSettingsType(
-                                AppManagementMenuPermissionsProperties.CAMERA)))
-                .with(AppManagementMenuPermissionsProperties.LOCATION,
-                        getContentSetting(toContentSettingsType(
-                                AppManagementMenuPermissionsProperties.LOCATION)))
-                .with(AppManagementMenuPermissionsProperties.ON_CLICK,
-                        (propertyKey) -> onButtonClick(propertyKey))
-                .build();
-    }
-
-    private void onButtonClick(PropertyModel.WritableIntPropertyKey key) {
-        @ContentSettingsType
-        int type = toContentSettingsType(key);
-        @ContentSettingValues
-        int value = getContentSetting(type);
-        if (value == ContentSettingValues.ASK || value == ContentSettingValues.DEFAULT) {
-            assert false : "button should not be clickable";
-            return;
-        }
-
-        boolean shouldEnable = (value == ContentSettingValues.BLOCK);
-        @ContentSettingValues
-        int newPermission = shouldEnable ? ContentSettingValues.ALLOW : ContentSettingValues.BLOCK;
-
-        // Notifications permission on O+ devices are managed by notification channel.
-        if (type == ContentSettingsType.NOTIFICATIONS
-                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            launchNotificationChannelSettings();
-            return;
-        }
-
-        Profile profile = Profile.getLastUsedRegularProfile();
-        PermissionInfo permissionInfo =
-                new PermissionInfo(type, mOrigin.toString(), null /* embedder */);
-        permissionInfo.setContentSetting(profile, newPermission);
-
-        mModel.set(key, newPermission);
-    }
-
-    private void launchNotificationChannelSettings() {
-        String channelId =
-                SiteChannelsManager.getInstance().getChannelIdForOrigin(mOrigin.toString());
-        Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
-        intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
-        intent.putExtra(Settings.EXTRA_APP_PACKAGE, mContext.getPackageName());
-        mContext.startActivity(intent);
-    }
-
-    private @ContentSettingValues int getContentSetting(@ContentSettingsType int type) {
-        PermissionInfo permissionInfo = new PermissionInfo(type, mOrigin.toString(), null, false);
-        return permissionInfo.getContentSetting(mProfile);
-    }
-
-    private @ContentSettingsType int toContentSettingsType(PropertyKey key) {
-        if (key == AppManagementMenuPermissionsProperties.NOTIFICATIONS) {
-            return ContentSettingsType.NOTIFICATIONS;
-        } else if (key == AppManagementMenuPermissionsProperties.MIC) {
-            return ContentSettingsType.MEDIASTREAM_MIC;
-        } else if (key == AppManagementMenuPermissionsProperties.CAMERA) {
-            return ContentSettingsType.MEDIASTREAM_CAMERA;
-        } else if (key == AppManagementMenuPermissionsProperties.LOCATION) {
-            return ContentSettingsType.GEOLOCATION;
-        }
-        assert false;
-        return ContentSettingsType.DEFAULT;
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsProperties.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsProperties.java
deleted file mode 100644
index 337f518d..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsProperties.java
+++ /dev/null
@@ -1,27 +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.webapps.launchpad;
-
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
-import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
-
-/**
- * Properties for Launchpad app management menu's app permissions.
- */
-class AppManagementMenuPermissionsProperties {
-    private AppManagementMenuPermissionsProperties() {}
-
-    public static final WritableIntPropertyKey NOTIFICATIONS = new WritableIntPropertyKey();
-    public static final WritableIntPropertyKey MIC = new WritableIntPropertyKey();
-    public static final WritableIntPropertyKey CAMERA = new WritableIntPropertyKey();
-    public static final WritableIntPropertyKey LOCATION = new WritableIntPropertyKey();
-
-    public static final WritableObjectPropertyKey<
-            AppManagementMenuPermissionsView.OnButtonClickListener> ON_CLICK =
-            new WritableObjectPropertyKey<>();
-
-    public static final PropertyKey[] ALL_KEYS = {NOTIFICATIONS, MIC, CAMERA, LOCATION, ON_CLICK};
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsView.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsView.java
deleted file mode 100644
index d901cee..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsView.java
+++ /dev/null
@@ -1,57 +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.webapps.launchpad;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
-
-/**
- * View for app management menu permission buttons.
- */
-public class AppManagementMenuPermissionsView extends LinearLayout {
-    private ImageView mNotificationsIcon;
-    private ImageView mMicIcon;
-    private ImageView mCameraIcon;
-    private ImageView mLocationIcon;
-
-    /**
-     * Interface for receiving click events on permission buttons.
-     */
-    public interface OnButtonClickListener {
-        void onButtonClick(WritableIntPropertyKey propertyKey);
-    }
-
-    /**
-     * Constructor for inflating from XML.
-     */
-    public AppManagementMenuPermissionsView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mNotificationsIcon = findViewById(R.id.notifications_button);
-        mMicIcon = findViewById(R.id.mic_button);
-        mCameraIcon = findViewById(R.id.camera_button);
-        mLocationIcon = findViewById(R.id.location_button);
-    }
-
-    void setOnImageButtonClick(OnButtonClickListener listener) {
-        mNotificationsIcon.setOnClickListener(
-                v -> listener.onButtonClick(AppManagementMenuPermissionsProperties.NOTIFICATIONS));
-        mMicIcon.setOnClickListener(
-                v -> listener.onButtonClick(AppManagementMenuPermissionsProperties.MIC));
-        mCameraIcon.setOnClickListener(
-                v -> listener.onButtonClick(AppManagementMenuPermissionsProperties.CAMERA));
-        mLocationIcon.setOnClickListener(
-                v -> listener.onButtonClick(AppManagementMenuPermissionsProperties.LOCATION));
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsViewBinder.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsViewBinder.java
deleted file mode 100644
index 48ac3fe..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsViewBinder.java
+++ /dev/null
@@ -1,80 +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.webapps.launchpad;
-
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.appcompat.content.res.AppCompatResources;
-
-import org.chromium.components.content_settings.ContentSettingValues;
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel;
-
-/**
- * Class responsible for binding the model and the AppManagementMenuPermissionsView..
- */
-class AppManagementMenuPermissionsViewBinder {
-    static void bind(
-            PropertyModel model, AppManagementMenuPermissionsView view, PropertyKey propertyKey) {
-        if (propertyKey == AppManagementMenuPermissionsProperties.NOTIFICATIONS) {
-            @ContentSettingValues
-            int setting = model.get(AppManagementMenuPermissionsProperties.NOTIFICATIONS);
-            updatePermissionIcon(view, R.id.notifications_button, setting,
-                    R.drawable.gm_filled_notifications_24,
-                    R.drawable.gm_filled_notifications_off_24);
-        } else if (propertyKey == AppManagementMenuPermissionsProperties.MIC) {
-            @ContentSettingValues
-            int setting = model.get(AppManagementMenuPermissionsProperties.MIC);
-            updatePermissionIcon(view, R.id.mic_button, setting, R.drawable.gm_filled_mic_24,
-                    R.drawable.gm_filled_mic_off_24);
-        } else if (propertyKey == AppManagementMenuPermissionsProperties.CAMERA) {
-            @ContentSettingValues
-            int setting = model.get(AppManagementMenuPermissionsProperties.CAMERA);
-            updatePermissionIcon(view, R.id.camera_button, setting,
-                    R.drawable.gm_filled_videocam_24, R.drawable.gm_filled_videocam_off_24);
-        } else if (propertyKey == AppManagementMenuPermissionsProperties.LOCATION) {
-            @ContentSettingValues
-            int setting = model.get(AppManagementMenuPermissionsProperties.LOCATION);
-            updatePermissionIcon(view, R.id.location_button, setting,
-                    R.drawable.gm_filled_location_on_24, R.drawable.gm_filled_location_off_24);
-        } else if (propertyKey == AppManagementMenuPermissionsProperties.ON_CLICK) {
-            view.setOnImageButtonClick(model.get(AppManagementMenuPermissionsProperties.ON_CLICK));
-        }
-    }
-
-    private static void updatePermissionIcon(
-            View view, int iconId, int setting, int enabledIcon, int disabledIcon) {
-        ImageView icon = (ImageView) view.findViewById(iconId);
-        int resId;
-        int tintId;
-
-        switch (setting) {
-            case ContentSettingValues.ALLOW:
-                // Permission is ALLOW, showing the enabled icon with the default tint.
-                resId = enabledIcon;
-                tintId = R.color.default_icon_color_tint_list;
-                break;
-            case ContentSettingValues.BLOCK:
-                // Permission is BLOCK, showing the disabled icon (crossed-out icon), with the
-                // default tint.
-                resId = disabledIcon;
-                tintId = R.color.default_icon_color_tint_list;
-                break;
-            case ContentSettingValues.ASK:
-            case ContentSettingValues.DEFAULT:
-                // When value is ASK or DEFAULT, the permission is not set (never requested),
-                // showing the disabled icon and grey-out tint color.
-                resId = disabledIcon;
-                tintId = R.color.default_icon_color_disabled;
-                icon.setEnabled(false);
-                break;
-            default:
-                assert false : "Unexpected ContentSettingValue: " + setting;
-                return;
-        }
-        icon.setImageResource(resId);
-        icon.setImageTintList(AppCompatResources.getColorStateList(view.getContext(), tintId));
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinator.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinator.java
deleted file mode 100644
index 91c846c..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinator.java
+++ /dev/null
@@ -1,97 +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.webapps.launchpad;
-
-import android.app.Activity;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.appcompat.widget.Toolbar;
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.chromium.base.supplier.Supplier;
-import org.chromium.components.browser_ui.settings.SettingsLauncher;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-
-import java.util.List;
-
-/**
- * Displays and manages the UI for browsing installed web apps. This is the top level coordinator
- * for the launchpad ui.
- */
-class LaunchpadCoordinator {
-    private final Activity mActivity;
-    /** Main view for the Launchpad UI. */
-    private final ViewGroup mMainView;
-    /** The coordinator for displaying the app list in Launchpad. */
-    private AppListCoordinator mAppListCoordinator;
-    /** The coordinator for displaying the app management dialog. */
-    private AppManagementMenuCoordinator mAppManagementMenuCoordinator;
-
-    /**
-     * Creates a new LaunchpadCoordinator.
-     * @param activity The activity associated with the LaunchpadCoordinator.
-     * @param modalDialogManagerSupplier Supplies the {@link ModalDialogManager}.
-     * @param items The list of LaunchpadItems to be displayed.
-     * @param isSeparateActivity Whether the launchpad UI will be shown in a separate activity than
-     *                           the main Chrome activity.
-     */
-    LaunchpadCoordinator(Activity activity, Supplier<ModalDialogManager> modalDialogManagerSupplier,
-            SettingsLauncher settingsLauncher, List<LaunchpadItem> items,
-            boolean isSeparateActivity) {
-        mActivity = activity;
-        mMainView = (ViewGroup) activity.getLayoutInflater().inflate(
-                R.layout.launchpad_page_layout, null);
-
-        RecyclerView appListRecyclerView = getView().findViewById(R.id.launchpad_recycler);
-
-        mAppManagementMenuCoordinator = new AppManagementMenuCoordinator(
-                activity, modalDialogManagerSupplier, settingsLauncher);
-        mAppListCoordinator =
-                new AppListCoordinator(appListRecyclerView, mAppManagementMenuCoordinator, items);
-
-        initializeActionBar(isSeparateActivity);
-
-        View shadow = mMainView.findViewById(R.id.shadow);
-        appListRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
-            @Override
-            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-                shadow.setVisibility(
-                        appListRecyclerView.canScrollVertically(-1) ? View.VISIBLE : View.GONE);
-            }
-        });
-    }
-
-    /**
-     * @return The view that shows the main launchpad UI.
-     */
-    ViewGroup getView() {
-        return mMainView;
-    }
-
-    void destroy() {
-        mAppListCoordinator.destroy();
-        mAppListCoordinator = null;
-        mAppManagementMenuCoordinator.destroy();
-        mAppManagementMenuCoordinator = null;
-    }
-
-    private void initializeActionBar(boolean isSeparateActivity) {
-        Toolbar toolbar = (Toolbar) mMainView.findViewById(R.id.toolbar);
-        toolbar.setTitle(R.string.launchpad_title);
-        toolbar.inflateMenu(R.menu.launchpad_action_bar_menu);
-        if (!isSeparateActivity) toolbar.getMenu().removeItem(R.id.close_menu_id);
-        toolbar.setOnMenuItemClickListener(this::onMenuItemClick);
-    }
-
-    private boolean onMenuItemClick(MenuItem menuItem) {
-        if (menuItem.getItemId() == R.id.close_menu_id) {
-            mActivity.finish();
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinatorTest.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinatorTest.java
deleted file mode 100644
index 97582b29..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinatorTest.java
+++ /dev/null
@@ -1,92 +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.webapps.launchpad;
-
-import static org.mockito.Mockito.mock;
-
-import android.app.Activity;
-
-import androidx.appcompat.widget.Toolbar;
-
-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.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-import org.robolectric.Robolectric;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.supplier.ObservableSupplierImpl;
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.components.browser_ui.settings.SettingsLauncher;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Tests for {@link LaunchpadCoordinator}.
- *
- */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class LaunchpadCoordinatorTest {
-    private static final String APP_PACKAGE_NAME_1 = "package.name.1";
-    private static final String APP_NAME_1 = "App Name 1";
-    private static final String APP_SHORT_NAME_1 = "App 1";
-    private static final String APP_URL_1 = "https://example.com/1";
-    private static final String APP_PACKAGE_NAME_2 = "package.name.2";
-    private static final String APP_NAME_2 = "App Name 2";
-    private static final String APP_SHORT_NAME_2 = "App 2 with long short name";
-    private static final String APP_URL_2 = "https://example.com/2";
-
-    private static final List<LaunchpadItem> MOCK_APP_LIST =
-            new ArrayList<>(Arrays.asList(new LaunchpadItem(APP_PACKAGE_NAME_1, APP_SHORT_NAME_1,
-                                                  APP_NAME_1, APP_URL_1, null, null),
-                    new LaunchpadItem(APP_PACKAGE_NAME_2, APP_SHORT_NAME_2, APP_NAME_2, APP_URL_2,
-                            null, null)));
-
-    private Activity mActivity;
-
-    @Mock
-    private ModalDialogManager mModalDialogManager;
-
-    @Rule
-    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
-
-    @Before
-    public void setUp() {
-        mActivity = Robolectric.buildActivity(Activity.class).setup().get();
-        mActivity.setTheme(org.chromium.chrome.R.style.Theme_MaterialComponents);
-    }
-
-    private LaunchpadCoordinator createCoordinator(boolean isSeparateActivity) {
-        ObservableSupplierImpl<ModalDialogManager> modalDialogManagerSupplier =
-                new ObservableSupplierImpl<>();
-        modalDialogManagerSupplier.set(mModalDialogManager);
-
-        return new LaunchpadCoordinator(mActivity, modalDialogManagerSupplier,
-                mock(SettingsLauncher.class), MOCK_APP_LIST, isSeparateActivity);
-    }
-
-    @Test
-    public void testLaunchpadToolbar() {
-        LaunchpadCoordinator coordinator = createCoordinator(true /* isSeparateActivity */);
-        Toolbar toolbar = (Toolbar) coordinator.getView().findViewById(R.id.toolbar);
-        Assert.assertEquals(
-                ContextUtils.getApplicationContext().getString(R.string.launchpad_title),
-                toolbar.getTitle());
-        Assert.assertNotNull(toolbar.getMenu().findItem(R.id.close_menu_id));
-
-        coordinator = createCoordinator(false /* isSeparateActivity */);
-        toolbar = (Toolbar) coordinator.getView().findViewById(R.id.toolbar);
-        Assert.assertNull(toolbar.getMenu().findItem(R.id.close_menu_id));
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadItem.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadItem.java
deleted file mode 100644
index 4793a2ad..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadItem.java
+++ /dev/null
@@ -1,33 +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.webapps.launchpad;
-
-import android.graphics.Bitmap;
-
-import org.chromium.chrome.browser.browserservices.intents.WebApkExtras.ShortcutItem;
-
-import java.util.List;
-
-/**
- * A plain old data class that holds webapk info for each launchpad item.
- */
-class LaunchpadItem {
-    public final String packageName;
-    public final String shortName;
-    public final String name;
-    public final String url;
-    public final Bitmap icon;
-    public final List<ShortcutItem> shortcutItems;
-
-    LaunchpadItem(String packageName, String shortName, String name, String url, Bitmap icon,
-            List<ShortcutItem> shortcutItems) {
-        this.packageName = packageName;
-        this.shortName = shortName;
-        this.name = name;
-        this.url = url;
-        this.icon = icon;
-        this.shortcutItems = shortcutItems;
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPage.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPage.java
deleted file mode 100644
index 4811a37..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPage.java
+++ /dev/null
@@ -1,66 +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.webapps.launchpad;
-
-import android.app.Activity;
-
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.ui.native_page.BasicNativePage;
-import org.chromium.chrome.browser.ui.native_page.NativePageHost;
-import org.chromium.components.browser_ui.settings.SettingsLauncher;
-import org.chromium.components.embedder_support.util.UrlConstants;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-
-import java.util.List;
-
-/**
- * Native page for launching WebApks.
- */
-public class LaunchpadPage extends BasicNativePage {
-    private LaunchpadCoordinator mLaunchpadCoordinator;
-    private String mTitle;
-
-    /**
-     * Create a new instance of the app launcher page.
-     * @param activity The activity to get context and launch apps.
-     * @param host A NativePageHost to load URLs.
-     * @param items The list of LaunchpadItems to be displayed.
-     */
-    public LaunchpadPage(Activity activity, NativePageHost host,
-            Supplier<ModalDialogManager> modalDialogManagerSupplier,
-            SettingsLauncher settingsLauncher, List<LaunchpadItem> items) {
-        super(host);
-
-        mTitle = host.getContext().getResources().getString(R.string.launchpad_title);
-        mLaunchpadCoordinator = new LaunchpadCoordinator(activity, modalDialogManagerSupplier,
-                settingsLauncher, items, false /* isSeparateActivity */);
-
-        initWithView(mLaunchpadCoordinator.getView());
-    }
-
-    @Override
-    public String getTitle() {
-        return mTitle;
-    }
-
-    @Override
-    public String getHost() {
-        return UrlConstants.LAUNCHPAD_HOST;
-    }
-
-    @Override
-    public void destroy() {
-        mLaunchpadCoordinator.destroy();
-        mLaunchpadCoordinator = null;
-        super.destroy();
-    }
-
-    @VisibleForTesting
-    LaunchpadCoordinator getCoordinatorForTesting() {
-        return mLaunchpadCoordinator;
-    }
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/ShortcutItemProperties.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/ShortcutItemProperties.java
deleted file mode 100644
index 92a0b5bf..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/ShortcutItemProperties.java
+++ /dev/null
@@ -1,28 +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.webapps.launchpad;
-
-import android.graphics.Bitmap;
-import android.view.View;
-
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
-
-class ShortcutItemProperties {
-    private ShortcutItemProperties() {}
-
-    public static final WritableObjectPropertyKey<String> NAME = new WritableObjectPropertyKey<>();
-    public static final WritableObjectPropertyKey<CharSequence> LAUNCH_URL =
-            new WritableObjectPropertyKey<>();
-    public static final WritableObjectPropertyKey<Bitmap> SHORTCUT_ICON =
-            new WritableObjectPropertyKey<>();
-    public static final WritableBooleanPropertyKey HIDE_ICON = new WritableBooleanPropertyKey();
-    public static final WritableObjectPropertyKey<View.OnClickListener> ON_CLICK =
-            new WritableObjectPropertyKey<>();
-
-    public static final PropertyKey[] ALL_KEYS = {
-            NAME, LAUNCH_URL, SHORTCUT_ICON, HIDE_ICON, ON_CLICK};
-}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/ShortcutItemViewBinder.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/ShortcutItemViewBinder.java
deleted file mode 100644
index 2d883be..0000000
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/ShortcutItemViewBinder.java
+++ /dev/null
@@ -1,33 +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.webapps.launchpad;
-
-import android.graphics.Bitmap;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.chromium.ui.modelutil.PropertyKey;
-import org.chromium.ui.modelutil.PropertyModel;
-
-/**
- * Class responsible for binding the model and the ShortcutItemView.
- */
-class ShortcutItemViewBinder {
-    static void bind(PropertyModel model, View view, PropertyKey propertyKey) {
-        if (propertyKey == ShortcutItemProperties.NAME) {
-            TextView titleText = view.findViewById(R.id.shortcut_name);
-            titleText.setText(model.get(ShortcutItemProperties.NAME));
-        } else if (propertyKey == ShortcutItemProperties.SHORTCUT_ICON) {
-            Bitmap bitmap = model.get(ShortcutItemProperties.SHORTCUT_ICON);
-            ImageView imageView = view.findViewById(R.id.shortcut_icon);
-            imageView.setImageBitmap(bitmap);
-            imageView.setVisibility(View.VISIBLE);
-        } else if (propertyKey == ShortcutItemProperties.ON_CLICK) {
-            view.setOnClickListener(model.get(ShortcutItemProperties.ON_CLICK));
-        } else if (propertyKey == ShortcutItemProperties.HIDE_ICON) {
-            view.findViewById(R.id.shortcut_icon).setVisibility(View.GONE);
-        }
-    }
-}
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 2c3c73e2..3c03a79 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -2389,6 +2389,8 @@
     "policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_bus_sampler_handler.h",
     "policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_cpu_sampler_handler.cc",
     "policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_cpu_sampler_handler.h",
+    "policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.cc",
+    "policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.h",
     "policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_sampler_handler.h",
     "policy/reporting/metrics_reporting/cros_reporting_settings.cc",
     "policy/reporting/metrics_reporting/cros_reporting_settings.h",
@@ -3061,8 +3063,6 @@
     "web_applications/shimless_rma_system_web_app_info.h",
     "web_applications/shortcut_customization_system_web_app_info.cc",
     "web_applications/shortcut_customization_system_web_app_info.h",
-    "web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.cc",
-    "web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h",
     "web_applications/system_web_app_install_utils.cc",
     "web_applications/system_web_app_install_utils.h",
     "web_applications/terminal_source.cc",
@@ -3149,7 +3149,6 @@
     "//ash/webui/shimless_rma",
     "//ash/webui/shimless_rma/backend",
     "//ash/webui/shortcut_customization_ui",
-    "//ash/webui/shortcut_customization_ui/backend",
     "//base",
     "//base:i18n",
     "//build:chromeos_buildflags",
diff --git a/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc b/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc
index 93abaa24..d194b07 100644
--- a/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc
+++ b/chrome/browser/ash/extensions/autotest_private/autotest_private_apitest.cc
@@ -558,9 +558,12 @@
       const std::vector<double>& scores) {
     std::vector<std::unique_ptr<ChromeSearchResult>> results;
     for (size_t i = 0; i < ids.size(); ++i) {
-      results.emplace_back(std::make_unique<app_list::TestResult>(
-          ids[i], display_types[i], categories[i], best_match_ranks[i],
-          /*relevance=*/scores[i], /*ftrl_result_score=*/scores[i]));
+      std::unique_ptr<app_list::TestResult> test_result =
+          std::make_unique<app_list::TestResult>(
+              ids[i], display_types[i], categories[i], best_match_ranks[i],
+              /*relevance=*/scores[i], /*ftrl_result_score=*/scores[i]);
+      test_result->scoring().override_filter_for_test = true;
+      results.emplace_back(std::move(test_result));
     }
     return results;
   }
@@ -575,7 +578,7 @@
 
 // TODO(https://crbug.com/1385365): This test is extremely flaky.
 IN_PROC_BROWSER_TEST_P(AutotestPrivateSearchTest,
-                       DISABLED_LauncherSearchBoxStateAPITest) {
+                       LauncherSearchBoxStateAPITest) {
   ash::ShellTestApi().SetTabletModeEnabledForTest(GetParam());
   test::GetAppListClient()->ShowAppList();
   if (!GetParam()) {
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc
index a588bd3..d3d8ae6 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_boot_performance_sampler_handler.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_bus_sampler_handler.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_cpu_sampler_handler.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.h"
 #include "chromeos/ash/services/cros_healthd/public/cpp/service_connection.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -104,61 +105,6 @@
   std::move(callback).Run(std::move(metric_data));
 }
 
-void HandleInputResult(OptionalMetricCallback callback,
-                       CrosHealthdMetricSampler::MetricType metric_type,
-                       cros_healthd::TelemetryInfoPtr result) {
-  absl::optional<MetricData> metric_data;
-  const auto& input_result = result->input_result;
-
-  if (!input_result.is_null()) {
-    switch (input_result->which()) {
-      case cros_healthd::InputResult::Tag::kError: {
-        DVLOG(1) << "cros_healthd: Error getting input info: "
-                 << input_result->get_error()->msg;
-        break;
-      }
-
-      case cros_healthd::InputResult::Tag::kInputInfo: {
-        const auto& input_info = input_result->get_input_info();
-        if (input_info.is_null()) {
-          DVLOG(1) << "Null InputInfo from cros_healthd";
-          break;
-        }
-
-        // Gather touch screen info.
-        if (metric_type == CrosHealthdMetricSampler::MetricType::kInfo) {
-          metric_data = absl::make_optional<MetricData>();
-          auto* const touch_screen_info_out =
-              metric_data->mutable_info_data()->mutable_touch_screen_info();
-
-          touch_screen_info_out->set_library_name(
-              input_info->touchpad_library_name);
-
-          for (const auto& screen : input_info->touchscreen_devices) {
-            if (screen->input_device->is_enabled &&
-                screen->input_device->connection_type ==
-                    cros_healthd::InputDevice::ConnectionType::kInternal) {
-              auto* const touch_screen_device_out =
-                  touch_screen_info_out->add_touch_screen_devices();
-              touch_screen_device_out->set_display_name(
-                  screen->input_device->name);
-              touch_screen_device_out->set_touch_points(screen->touch_points);
-              touch_screen_device_out->set_has_stylus(screen->has_stylus);
-            }
-          }
-          // Don't report anything if no internal touchscreen was detected.
-          if (touch_screen_info_out->touch_screen_devices().empty()) {
-            metric_data = absl::nullopt;
-          }
-        }
-        break;
-      }
-    }
-  }
-
-  std::move(callback).Run(std::move(metric_data));
-}
-
 void HandleDisplayResult(OptionalMetricCallback callback,
                          CrosHealthdMetricSampler::MetricType metric_type,
                          cros_healthd::TelemetryInfoPtr result) {
@@ -339,7 +285,8 @@
       break;
     }
     case cros_healthd::ProbeCategoryEnum::kInput: {
-      HandleInputResult(std::move(callback), metric_type, std::move(result));
+      CrosHealthdInputSamplerHandler handler = CrosHealthdInputSamplerHandler();
+      handler.HandleResult(std::move(result), std::move(callback));
       break;
     }
     case cros_healthd::ProbeCategoryEnum::kDisplay: {
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.cc
new file mode 100644
index 0000000..97bb9ab5
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.cc
@@ -0,0 +1,73 @@
+// 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/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "components/reporting/metrics/sampler.h"
+#include "components/reporting/proto/synced/metric_data.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace reporting {
+
+namespace cros_healthd = ::ash::cros_healthd::mojom;
+
+CrosHealthdInputSamplerHandler::~CrosHealthdInputSamplerHandler() = default;
+
+void CrosHealthdInputSamplerHandler::HandleResult(
+    cros_healthd::TelemetryInfoPtr result,
+    OptionalMetricCallback callback) const {
+  absl::optional<MetricData> metric_data;
+  const auto& input_result = result->input_result;
+
+  if (!input_result.is_null()) {
+    switch (input_result->which()) {
+      case cros_healthd::InputResult::Tag::kError: {
+        DVLOG(1) << "cros_healthd: Error getting input info: "
+                 << input_result->get_error()->msg;
+        break;
+      }
+
+      case cros_healthd::InputResult::Tag::kInputInfo: {
+        const auto& input_info = input_result->get_input_info();
+        if (input_info.is_null()) {
+          DVLOG(1) << "Null InputInfo from cros_healthd";
+          break;
+        }
+
+        // Gather touch screen info.
+        metric_data = absl::make_optional<MetricData>();
+        auto* const touch_screen_info_out =
+            metric_data->mutable_info_data()->mutable_touch_screen_info();
+
+        touch_screen_info_out->set_library_name(
+            input_info->touchpad_library_name);
+
+        for (const auto& screen : input_info->touchscreen_devices) {
+          if (screen->input_device->is_enabled &&
+              screen->input_device->connection_type ==
+                  cros_healthd::InputDevice::ConnectionType::kInternal) {
+            auto* const touch_screen_device_out =
+                touch_screen_info_out->add_touch_screen_devices();
+            touch_screen_device_out->set_display_name(
+                screen->input_device->name);
+            touch_screen_device_out->set_touch_points(screen->touch_points);
+            touch_screen_device_out->set_has_stylus(screen->has_stylus);
+          }
+        }
+        // Don't report anything if no internal touchscreen was detected.
+        if (touch_screen_info_out->touch_screen_devices().empty()) {
+          metric_data = absl::nullopt;
+        }
+        break;
+      }
+    }
+  }
+
+  std::move(callback).Run(std::move(metric_data));
+}
+
+}  // namespace reporting
\ No newline at end of file
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.h b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.h
new file mode 100644
index 0000000..178f3b2e
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_input_sampler_handler.h
@@ -0,0 +1,36 @@
+// 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_ASH_POLICY_REPORTING_METRICS_REPORTING_CROS_HEALTHD_SAMPLER_HANDLERS_CROS_HEALTHD_INPUT_SAMPLER_HANDLER_H_
+#define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_CROS_HEALTHD_SAMPLER_HANDLERS_CROS_HEALTHD_INPUT_SAMPLER_HANDLER_H_
+
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_sampler_handlers/cros_healthd_sampler_handler.h"
+#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
+#include "components/reporting/metrics/sampler.h"
+
+namespace reporting {
+
+namespace cros_healthd = ::ash::cros_healthd::mojom;
+
+// Class that handles the resulting data after probing the croshealthd for the
+// Input category.
+class CrosHealthdInputSamplerHandler : public CrosHealthdSamplerHandler {
+ public:
+  CrosHealthdInputSamplerHandler() = default;
+
+  CrosHealthdInputSamplerHandler(const CrosHealthdInputSamplerHandler&) =
+      delete;
+  CrosHealthdInputSamplerHandler& operator=(
+      const CrosHealthdInputSamplerHandler&) = delete;
+
+  ~CrosHealthdInputSamplerHandler() override;
+
+  // HandleResult converts |result| to MetricData.
+  void HandleResult(cros_healthd::TelemetryInfoPtr result,
+                    OptionalMetricCallback callback) const override;
+};
+
+}  // namespace reporting
+
+#endif  // CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_CROS_HEALTHD_SAMPLER_HANDLERS_CROS_HEALTHD_INPUT_SAMPLER_HANDLER_H_
diff --git a/chrome/browser/ash/video_conference/BUILD.gn b/chrome/browser/ash/video_conference/BUILD.gn
index 99f6b15..b46875a 100644
--- a/chrome/browser/ash/video_conference/BUILD.gn
+++ b/chrome/browser/ash/video_conference/BUILD.gn
@@ -13,10 +13,15 @@
     "video_conference_manager_ash.cc",
     "video_conference_manager_ash.h",
     "video_conference_state.h",
+    "video_conference_tray_controller_impl.cc",
+    "video_conference_tray_controller_impl.h",
   ]
   deps = [
+    "//ash",
     "//base",
     "//chromeos/crosapi/mojom",
+    "//media/capture:capture_lib",
+    "//media/capture/video/chromeos/mojom:cros_camera",
   ]
 }
 
diff --git a/chrome/browser/ash/video_conference/video_conference_tray_controller_impl.cc b/chrome/browser/ash/video_conference/video_conference_tray_controller_impl.cc
new file mode 100644
index 0000000..7b70926
--- /dev/null
+++ b/chrome/browser/ash/video_conference/video_conference_tray_controller_impl.cc
@@ -0,0 +1,25 @@
+// 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/ash/video_conference/video_conference_tray_controller_impl.h"
+
+#include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
+#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom-shared.h"
+
+namespace ash {
+
+VideoConferenceTrayControllerImpl::VideoConferenceTrayControllerImpl() =
+    default;
+
+VideoConferenceTrayControllerImpl::~VideoConferenceTrayControllerImpl() =
+    default;
+
+void VideoConferenceTrayControllerImpl::SetCameraSoftwareMuted(
+    bool mute_camera) {
+  media::CameraHalDispatcherImpl::GetInstance()->SetCameraSWPrivacySwitchState(
+      mute_camera ? cros::mojom::CameraPrivacySwitchState::ON
+                  : cros::mojom::CameraPrivacySwitchState::OFF);
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ash/video_conference/video_conference_tray_controller_impl.h b/chrome/browser/ash/video_conference/video_conference_tray_controller_impl.h
new file mode 100644
index 0000000..330902ac
--- /dev/null
+++ b/chrome/browser/ash/video_conference/video_conference_tray_controller_impl.h
@@ -0,0 +1,30 @@
+// 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_ASH_VIDEO_CONFERENCE_VIDEO_CONFERENCE_TRAY_CONTROLLER_IMPL_H_
+#define CHROME_BROWSER_ASH_VIDEO_CONFERENCE_VIDEO_CONFERENCE_TRAY_CONTROLLER_IMPL_H_
+
+#include "ash/system/video_conference/video_conference_tray_controller.h"
+
+namespace ash {
+
+// Implementation for VideoConferenceTrayController.
+class VideoConferenceTrayControllerImpl : public VideoConferenceTrayController {
+ public:
+  VideoConferenceTrayControllerImpl();
+
+  VideoConferenceTrayControllerImpl(const VideoConferenceTrayControllerImpl&) =
+      delete;
+  VideoConferenceTrayControllerImpl& operator=(
+      const VideoConferenceTrayControllerImpl&) = delete;
+
+  ~VideoConferenceTrayControllerImpl() override;
+
+  // VideoConferenceTrayController:
+  void SetCameraSoftwareMuted(bool mute_camera) override;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_VIDEO_CONFERENCE_VIDEO_CONFERENCE_TRAY_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/ash/web_applications/shortcut_customization_ui/OWNERS b/chrome/browser/ash/web_applications/shortcut_customization_ui/OWNERS
deleted file mode 100644
index d8674605..0000000
--- a/chrome/browser/ash/web_applications/shortcut_customization_ui/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://ash/webui/shortcut_customization_ui/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.cc b/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.cc
deleted file mode 100644
index e5a7b02..0000000
--- a/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.cc
+++ /dev/null
@@ -1,17 +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/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h"
-#include "chrome/browser/profiles/profile.h"
-
-ChromeShortcutCustomizationDelegate::ChromeShortcutCustomizationDelegate(
-    content::WebUI* web_ui)
-    : web_ui_(web_ui) {}
-
-ChromeShortcutCustomizationDelegate::~ChromeShortcutCustomizationDelegate() =
-    default;
-
-PrefService* ChromeShortcutCustomizationDelegate::GetPrefService() {
-  return Profile::FromWebUI(web_ui_)->GetPrefs();
-}
diff --git a/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h b/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h
deleted file mode 100644
index 3e36a8c..0000000
--- a/chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h
+++ /dev/null
@@ -1,35 +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_ASH_WEB_APPLICATIONS_SHORTCUT_CUSTOMIZATION_UI_CHROME_SHORTCUT_CUSTOMIZATION_DELEGATE_H_
-#define CHROME_BROWSER_ASH_WEB_APPLICATIONS_SHORTCUT_CUSTOMIZATION_UI_CHROME_SHORTCUT_CUSTOMIZATION_DELEGATE_H_
-
-#include "ash/webui/shortcut_customization_ui/backend/shortcut_customization_delegate.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/browser/web_ui.h"
-
-/**
- * Implementation of the ShortcutCustomizationDelegate interface. Provides the
- * shortcut customization app code in ash/ with functions that only exist in
- * chrome/.
- */
-class ChromeShortcutCustomizationDelegate
-    : public ash::shortcut_ui::ShortcutCustomizationDelegate {
- public:
-  explicit ChromeShortcutCustomizationDelegate(content::WebUI* web_ui);
-
-  ChromeShortcutCustomizationDelegate(
-      const ChromeShortcutCustomizationDelegate&) = delete;
-  ChromeShortcutCustomizationDelegate& operator=(
-      const ChromeShortcutCustomizationDelegate&) = delete;
-  ~ChromeShortcutCustomizationDelegate() override;
-
-  // Get the pref service.
-  PrefService* GetPrefService() override;
-
- private:
-  content::WebUI* web_ui_;
-};
-
-#endif  // CHROME_BROWSER_ASH_WEB_APPLICATIONS_SHORTCUT_CUSTOMIZATION_UI_CHROME_SHORTCUT_CUSTOMIZATION_DELEGATE_H_
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.cc b/chrome/browser/autofill/android/personal_data_manager_android.cc
index cca547c..279500e 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.cc
+++ b/chrome/browser/autofill/android/personal_data_manager_android.cc
@@ -116,28 +116,32 @@
     jdelegate_.Reset(env, jdelegate);
 
     if (!card_) {
-      OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE);
+      OnFullCardRequestFailed(card_->record_type(),
+                              FullCardRequest::FailureType::GENERIC_FAILURE);
       return;
     }
 
     content::WebContents* contents =
         content::WebContents::FromJavaWebContents(jweb_contents);
     if (!contents) {
-      OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE);
+      OnFullCardRequestFailed(card_->record_type(),
+                              FullCardRequest::FailureType::GENERIC_FAILURE);
       return;
     }
 
     ContentAutofillDriverFactory* factory =
         ContentAutofillDriverFactory::FromWebContents(contents);
     if (!factory) {
-      OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE);
+      OnFullCardRequestFailed(card_->record_type(),
+                              FullCardRequest::FailureType::GENERIC_FAILURE);
       return;
     }
 
     ContentAutofillDriver* driver =
         factory->DriverForFrame(contents->GetPrimaryMainFrame());
     if (!driver) {
-      OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE);
+      OnFullCardRequestFailed(card_->record_type(),
+                              FullCardRequest::FailureType::GENERIC_FAILURE);
       return;
     }
 
@@ -166,6 +170,7 @@
 
   // payments::FullCardRequest::ResultDelegate:
   void OnFullCardRequestFailed(
+      CreditCard::RecordType card_type,
       FullCardRequest::FailureType failure_type) override {
     JNIEnv* env = base::android::AttachCurrentThread();
     Java_FullCardRequestDelegate_onFullCardError(env, jdelegate_);
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 634bf72..7f1a1f3 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -229,9 +229,9 @@
         <include name="IDR_SOUND_SPOKEN_FEEDBACK_TOGGLE_COUNTDOWN_HIGH_WAV" file="resources\chromeos\sounds\spoken_feedback_toggle_countdown_high.wav" type="BINDATA" />
         <include name="IDR_SOUND_SPOKEN_FEEDBACK_TOGGLE_COUNTDOWN_LOW_WAV" file="resources\chromeos\sounds\spoken_feedback_toggle_countdown_low.wav" type="BINDATA" />
         <include name="IDR_SOUND_TOUCH_TYPE_WAV" file="resources\chromeos\sounds\touch_type.wav" type="BINDATA" />
-        <include name="IDR_SOUND_CHARGE_HIGH_BATTERY_WAV" file="resources\chromeos\sounds\power\charge_high.wav" type="BINDATA" />
-        <include name="IDR_SOUND_CHARGE_MEDIUM_BATTERY_WAV" file="resources\chromeos\sounds\power\charge_medium.wav" type="BINDATA" />
-        <include name="IDR_SOUND_CHARGE_LOW_BATTERY_WAV" file="resources\chromeos\sounds\power\charge_low.wav" type="BINDATA" />
+        <include name="IDR_SOUND_CHARGE_HIGH_BATTERY_WAV" file="resources\chromeos\sounds\power\charging_started_high.wav" type="BINDATA" />
+        <include name="IDR_SOUND_CHARGE_MEDIUM_BATTERY_WAV" file="resources\chromeos\sounds\power\charging_started_medium.wav" type="BINDATA" />
+        <include name="IDR_SOUND_CHARGE_LOW_BATTERY_WAV" file="resources\chromeos\sounds\power\charging_started_low.wav" type="BINDATA" />
         <include name="IDR_SOUND_NO_CHARGE_LOW_BATTERY_WAV" file="resources\chromeos\sounds\power\low_battery.wav" type="BINDATA" />
       </if>
       <if expr="chromeos_ash">
diff --git a/chrome/browser/browsing_data/counters/site_data_counter.cc b/chrome/browser/browsing_data/counters/site_data_counter.cc
index 4eb28bb6..2c782e3 100644
--- a/chrome/browser/browsing_data/counters/site_data_counter.cc
+++ b/chrome/browser/browsing_data/counters/site_data_counter.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "chrome/browser/browsing_data/counters/browsing_data_counter_utils.h"
 #include "chrome/browser/browsing_data/counters/site_data_counting_helper.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "components/browsing_data/core/pref_names.h"
 #include "components/sync/driver/sync_service.h"
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc b/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc
index cd5e27044..2be0adb0 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc
@@ -27,10 +27,31 @@
 #include "extensions/common/mojom/view_type.mojom.h"
 #include "extensions/test/result_catcher.h"
 #include "extensions/test/test_extension_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
 
-using DeveloperPrivateApiTest = ExtensionApiTest;
+class DeveloperPrivateApiTest : public ExtensionApiTest {
+ protected:
+  std::unique_ptr<api::developer_private::ExtensionInfo> GetExtensionInfo(
+      const Extension& extension) {
+    auto get_info_function =
+        base::MakeRefCounted<api::DeveloperPrivateGetExtensionInfoFunction>();
+    std::unique_ptr<base::Value> result =
+        extension_function_test_utils::RunFunctionAndReturnSingleResult(
+            get_info_function.get(),
+            content::JsReplace(R"([$1])", extension.id()), browser());
+    if (!result) {
+      ADD_FAILURE() << "No result back when getting extension info";
+      return nullptr;
+    }
+    std::unique_ptr<api::developer_private::ExtensionInfo> info =
+        api::developer_private::ExtensionInfo::FromValue(*result);
+    if (!info)
+      ADD_FAILURE() << "Problem creating ExtensionInfo from result data";
+    return info;
+  }
+};
 
 IN_PROC_BROWSER_TEST_F(DeveloperPrivateApiTest, Basics) {
   // Load up some extensions so that we can query their info and adjust their
@@ -58,16 +79,7 @@
   const Extension* app = LoadAndLaunchApp(dir);
 
   // Get the info about the app, including the inspectable views.
-  scoped_refptr<ExtensionFunction> function =
-      base::MakeRefCounted<api::DeveloperPrivateGetExtensionInfoFunction>();
-  std::unique_ptr<base::Value> result(
-      extension_function_test_utils::RunFunctionAndReturnSingleResult(
-          function.get(), base::StringPrintf("[\"%s\"]", app->id().c_str()),
-          browser()));
-  ASSERT_TRUE(result);
-  std::unique_ptr<api::developer_private::ExtensionInfo> info =
-      api::developer_private::ExtensionInfo::FromValue(*result);
-  ASSERT_TRUE(info);
+  auto info = GetExtensionInfo(*app);
 
   // There should be two inspectable views - the background page and the app
   // window.  Find the app window.
@@ -82,7 +94,8 @@
   ASSERT_TRUE(window_view);
 
   // Inspect the app window.
-  function = base::MakeRefCounted<api::DeveloperPrivateOpenDevToolsFunction>();
+  auto function =
+      base::MakeRefCounted<api::DeveloperPrivateOpenDevToolsFunction>();
   extension_function_test_utils::RunFunction(
       function.get(),
       base::StringPrintf("[{\"renderViewId\": %d, \"renderProcessId\": %d}]",
@@ -112,16 +125,7 @@
   WaitForExtensionNotIdle(extension->id());
 
   // Get the info about the extension, including the inspectable views.
-  scoped_refptr<ExtensionFunction> function =
-      base::MakeRefCounted<api::DeveloperPrivateGetExtensionInfoFunction>();
-  std::unique_ptr<base::Value> result(
-      extension_function_test_utils::RunFunctionAndReturnSingleResult(
-          function.get(),
-          base::StringPrintf("[\"%s\"]", extension->id().c_str()), browser()));
-  ASSERT_TRUE(result);
-  std::unique_ptr<api::developer_private::ExtensionInfo> info =
-      api::developer_private::ExtensionInfo::FromValue(*result);
-  ASSERT_TRUE(info);
+  auto info = GetExtensionInfo(*extension);
 
   // The embedded options page should show up.
   ASSERT_EQ(1u, info->views.size());
@@ -129,7 +133,8 @@
   ASSERT_EQ(api::developer_private::VIEW_TYPE_EXTENSION_GUEST, view.type);
 
   // Inspect the embedded options page.
-  function = base::MakeRefCounted<api::DeveloperPrivateOpenDevToolsFunction>();
+  auto function =
+      base::MakeRefCounted<api::DeveloperPrivateOpenDevToolsFunction>();
   extension_function_test_utils::RunFunction(
       function.get(),
       base::StringPrintf("[{\"renderViewId\": %d, \"renderProcessId\": %d}]",
@@ -164,16 +169,7 @@
                                                              extension->id());
 
   // Get the info about the extension, including the inspectable views.
-  auto get_info_function =
-      base::MakeRefCounted<api::DeveloperPrivateGetExtensionInfoFunction>();
-  std::unique_ptr<base::Value> result(
-      extension_function_test_utils::RunFunctionAndReturnSingleResult(
-          get_info_function.get(),
-          base::StringPrintf("[\"%s\"]", extension->id().c_str()), browser()));
-  ASSERT_TRUE(result);
-  std::unique_ptr<api::developer_private::ExtensionInfo> info =
-      api::developer_private::ExtensionInfo::FromValue(*result);
-  ASSERT_TRUE(info);
+  auto info = GetExtensionInfo(*extension);
 
   // There should be a worker based background for the extension.
   ASSERT_EQ(1u, info->views.size());
@@ -230,16 +226,7 @@
   ASSERT_TRUE(result_catcher.GetNextResult());
 
   // Get the info about the extension, including the inspectable views.
-  auto get_info_function =
-      base::MakeRefCounted<api::DeveloperPrivateGetExtensionInfoFunction>();
-  std::unique_ptr<base::Value> result(
-      extension_function_test_utils::RunFunctionAndReturnSingleResult(
-          get_info_function.get(),
-          base::StringPrintf("[\"%s\"]", extension->id().c_str()), browser()));
-  ASSERT_TRUE(result);
-  std::unique_ptr<api::developer_private::ExtensionInfo> info =
-      api::developer_private::ExtensionInfo::FromValue(*result);
-  ASSERT_TRUE(info);
+  auto info = GetExtensionInfo(*extension);
 
   // There should be a worker based background for the extension.
   ASSERT_EQ(1u, info->views.size());
@@ -282,6 +269,112 @@
   EXPECT_TRUE(DevToolsWindow::FindDevToolsWindow(service_worker_host.get()));
 }
 
+IN_PROC_BROWSER_TEST_F(DeveloperPrivateApiTest,
+                       InspectSplitModeServiceWorkerBackgrounds) {
+  ResultCatcher result_catcher;
+  // Load an extension that is service worker based, split mode and enabled in
+  // incognito.
+  static constexpr char kManifest[] =
+      R"({
+           "name": "Split mode worker test",
+           "manifest_version": 3,
+           "version": "0.1",
+           "background": {"service_worker": "worker.js"},
+           "incognito": "split"
+         })";
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(FILE_PATH_LITERAL("worker.js"),
+                     "chrome.test.notifyPass();");
+  const Extension* extension =
+      LoadExtension(test_dir.UnpackedPath(), {.allow_in_incognito = true});
+  ASSERT_TRUE(extension);
+  ASSERT_TRUE(result_catcher.GetNextResult());
+
+  // Get the info about the extension, including the inspectable views.
+  auto info = GetExtensionInfo(*extension);
+
+  // With no incognito window open, there should be a single worker based
+  // background page for the extension.
+  ASSERT_EQ(1u, info->views.size());
+  int main_render_process_id = -1;
+  {
+    const api::developer_private::ExtensionView& view = info->views[0];
+    EXPECT_EQ(
+        api::developer_private::VIEW_TYPE_EXTENSION_SERVICE_WORKER_BACKGROUND,
+        view.type);
+    EXPECT_NE(-1, view.render_process_id);
+    main_render_process_id = view.render_process_id;
+    EXPECT_FALSE(view.incognito);
+  }
+
+  // Now open up an incognito browser window page and check the inspectable
+  // views again. Waiting for the result catcher will wait for the incognito
+  // service worker to have become active.
+  Browser* inconito_browser = CreateIncognitoBrowser(browser()->profile());
+  ASSERT_TRUE(inconito_browser);
+  ASSERT_TRUE(result_catcher.GetNextResult());
+  info = GetExtensionInfo(*extension);
+  // The views should now have 2 entries, one for the main worker which will be
+  // the same as before and a new one for the incognito worker.
+  ASSERT_EQ(2u, info->views.size());
+  EXPECT_NE(info->views[0].incognito, info->views[1].incognito);
+  int incognito_render_process_id = -1;
+  for (auto& view : info->views) {
+    EXPECT_EQ(
+        api::developer_private::VIEW_TYPE_EXTENSION_SERVICE_WORKER_BACKGROUND,
+        view.type);
+    EXPECT_NE(-1, view.render_process_id);
+    if (view.incognito) {
+      EXPECT_NE(main_render_process_id, view.render_process_id);
+      incognito_render_process_id = view.render_process_id;
+    } else {
+      EXPECT_EQ(main_render_process_id, view.render_process_id);
+    }
+  }
+
+  // Open a devtools window for both the primary and incognito worker.
+  std::string kOpenDevToolsParams =
+      R"([{"renderViewId": -1,
+           "renderProcessId": $1,
+           "isServiceWorker": true,
+           "extensionId": $2,
+           "incognito": $3
+         }])";
+  DevToolsWindow* main_devtools_window = nullptr;
+  {
+    DevToolsWindowCreationObserver devtools_window_created_observer;
+    auto dev_tools_function =
+        base::MakeRefCounted<api::DeveloperPrivateOpenDevToolsFunction>();
+    extension_function_test_utils::RunFunction(
+        dev_tools_function.get(),
+        content::JsReplace(kOpenDevToolsParams, main_render_process_id,
+                           extension->id().c_str(), /*incognito:*/ false),
+        browser(), api_test_utils::NONE);
+    devtools_window_created_observer.WaitForLoad();
+    main_devtools_window = devtools_window_created_observer.devtools_window();
+  }
+  DevToolsWindow* incognito_devtools_window = nullptr;
+  {
+    DevToolsWindowCreationObserver devtools_window_created_observer;
+    auto dev_tools_function =
+        base::MakeRefCounted<api::DeveloperPrivateOpenDevToolsFunction>();
+    extension_function_test_utils::RunFunction(
+        dev_tools_function.get(),
+        content::JsReplace(kOpenDevToolsParams, incognito_render_process_id,
+                           extension->id().c_str(), /*incognito:*/ true),
+        browser(), api_test_utils::NONE);
+    devtools_window_created_observer.WaitForLoad();
+    incognito_devtools_window =
+        devtools_window_created_observer.devtools_window();
+  }
+
+  // Both windows should have opened and should not point to the same window.
+  ASSERT_TRUE(main_devtools_window);
+  ASSERT_TRUE(incognito_devtools_window);
+  ASSERT_NE(main_devtools_window, incognito_devtools_window);
+}
+
 // Test that offscreen documents show up in the list of inspectable views and
 // can be inspected.
 IN_PROC_BROWSER_TEST_F(DeveloperPrivateApiTest, InspectOffscreenDocument) {
@@ -315,16 +408,7 @@
   }
 
   // Get the list of inspectable views for the extension.
-  auto get_info_function =
-      base::MakeRefCounted<api::DeveloperPrivateGetExtensionInfoFunction>();
-  std::unique_ptr<base::Value> result =
-      extension_function_test_utils::RunFunctionAndReturnSingleResult(
-          get_info_function.get(),
-          content::JsReplace(R"([$1])", extension->id()), browser());
-  ASSERT_TRUE(result);
-  std::unique_ptr<api::developer_private::ExtensionInfo> info =
-      api::developer_private::ExtensionInfo::FromValue(*result);
-  ASSERT_TRUE(info);
+  auto info = GetExtensionInfo(*extension);
 
   // The only inspectable view should be the offscreen document. Validate the
   // metadata.
diff --git a/chrome/browser/extensions/devtools_util.cc b/chrome/browser/extensions/devtools_util.cc
index c4dde2b3..7a4a80d 100644
--- a/chrome/browser/extensions/devtools_util.cc
+++ b/chrome/browser/extensions/devtools_util.cc
@@ -57,7 +57,8 @@
     if (host->GetType() == content::DevToolsAgentHost::kTypeServiceWorker &&
         host->GetURL() ==
             extension->GetResourceURL(
-                BackgroundInfo::GetBackgroundServiceWorkerScript(extension))) {
+                BackgroundInfo::GetBackgroundServiceWorkerScript(extension)) &&
+        host->GetBrowserContext() == profile) {
       DevToolsWindow::OpenDevToolsWindow(host, profile);
       break;
     }
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 089e7ba..3e782c1 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -180,7 +180,6 @@
     &kAssistantIntentPageUrl,
     &kAssistantIntentTranslateInfo,
     &kAssistantNonPersonalizedVoiceSearch,
-    &kAppLaunchpad,
     &kAppMenuMobileSiteOption,
     &kAppToWebAttribution,
     &kBackgroundThreadPool,
@@ -514,8 +513,6 @@
              "AssistantNonPersonalizedVoiceSearch",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kAppLaunchpad, "AppLaunchpad", base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kAppMenuMobileSiteOption,
              "AppMenuMobileSiteOption",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 3682dc84..444de07d 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -27,7 +27,6 @@
 BASE_DECLARE_FEATURE(kAssistantIntentPageUrl);
 BASE_DECLARE_FEATURE(kAssistantIntentTranslateInfo);
 BASE_DECLARE_FEATURE(kAssistantNonPersonalizedVoiceSearch);
-BASE_DECLARE_FEATURE(kAppLaunchpad);
 BASE_DECLARE_FEATURE(kAppMenuMobileSiteOption);
 BASE_DECLARE_FEATURE(kAppToWebAttribution);
 BASE_DECLARE_FEATURE(kBackgroundThreadPool);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 8d9aeaf3..fd64bb8b 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -206,7 +206,6 @@
     public static final String AUTOFILL_MANUAL_FALLBACK_ANDROID = "AutofillManualFallbackAndroid";
     public static final String AUTOFILL_REFRESH_STYLE_ANDROID = "AutofillRefreshStyleAndroid";
     public static final String AUTOFILL_KEYBOARD_ACCESSORY = "AutofillKeyboardAccessory";
-    public static final String APP_LAUNCHPAD = "AppLaunchpad";
     public static final String APP_MENU_MOBILE_SITE_OPTION = "AppMenuMobileSiteOption";
     public static final String BACKGROUND_THREAD_POOL = "BackgroundThreadPool";
     public static final String BACK_FORWARD_CACHE = "BackForwardCache";
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor.h b/chrome/browser/metrics/chrome_metrics_service_accessor.h
index b7422c8..aa5ee98 100644
--- a/chrome/browser/metrics/chrome_metrics_service_accessor.h
+++ b/chrome/browser/metrics/chrome_metrics_service_accessor.h
@@ -44,7 +44,7 @@
 namespace extensions {
 class ChromeGuestViewManagerDelegate;
 class ChromeMetricsPrivateDelegate;
-}
+}  // namespace extensions
 
 namespace first_run {
 class FirstRunMasterPrefsVariationsSeedTest;
@@ -53,7 +53,7 @@
 namespace metrics {
 class ChromeOSPerUserMetricsBrowserTestBase;
 class UkmConsentParamBrowserTest;
-}
+}  // namespace metrics
 
 namespace welcome {
 void JoinOnboardingGroup(Profile* profile);
@@ -174,6 +174,7 @@
   friend class ClonedInstallClientIdResetBrowserTest;
   friend class metrics::ChromeOSPerUserMetricsBrowserTestBase;
   friend class SampledOutClientIdSavedBrowserTest;
+  friend class MetricsInternalsUIBrowserTest;
   FRIEND_TEST_ALL_PREFIXES(ChromeMetricsServiceAccessorTest,
                            MetricsReportingEnabled);
   FRIEND_TEST_ALL_PREFIXES(ChromeMetricsServicesManagerClientTest,
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/overlay_popup_ad_intervention_browsertest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/overlay_popup_ad_intervention_browsertest.cc
index f6353ea..ae5d525 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/overlay_popup_ad_intervention_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/overlay_popup_ad_intervention_browsertest.cc
@@ -95,8 +95,11 @@
       subresource_filter::mojom::AdsViolation::kOverlayPopupAd, 0);
 }
 
-// TODO(https://crbug.com/1350894): Flaky on Linux MSAN.
-#if BUILDFLAG(IS_LINUX) && defined(MEMORY_SANITIZER)
+// TODO(https://crbug.com/1350894): Flaky on Linux MSAN and LSAN, and on
+// ChromeOS for debug builds.
+#if (BUILDFLAG(IS_LINUX) &&                                     \
+     (defined(MEMORY_SANITIZER) || defined(LEAK_SANITIZER))) || \
+    (BUILDFLAG(IS_CHROMEOS) && !defined(NDEBUG))
 #define MAYBE_OverlayPopupAd_AdInterventionTriggered \
   DISABLED_OverlayPopupAd_AdInterventionTriggered
 #else
diff --git a/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn b/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
index 4622e18..e0861f59 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
+++ b/chrome/browser/resources/chromeos/assistant_optin/BUILD.gn
@@ -79,8 +79,6 @@
   out_manifest = "$target_gen_dir/$existing_files_manifest"
   in_files = [
     "assistant_common_styles.html",
-    "assistant_loading.html",
-    "assistant_loading.js",
     "assistant_optin.html",
     "assistant_optin.js",
     "assistant_related_info.html",
@@ -108,7 +106,6 @@
   in_files = [
     "assistant_common_styles.m.js",
     "assistant_icon.m.js",
-    "assistant_loading.m.js",
     "assistant_related_info.m.js",
     "assistant_value_prop.m.js",
     "assistant_voice_match.m.js",
@@ -126,6 +123,7 @@
   out_manifest = "$target_gen_dir/$web_components_manifest"
 
   in_files = [
+    "assistant_loading.js",
     "assistant_optin_flow.js",
     "setting_zippy.js",
   ]
@@ -146,13 +144,13 @@
   ]
 }
 
-js_library("assistant_loading.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.m.js" ]
+js_library("assistant_loading") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js" ]
   deps = [
     "../login/components/behaviors:oobe_i18n_behavior.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
-  extra_deps = [ ":assistant_loading_module" ]
+  extra_deps = [ ":web_components" ]
 }
 
 js_library("assistant_optin_flow") {
@@ -211,7 +209,6 @@
   public_deps = [
     ":assistant_common_styles_module",
     ":assistant_icon_module",
-    ":assistant_loading_module",
     ":assistant_related_info_module",
     ":assistant_value_prop_module",
     ":assistant_voice_match_module",
@@ -232,15 +229,6 @@
   html_type = "iron-iconset"
 }
 
-polymer_modulizer("assistant_loading") {
-  js_file = "assistant_loading.js"
-  html_file = "assistant_loading.html"
-  html_type = "dom-module"
-  auto_imports = assistant_auto_imports
-  migrated_imports = assistant_migrated_imports
-  namespace_rewrites = assistant_namespace_rewrites
-}
-
 polymer_modulizer("assistant_related_info") {
   js_file = "assistant_related_info.js"
   html_file = "assistant_related_info.html"
@@ -287,6 +275,7 @@
 
 html_to_js("web_components") {
   js_files = [
+    "assistant_loading.js",
     "assistant_optin_flow.js",
     "setting_zippy.js",
   ]
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.html
index 614735a..e2ecbf66 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.html
@@ -4,71 +4,49 @@
 found in the LICENSE file.
 -->
 
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="oobe-dialog-host-styles assistant-common-styles">
+  #retry-button {
+    margin-inline-end: 0;
+  }
 
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
-
-<link rel="import" href="../components/buttons/oobe_text_button.html">
-<link rel="import" href="../components/behaviors/multi_step_behavior.html">
-<link rel="import" href="../components/behaviors/oobe_dialog_host_behavior.html">
-<link rel="import" href="../components/behaviors/oobe_i18n_behavior.html">
-<link rel="import" href="../components/common_styles/oobe_dialog_host_styles.html">
-<link rel="import" href="../components/dialogs/oobe_adaptive_dialog.html">
-<link rel="import" href="../components/dialogs/oobe_content_dialog.html">
-
-<link rel="import" href="./assistant_icon.html">
-<link rel="import" href="./browser_proxy.html">
-<link rel="import" href="./assistant_common_styles.html">
-
-<dom-module id="assistant-loading">
-  <template>
-    <style include="oobe-dialog-host-styles assistant-common-styles">
-      #retry-button {
-        margin-inline-end: 0;
-      }
-
-      paper-progress {
-        --paper-progress-active-color: var(--cros-slider-color-active);
-        --paper-progress-container-color: var(--cros-slider-track-color-active);
-        --paper-progress-secondary-color: var(--cros-slider-color-active);
-        display: block;
-        height: 4px;
-        padding: 12px 0 0 0;
-        width: 100%;
-      }
-    </style>
-    <oobe-content-dialog id="loading-dialog" role="dialog" for-step="loading"
-        no-buttons>
-      <div slot="content" class="flex layout vertical center-justified">
-        <div id="loading-content" class="content">
-          <div>
-            [[i18nDynamic(locale, 'assistantOptinLoading')]]
-          </div>
-          <paper-progress class="slow" indeterminate></paper-progress>
-        </div>
+  paper-progress {
+    --paper-progress-active-color: var(--cros-slider-color-active);
+    --paper-progress-container-color: var(--cros-slider-track-color-active);
+    --paper-progress-secondary-color: var(--cros-slider-color-active);
+    display: block;
+    height: 4px;
+    padding: 12px 0 0 0;
+    width: 100%;
+  }
+</style>
+<oobe-content-dialog id="loading-dialog" role="dialog" for-step="loading"
+    no-buttons>
+  <div slot="content" class="flex layout vertical center-justified">
+    <div id="loading-content" class="content">
+      <div>
+        [[i18nDynamic(locale, 'assistantOptinLoading')]]
       </div>
-    </oobe-content-dialog>
-    <oobe-adaptive-dialog id="error-dialog" role="dialog" hide-shadow
-        for-step="error">
-      <iron-icon slot="icon" icon="assistant-32:assistant"
-          aria-label$="[[i18nDynamic(locale, 'assistantLogo')]]">
-      </iron-icon>
-      <h1 slot="title">
-        [[i18nDynamic(locale, 'assistantOptinLoadErrorTitle')]]
-      </h1>
-      <div slot="subtitle">
-        [[i18nDynamic(locale, 'assistantOptinLoadErrorMessage')]]
-      </div>
-      <div slot="bottom-buttons">
-        <oobe-text-button id="skip-button" on-click="onSkipTap_"
-            disabled="[[buttonsDisabled]]" text-key="assistantOptinSkipButton">
-        </oobe-text-button>
-        <oobe-text-button id="retry-button" inverse on-click="onRetryTap_"
-            disabled="[[buttonsDisabled]]" text-key="assistantOptinRetryButton">
-        </oobe-text-button>
-      </div>
-    </oobe-adaptive-dialog>
-  </template>
-  <script src="assistant_loading.js"></script>
-</dom-module>
+      <paper-progress class="slow" indeterminate></paper-progress>
+    </div>
+  </div>
+</oobe-content-dialog>
+<oobe-adaptive-dialog id="error-dialog" role="dialog" hide-shadow
+    for-step="error">
+  <iron-icon slot="icon" icon="assistant-32:assistant"
+      aria-label$="[[i18nDynamic(locale, 'assistantLogo')]]">
+  </iron-icon>
+  <h1 slot="title">
+    [[i18nDynamic(locale, 'assistantOptinLoadErrorTitle')]]
+  </h1>
+  <div slot="subtitle">
+    [[i18nDynamic(locale, 'assistantOptinLoadErrorMessage')]]
+  </div>
+  <div slot="bottom-buttons">
+    <oobe-text-button id="skip-button" on-click="onSkipTap_"
+        disabled="[[buttonsDisabled]]" text-key="assistantOptinSkipButton">
+    </oobe-text-button>
+    <oobe-text-button id="retry-button" inverse on-click="onRetryTap_"
+        disabled="[[buttonsDisabled]]" text-key="assistantOptinRetryButton">
+    </oobe-text-button>
+  </div>
+</oobe-adaptive-dialog>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js
index 067db23..ce30ac7f 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_loading.js
@@ -9,7 +9,23 @@
  * Event 'reload' will be fired when the user click the retry button.
  */
 
-/* #js_imports_placeholder */
+import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '//resources/polymer/v3_0/paper-progress/paper-progress.js';
+import '../components/buttons/oobe_text_button.js';
+import '../components/common_styles/oobe_dialog_host_styles.m.js';
+import '../components/dialogs/oobe_adaptive_dialog.js';
+import '../components/dialogs/oobe_content_dialog.js';
+import './assistant_icon.m.js';
+import './assistant_common_styles.m.js';
+
+import {afterNextRender, html, mixinBehaviors, Polymer, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {MultiStepBehavior, MultiStepBehaviorInterface} from '../components/behaviors/multi_step_behavior.m.js';
+import {OobeDialogHostBehavior} from '../components/behaviors/oobe_dialog_host_behavior.m.js';
+import {OobeI18nBehavior, OobeI18nBehaviorInterface} from '../components/behaviors/oobe_i18n_behavior.m.js';
+
+import {BrowserProxyImpl} from './browser_proxy.m.js';
+
 
 const AssistantLoadingUIState = {
   LOADING: 'loading',
@@ -21,8 +37,8 @@
  * @constructor
  * @extends {PolymerElement}
  */
-const AssistantLoadingBase = Polymer.mixinBehaviors(
-    [OobeI18nBehavior, MultiStepBehavior], Polymer.Element);
+const AssistantLoadingBase =
+    mixinBehaviors([OobeI18nBehavior, MultiStepBehavior], PolymerElement);
 
 /**
  * @polymer
@@ -32,7 +48,9 @@
     return 'assistant-loading';
   }
 
-  /* #html_template_placeholder */
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
   static get properties() {
     return {
@@ -72,8 +90,8 @@
      */
     this.loadingTimeout_ = null;
 
-    /** @private {?assistant.BrowserProxy} */
-    this.browserProxy_ = assistant.BrowserProxyImpl.getInstance();
+    /** @private {?BrowserProxy} */
+    this.browserProxy_ = BrowserProxyImpl.getInstance();
   }
 
   defaultUIStep() {
@@ -153,8 +171,7 @@
    */
   onShow() {
     this.reloadPage();
-    Polymer.RenderStatus.afterNextRender(
-        this, () => this.$['loading-dialog'].focus());
+    afterNextRender(this, () => this.$['loading-dialog'].focus());
   }
 }
 
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
index 984bb3dc..b936278 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
@@ -11,7 +11,7 @@
 import '../components/common_styles/common_styles.m.js';
 import './assistant_common_styles.m.js';
 import './assistant_icon.m.js';
-import './assistant_loading.m.js';
+import './assistant_loading.js';
 import './assistant_related_info.m.js';
 import './assistant_voice_match.m.js';
 import './assistant_value_prop.m.js';
diff --git a/chrome/browser/resources/chromeos/assistant_optin/modulization_utils.gni b/chrome/browser/resources/chromeos/assistant_optin/modulization_utils.gni
index 7c44e51..f69cc5d4 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/modulization_utils.gni
+++ b/chrome/browser/resources/chromeos/assistant_optin/modulization_utils.gni
@@ -36,6 +36,7 @@
   "chrome/browser/resources/chromeos/components/oobe_cr_lottie.html",
 
   # Assistant
+  "chrome/browser/resources/chromeos/assistant_optin/assistant_loading.html",
   "chrome/browser/resources/chromeos/assistant_optin/setting_zippy.html",
 
   # Chrome
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index c613bf6..9f10a01 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -217,10 +217,8 @@
     "screens/common/family_link_notice.m.js",
     "screens/common/guest_tos.m.js",
     "screens/common/hw_data_collection.m.js",
-    "screens/common/recommend_apps.m.js",
     "screens/common/smart_privacy_protection.m.js",
     "screens/common/sync_consent.m.js",
-    "screens/oobe/quick_start.m.js",
 
     # Special files.
     "keyboard_utils.m.js",  # See chromeos/keyboard/BUILD.gn
@@ -284,6 +282,7 @@
     "screens/common/os_trial.js",
     "screens/common/parental_handoff.js",
     "screens/common/pin_setup.js",
+    "screens/common/recommend_apps.js",
     "screens/common/saml_confirm_password.js",
     "screens/common/signin_fatal_error.js",
     "screens/common/theme_selection.js",
@@ -308,6 +307,7 @@
     "screens/oobe/hid_detection.js",
     "screens/oobe/oobe_network.js",
     "screens/oobe/packaged_license.js",
+    "screens/oobe/quick_start.js",
     "screens/oobe/update.js",
     "screens/oobe/welcome.js",
     "screens/oobe/welcome_dialog.js",
@@ -322,7 +322,6 @@
     "components:polymer3_elements",
     "debug:modulize",
     "screens/common:polymer3_elements",
-    "screens/oobe:polymer3_elements",
     "test_api:modulize",
   ]
 }
diff --git a/chrome/browser/resources/chromeos/login/debug/debug.js b/chrome/browser/resources/chromeos/login/debug/debug.js
index 4542f7b7..560b903 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug.js
@@ -1334,35 +1334,26 @@
           id: '2-apps',
           trigger: (screen) => {
             screen.reset();
-            screen.setWebview(RECOMMENDED_APPS_CONTENT);
             screen.loadAppList([
               {
-                name: 'Test app 1',
-                package_name: 'test1.app',
+                title: 'gApp',
+                icon_url: 'https://www.google.com/favicon.ico',
+                category: 'Games',
+                in_app_purchases: true,
+                was_installed: false,
+                content_rating: '',
               },
               {
-                name: 'Test app 2 with some really long name',
-                package_name: 'test2.app',
+                title: 'anotherGapp',
+                icon_url: 'https://www.google.com/favicon.ico',
+                category: 'Games',
+                in_app_purchases: true,
+                was_installed: false,
+                content_rating: '',
               },
             ]);
           },
         },
-        {
-          id: '21-apps',
-          trigger: (screen) => {
-            // There can be up to 21 apps: see recommend_apps_fetcher_impl
-            screen.reset();
-            screen.setWebview(RECOMMENDED_APPS_CONTENT);
-            const apps = [];
-            for (let i = 1; i <= 21; i++) {
-              apps.push({
-                name: 'Test app ' + i,
-                package_name: 'app.test' + i,
-              });
-            }
-            screen.loadAppList(apps);
-          },
-        },
       ],
     },
     {
diff --git a/chrome/browser/resources/chromeos/login/screens.js b/chrome/browser/resources/chromeos/login/screens.js
index f0faf07..aff3ace0 100644
--- a/chrome/browser/resources/chromeos/login/screens.js
+++ b/chrome/browser/resources/chromeos/login/screens.js
@@ -34,7 +34,7 @@
 import './screens/common/os_trial.js';
 import './screens/common/parental_handoff.js';
 import './screens/common/pin_setup.js';
-import './screens/common/recommend_apps.m.js';
+import './screens/common/recommend_apps.js';
 import './screens/common/saml_confirm_password.js';
 import './screens/common/signin_fatal_error.js';
 import './screens/common/smart_privacy_protection.m.js';
@@ -62,7 +62,7 @@
 import './screens/oobe/hid_detection.js';
 import './screens/oobe/oobe_network.js';
 import './screens/oobe/packaged_license.js';
-import './screens/oobe/quick_start.m.js';
+import './screens/oobe/quick_start.js';
 import './screens/oobe/update.js';
 import './screens/oobe/welcome.js';
 
diff --git a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
index 1c7097b..a180498 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
@@ -16,7 +16,6 @@
     ":family_link_notice_module",
     ":guest_tos_module",
     ":hw_data_collection_module",
-    ":recommend_apps_module",
     ":smart_privacy_protection_module",
     ":sync_consent_module",
   ]
@@ -67,7 +66,7 @@
     ":os_trial",
     ":parental_handoff",
     ":pin_setup",
-    ":recommend_apps.m",
+    ":recommend_apps",
     ":saml_confirm_password",
     ":signin_fatal_error",
     ":smart_privacy_protection.m",
@@ -529,8 +528,8 @@
   extra_deps = [ ":web_components" ]
 }
 
-js_library("recommend_apps.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.m.js" ]
+js_library("recommend_apps") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js" ]
   deps = [
     "../../components:oobe_apps_list",
     "../../components:oobe_types.m",
@@ -545,7 +544,7 @@
     "//ui/webui/resources/js:load_time_data.m",
   ]
   externs_list = [ "$externs_path/webview_tag.js" ]
-  extra_deps = [ ":recommend_apps_module" ]
+  extra_deps = [ ":web_components" ]
 }
 
 js_library("saml_confirm_password") {
@@ -704,15 +703,6 @@
   migrated_imports = oobe_migrated_imports
 }
 
-polymer_modulizer("recommend_apps") {
-  js_file = "recommend_apps.js"
-  html_file = "recommend_apps.html"
-  html_type = "dom-module"
-  auto_imports = oobe_auto_imports
-  namespace_rewrites = oobe_namespace_rewrites
-  migrated_imports = oobe_migrated_imports
-}
-
 polymer_modulizer("smart_privacy_protection") {
   js_file = "smart_privacy_protection.js"
   html_file = "smart_privacy_protection.html"
@@ -758,6 +748,7 @@
     "os_trial.js",
     "parental_handoff.js",
     "pin_setup.js",
+    "recommend_apps.js",
     "saml_confirm_password.js",
     "signin_fatal_error.js",
     "theme_selection.js",
diff --git a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.html b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.html
index c1f37d0..aa58553 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.html
@@ -4,70 +4,47 @@
 found in the LICENSE file.
 -->
 
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/load_time_data.html">
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<style include="oobe-dialog-host-styles action-link">
+  #skipButton {
+    color: rgba(0, 0, 0, 0.54);
+    padding-inline-end: 6px;
+  }
 
-<link rel="import" href="../../display_manager.html">
-<link rel="import" href="../../components/display_manager_types.html">
-<link rel="import" href="../../components/behaviors/login_screen_behavior.html">
-<link rel="import" href="../../components/behaviors/multi_step_behavior.html">
-<link rel="import" href="../../components/behaviors/oobe_dialog_host_behavior.html">
-<link rel="import" href="../../components/behaviors/oobe_i18n_behavior.html">
-<link rel="import" href="../../components/buttons/oobe_text_button.html">
-<link rel="import" href="../../components/common_styles/oobe_dialog_host_styles.html">
-<link rel="import" href="../../components/dialogs/oobe_adaptive_dialog.html">
-<link rel="import" href="../../components/oobe_apps_list.html">
-<link rel="import" href="../../components/oobe_types.html">
-
-<dom-module id="recommend-apps-element">
-  <template>
-    <style include="oobe-dialog-host-styles action-link">
-      #skipButton {
-        color: rgba(0, 0, 0, 0.54);
-        padding-inline-end: 6px;
-      }
-
-      #app-list-view-container {
-        overflow-y: auto;
-      }
-    </style>
-    <oobe-loading-dialog id="loadingDialog" for-step="loading"
-        title-key="recommendAppsLoading">
-      <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
-    </oobe-loading-dialog>
-    <oobe-adaptive-dialog id="appsDialog" role="dialog"
-        aria-label$="[[i18nDynamic(locale, 'recommendAppsScreenTitle')]]"
-        no-footer-padding footer-shrinkable for-step="list">
-      <iron-icon src="chrome://oobe/arc_support/icon/playstore.svg" slot="icon">
-      </iron-icon>
-      <h1 slot="title">
-        [[i18nDynamic(locale, 'recommendAppsScreenTitle')]]
-      </h1>
-      <div slot="subtitle">
-        [[i18nDynamic(locale, 'recommendAppsScreenDescription')]]
+  #app-list-view-container {
+    overflow-y: auto;
+  }
+</style>
+<oobe-loading-dialog id="loadingDialog" for-step="loading"
+    title-key="recommendAppsLoading">
+  <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
+</oobe-loading-dialog>
+<oobe-adaptive-dialog id="appsDialog" role="dialog"
+    aria-label$="[[i18nDynamic(locale, 'recommendAppsScreenTitle')]]"
+    no-footer-padding footer-shrinkable for-step="list">
+  <iron-icon src="chrome://oobe/arc_support/icon/playstore.svg" slot="icon">
+  </iron-icon>
+  <h1 slot="title">
+    [[i18nDynamic(locale, 'recommendAppsScreenTitle')]]
+  </h1>
+  <div slot="subtitle">
+    [[i18nDynamic(locale, 'recommendAppsScreenDescription')]]
+  </div>
+  <div id="app-list-view-container" slot="content">
+    <oobe-apps-list id="appsList" app-list="[[appList_]]"
+        apps-selected="{{appsSelected_}}"
+        on-apps-list-loaded="onFullyLoaded_">
+      <div slot="selectAllTitle">
+        [[i18nDynamic(locale, 'recommendAppsSelectAll')]]
       </div>
-      <div id="app-list-view-container" slot="content">
-        <oobe-apps-list id="appsList" app-list="[[appList_]]"
-            apps-selected="{{appsSelected_}}"
-            on-apps-list-loaded="onFullyLoaded_">
-          <div slot="selectAllTitle">
-            [[i18nDynamic(locale, 'recommendAppsSelectAll')]]
-          </div>
-        </oobe-apps-list>
-      </div>
-      <div slot="bottom-buttons">
-        <oobe-text-button id="skipButton"
-            text-key="recommendAppsSkip" on-click="onSkip_" border>
-        </oobe-text-button>
-        <oobe-text-button id="installButton" on-click="onInstall_" inverse
-            text-key="recommendAppsInstall"
-            disabled="[[!canProceed_(appsSelected_)]]">
-        </oobe-text-button>
-      </div>
-    </oobe-adaptive-dialog>
-  </template>
-  <script src="recommend_apps.js"></script>
-</dom-module>
+    </oobe-apps-list>
+  </div>
+  <div slot="bottom-buttons">
+    <oobe-text-button id="skipButton"
+        text-key="recommendAppsSkip" on-click="onSkip_" border>
+    </oobe-text-button>
+    <oobe-text-button id="installButton" on-click="onInstall_" inverse
+        text-key="recommendAppsInstall"
+        disabled="[[!canProceed_(appsSelected_)]]">
+    </oobe-text-button>
+  </div>
+</oobe-adaptive-dialog>
diff --git a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js
index 840eb08..1b14545f 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/recommend_apps.js
@@ -7,7 +7,24 @@
  * screen.
  */
 
-/* #js_imports_placeholder */
+import '//resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '../../components/common_styles/oobe_dialog_host_styles.m.js';
+
+import {assert, assertNotReached} from '//resources/js/assert.js';
+import {loadTimeData} from '//resources/js/load_time_data.m.js';
+import {html, mixinBehaviors, Polymer, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {LoginScreenBehavior, LoginScreenBehaviorInterface} from '../../components/behaviors/login_screen_behavior.m.js';
+import {MultiStepBehavior, MultiStepBehaviorInterface} from '../../components/behaviors/multi_step_behavior.m.js';
+import {OobeDialogHostBehavior} from '../../components/behaviors/oobe_dialog_host_behavior.m.js';
+import {OobeI18nBehavior, OobeI18nBehaviorInterface} from '../../components/behaviors/oobe_i18n_behavior.m.js';
+import {OobeTextButton} from '../../components/buttons/oobe_text_button.js';
+import {OobeAdaptiveDialog} from '../../components/dialogs/oobe_adaptive_dialog.js';
+import {OOBE_UI_STATE} from '../../components/display_manager_types.m.js';
+import {OobeAppsList} from '../../components/oobe_apps_list.js';
+
+
 
 /**
  * UI mode for the dialog.
@@ -27,9 +44,14 @@
  * @implements {MultiStepBehaviorInterface}
  * @implements {OobeI18nBehaviorInterface}
  */
-const RecommendAppsElementBase = Polymer.mixinBehaviors(
-  [OobeI18nBehavior, OobeDialogHostBehavior, LoginScreenBehavior, MultiStepBehavior],
-  Polymer.Element);
+const RecommendAppsElementBase = mixinBehaviors(
+    [
+      OobeI18nBehavior,
+      OobeDialogHostBehavior,
+      LoginScreenBehavior,
+      MultiStepBehavior,
+    ],
+    PolymerElement);
 
 /**
  * @typedef {{
@@ -50,7 +72,9 @@
     return 'recommend-apps-element';
   }
 
-  /* #html_template_placeholder */
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
   static get properties() {
     return {
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
index c7ebeef..8cace5b 100644
--- a/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/oobe/BUILD.gn
@@ -8,9 +8,6 @@
 import("//tools/polymer/polymer.gni")
 import("../../oobe_auto_imports.gni")
 
-group("polymer3_elements") {
-  public_deps = [ ":quick_start_module" ]
-}
 
 js_type_check("closure_compile") {
   is_polymer3 = true
@@ -30,7 +27,7 @@
     ":hid_detection",
     ":oobe_network",
     ":packaged_license",
-    ":quick_start.m",
+    ":quick_start",
     ":update",
     ":welcome",
     ":welcome_dialog",
@@ -128,8 +125,8 @@
   extra_deps = [ ":web_components" ]
 }
 
-js_library("quick_start.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.m.js" ]
+js_library("quick_start") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.js" ]
   deps = [
     "../../components:oobe_types.m",
     "../../components/behaviors:login_screen_behavior.m",
@@ -137,7 +134,7 @@
     "../../components/dialogs:oobe_loading_dialog.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
-  extra_deps = [ ":quick_start_module" ]
+  extra_deps = [ ":web_components" ]
 }
 
 js_library("oobe_network") {
@@ -219,15 +216,6 @@
   externs_list = [ "$externs_path/tts.js" ]
 }
 
-polymer_modulizer("quick_start") {
-  js_file = "quick_start.js"
-  html_file = "quick_start.html"
-  html_type = "dom-module"
-  auto_imports = oobe_auto_imports
-  migrated_imports = oobe_migrated_imports
-  namespace_rewrites = oobe_namespace_rewrites
-}
-
 html_to_js("web_components") {
   js_files = [
     "auto_enrollment_check.js",
@@ -238,6 +226,7 @@
     "hid_detection.js",
     "oobe_network.js",
     "packaged_license.js",
+    "quick_start.js",
     "update.js",
     "welcome.js",
     "welcome_dialog.js",
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.html b/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.html
index e6905cf..135bcda 100644
--- a/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.html
+++ b/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.html
@@ -4,125 +4,109 @@
 found in the LICENSE file.
 -->
 
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="oobe-dialog-host-styles">
+  svg {
+    margin: 28px 14px;
+  }
 
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+  #figures {
+    margin: auto;
+  }
 
-<link rel="import" href="../../components/behaviors/login_screen_behavior.html">
-<link rel="import" href="../../components/behaviors/multi_step_behavior.html">
-<link rel="import" href="../../components/common_styles/common_styles.html">
-<link rel="import" href="../../components/dialogs/oobe_adaptive_dialog.html">
-<link rel="import" href="../../components/dialogs/oobe_loading_dialog.html">
-<link rel="import" href="../../components/oobe_types.html">
+  div[clr=blue] {
+    color: var(--google-blue-500);
+  }
+  div[clr=red] {
+    color: var(--google-red-500);
+  }
+  div[clr=green] {
+    color: var(--google-green-500);
+  }
+  div[clr=yellow] {
+    color: var(--google-yellow-500);
+  }
 
-<dom-module id="quick-start-element">
-  <template>
-    <style include="oobe-dialog-host-styles">
-      svg {
-        margin: 28px 14px;
-      }
+  span {
+    font-size: 22px;
+  }
+</style>
+<oobe-loading-dialog id="loadingDialog" for-step="loading"
+    title-key="gaiaLoading">
+</oobe-loading-dialog>
+<oobe-adaptive-dialog for-step="verification">
+  <h1 slot="title">
+    <!--TODO(https://crbug.com/1278686) Update with finalize strings -->
+    Scan the QR code
+  </h1>
+  <div id="code" class="layout horizontal" slot="content">
+    <div id="qrCodeWrapper">
+      <canvas id="qrCodeCanvas" width="[[canvasSize_]]"
+          height="[[canvasSize_]]">
+    </div>
+  </div>
+  <div slot="bottom-buttons">
+    <oobe-next-button on-click="onNextClicked_"></oobe-next-button>
+  </div>
+</oobe-adaptive-dialog>
+<oobe-adaptive-dialog for-step="figures">
+  <h1 slot="title">
+    <!--TODO(https://crbug.com/1278686) Update with finalize strings -->
+    Verify the shapes
+  </h1>
+  <div id="figures" class="layout horizontal" slot="content">
+    <!-- We inline SVGs here to be able to control the color of them. -->
+    <template is="dom-repeat" items="[[figures_]]">
+      <div class="layout vertical center">
+        <div clr$="[[item.color]]">
+          <template is="dom-if" if="[[isEq_(item.shape, shapes_.CIRCLE)]]">
+            <svg width="100" height="100" viewBox="0 0 100 100" fill="none"
+                xmlns="http://www.w3.org/2000/svg">
+              <circle cx="50" cy="50" r="46" fill="currentColor">
+              </circle>
+            </svg>
+          </template>
 
-      #figures {
-        margin: auto;
-      }
+          <template is="dom-if" if="[[isEq_(item.shape, shapes_.DIAMOND)]]">
+            <svg width="100" height="100" viewBox="0 0 100 100" fill="none"
+                xmlns="http://www.w3.org/2000/svg">
+              <path d="M45.793 5.746L5.749 45.789a5.96 5.96 0 0 0 0 8.429L45.793 94.26a5.96 5.96 0 0 0 8.428 0l40.044-40.043a5.96 5.96 0 0 0 0-8.429L54.22 5.746a5.96 5.96 0 0 0-8.428 0z" fill="currentColor">
+              </path>
+            </svg>
+          </template>
 
-      div[clr=blue] {
-        color: var(--google-blue-500);
-      }
-      div[clr=red] {
-        color: var(--google-red-500);
-      }
-      div[clr=green] {
-        color: var(--google-green-500);
-      }
-      div[clr=yellow] {
-        color: var(--google-yellow-500);
-      }
+          <template is="dom-if"
+              if="[[isEq_(item.shape, shapes_.TRIANGLE)]]">
+            <svg width="100" height="100" viewBox="0 0 100 100" fill="none"
+                xmlns="http://www.w3.org/2000/svg">
+              <g clip-path="url(#clip0_123_9159)">
+                <path d="M50 95.97H11.773a7.778 7.778 0 0 1-6.7-3.879 7.763 7.763 0 0 1-.023-7.737L43.267 7.879a7.76 7.76 0 0 1 6.728-3.887 7.768 7.768 0 0 1 6.728 3.887L94.95 84.354a7.763 7.763 0 0 1-2.862 10.573 7.778 7.778 0 0 1-3.871 1.043H50z" fill="currentColor">
+                </path>
+              </g>
+              <defs>
+                <clipPath id="clip0_123_9159">
+                  <path fill="#fff" transform="translate(4 4)" d="M0 0h92v92H0z">
+                  </path>
+                </clipPath>
+              </defs>
+            </svg>
+          </template>
 
-      span {
-        font-size: 22px;
-      }
-    </style>
-    <oobe-loading-dialog id="loadingDialog" for-step="loading"
-        title-key="gaiaLoading">
-    </oobe-loading-dialog>
-    <oobe-adaptive-dialog for-step="verification">
-      <h1 slot="title">
-        <!--TODO(https://crbug.com/1278686) Update with finalize strings -->
-        Scan the QR code
-      </h1>
-      <div id="code" class="layout horizontal" slot="content">
-        <div id="qrCodeWrapper">
-          <canvas id="qrCodeCanvas" width="[[canvasSize_]]"
-              height="[[canvasSize_]]">
+          <template is="dom-if" if="[[isEq_(item.shape, shapes_.SQUARE)]]">
+            <svg width="100" height="100" viewBox="0 0 100 100" fill="none"
+                xmlns="http://www.w3.org/2000/svg">
+              <path fill="#fff" d="M0 0h100v100H0z">
+              </path>
+              <rect x="4" y="4" width="92" height="92" rx="8"
+                fill="currentColor">
+              </rect>
+            </svg>
+          </template>
         </div>
+        <span>[[item.digit]]</span>
       </div>
-      <div slot="bottom-buttons">
-        <oobe-next-button on-click="onNextClicked_"></oobe-next-button>
-      </div>
-    </oobe-adaptive-dialog>
-    <oobe-adaptive-dialog for-step="figures">
-      <h1 slot="title">
-        <!--TODO(https://crbug.com/1278686) Update with finalize strings -->
-        Verify the shapes
-      </h1>
-      <div id="figures" class="layout horizontal" slot="content">
-        <!-- We inline SVGs here to be able to control the color of them. -->
-        <template is="dom-repeat" items="[[figures_]]">
-          <div class="layout vertical center">
-            <div clr$="[[item.color]]">
-              <template is="dom-if" if="[[isEq_(item.shape, shapes_.CIRCLE)]]">
-                <svg width="100" height="100" viewBox="0 0 100 100" fill="none"
-                    xmlns="http://www.w3.org/2000/svg">
-                  <circle cx="50" cy="50" r="46" fill="currentColor">
-                  </circle>
-                </svg>
-              </template>
-
-              <template is="dom-if" if="[[isEq_(item.shape, shapes_.DIAMOND)]]">
-                <svg width="100" height="100" viewBox="0 0 100 100" fill="none"
-                    xmlns="http://www.w3.org/2000/svg">
-                  <path d="M45.793 5.746L5.749 45.789a5.96 5.96 0 0 0 0 8.429L45.793 94.26a5.96 5.96 0 0 0 8.428 0l40.044-40.043a5.96 5.96 0 0 0 0-8.429L54.22 5.746a5.96 5.96 0 0 0-8.428 0z" fill="currentColor">
-                  </path>
-                </svg>
-              </template>
-
-              <template is="dom-if"
-                  if="[[isEq_(item.shape, shapes_.TRIANGLE)]]">
-                <svg width="100" height="100" viewBox="0 0 100 100" fill="none"
-                    xmlns="http://www.w3.org/2000/svg">
-                  <g clip-path="url(#clip0_123_9159)">
-                    <path d="M50 95.97H11.773a7.778 7.778 0 0 1-6.7-3.879 7.763 7.763 0 0 1-.023-7.737L43.267 7.879a7.76 7.76 0 0 1 6.728-3.887 7.768 7.768 0 0 1 6.728 3.887L94.95 84.354a7.763 7.763 0 0 1-2.862 10.573 7.778 7.778 0 0 1-3.871 1.043H50z" fill="currentColor">
-                    </path>
-                  </g>
-                  <defs>
-                    <clipPath id="clip0_123_9159">
-                      <path fill="#fff" transform="translate(4 4)" d="M0 0h92v92H0z">
-                      </path>
-                    </clipPath>
-                  </defs>
-                </svg>
-              </template>
-
-              <template is="dom-if" if="[[isEq_(item.shape, shapes_.SQUARE)]]">
-                <svg width="100" height="100" viewBox="0 0 100 100" fill="none"
-                    xmlns="http://www.w3.org/2000/svg">
-                  <path fill="#fff" d="M0 0h100v100H0z">
-                  </path>
-                  <rect x="4" y="4" width="92" height="92" rx="8"
-                    fill="currentColor">
-                  </rect>
-                </svg>
-              </template>
-            </div>
-            <span>[[item.digit]]</span>
-          </div>
-        </template>
-      </div>
-      <div slot="bottom-buttons">
-        <oobe-next-button on-click="onNextClicked_"></oobe-next-button>
-      </div>
-    </oobe-adaptive-dialog>
-  </template>
-  <script src="quick_start.js"></script>
-</dom-module>
+    </template>
+  </div>
+  <div slot="bottom-buttons">
+    <oobe-next-button on-click="onNextClicked_"></oobe-next-button>
+  </div>
+</oobe-adaptive-dialog>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.js b/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.js
index 76c6d00..e4c3825 100644
--- a/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.js
+++ b/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.js
@@ -6,7 +6,17 @@
  * @fileoverview Polymer element for displaying quick start screen.
  */
 
-/* #js_imports_placeholder */
+import '//resources/polymer/v3_0/paper-styles/color.js';
+import '../../components/common_styles/common_styles.m.js';
+import '../../components/dialogs/oobe_loading_dialog.m.js';
+
+import {afterNextRender, dom, flush, html, mixinBehaviors, Polymer, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {LoginScreenBehavior, LoginScreenBehaviorInterface} from '../../components/behaviors/login_screen_behavior.m.js';
+import {MultiStepBehavior, MultiStepBehaviorInterface} from '../../components/behaviors/multi_step_behavior.m.js';
+import {OobeAdaptiveDialog} from '../../components/dialogs/oobe_adaptive_dialog.js';
+import {OobeTypes} from '../../components/oobe_types.m.js';
+
 
 /**
  * UI mode for the screen.
@@ -35,15 +45,17 @@
  * @implements {LoginScreenBehaviorInterface}
  * @implements {MultiStepBehaviorInterface}
  */
-const QuickStartScreenBase = Polymer.mixinBehaviors(
-    [LoginScreenBehavior, MultiStepBehavior], Polymer.Element);
+const QuickStartScreenBase =
+    mixinBehaviors([LoginScreenBehavior, MultiStepBehavior], PolymerElement);
 
 class QuickStartScreen extends QuickStartScreenBase {
   static get is() {
     return 'quick-start-element';
   }
 
-  /* #html_template_placeholder */
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
   static get properties() {
     return {
@@ -101,7 +113,7 @@
     this.setUIStep(QuickStartUIState.VERIFICATION);
 
     this.canvasSize_ = qrSize * QR_CODE_TILE_SIZE;
-    Polymer.dom.flush();
+    flush();
     const context = this.getCanvasContext_();
     context.clearRect(0, 0, this.canvasSize_, this.canvasSize_);
     context.fillStyle = QR_CODE_FILL_STYLE;
diff --git a/chrome/browser/resources/chromeos/sounds/power/charge_high.wav b/chrome/browser/resources/chromeos/sounds/power/charge_high.wav
deleted file mode 100644
index 49cbb35..0000000
--- a/chrome/browser/resources/chromeos/sounds/power/charge_high.wav
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/chromeos/sounds/power/charge_low.wav b/chrome/browser/resources/chromeos/sounds/power/charge_low.wav
deleted file mode 100644
index 73d3c71..0000000
--- a/chrome/browser/resources/chromeos/sounds/power/charge_low.wav
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/chromeos/sounds/power/charge_medium.wav b/chrome/browser/resources/chromeos/sounds/power/charge_medium.wav
deleted file mode 100644
index 0a8456a..0000000
--- a/chrome/browser/resources/chromeos/sounds/power/charge_medium.wav
+++ /dev/null
Binary files differ
diff --git a/chrome/browser/resources/chromeos/sounds/power/charging_started_high.wav b/chrome/browser/resources/chromeos/sounds/power/charging_started_high.wav
new file mode 100644
index 0000000..a7c2b91
--- /dev/null
+++ b/chrome/browser/resources/chromeos/sounds/power/charging_started_high.wav
Binary files differ
diff --git a/chrome/browser/resources/chromeos/sounds/power/charging_started_low.wav b/chrome/browser/resources/chromeos/sounds/power/charging_started_low.wav
new file mode 100644
index 0000000..e53e91d
--- /dev/null
+++ b/chrome/browser/resources/chromeos/sounds/power/charging_started_low.wav
Binary files differ
diff --git a/chrome/browser/resources/chromeos/sounds/power/charging_started_medium.wav b/chrome/browser/resources/chromeos/sounds/power/charging_started_medium.wav
new file mode 100644
index 0000000..3d6a01e
--- /dev/null
+++ b/chrome/browser/resources/chromeos/sounds/power/charging_started_medium.wav
Binary files differ
diff --git a/chrome/browser/resources/chromeos/sounds/power/low_battery.wav b/chrome/browser/resources/chromeos/sounds/power/low_battery.wav
index c470afe..ed05808 100644
--- a/chrome/browser/resources/chromeos/sounds/power/low_battery.wav
+++ b/chrome/browser/resources/chromeos/sounds/power/low_battery.wav
Binary files differ
diff --git a/chrome/browser/resources/pdf/pdf_viewer.ts b/chrome/browser/resources/pdf/pdf_viewer.ts
index 8d4b457..cedb526 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.ts
+++ b/chrome/browser/resources/pdf/pdf_viewer.ts
@@ -287,6 +287,10 @@
     return BACKGROUND_COLOR;
   }
 
+  setPluginSrc(plugin: HTMLEmbedElement) {
+    plugin.src = this.browserApi!.getStreamInfo().streamUrl;
+  }
+
   init(browserApi: BrowserApi) {
     this.initInternal(
         browserApi, this.$.scroller, this.$.sizer, this.$.content);
diff --git a/chrome/browser/resources/pdf/pdf_viewer_base.ts b/chrome/browser/resources/pdf/pdf_viewer_base.ts
index c7256eb..4a63be63 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_base.ts
+++ b/chrome/browser/resources/pdf/pdf_viewer_base.ts
@@ -73,6 +73,8 @@
 
   protected abstract afterZoom(viewportZoom: number): void;
 
+  protected abstract setPluginSrc(plugin: HTMLEmbedElement): void;
+
   /** Whether to enable the new UI. */
   protected isNewUiEnabled(): boolean {
     return true;
@@ -96,7 +98,7 @@
     plugin.type = 'application/x-google-chrome-pdf';
 
     plugin.setAttribute('original-url', this.originalUrl);
-    plugin.setAttribute('src', this.browserApi!.getStreamInfo().streamUrl);
+    this.setPluginSrc(plugin);
 
     plugin.setAttribute(
         'background-color', this.getBackgroundColor().toString());
diff --git a/chrome/browser/resources/pdf/pdf_viewer_pp.ts b/chrome/browser/resources/pdf/pdf_viewer_pp.ts
index df1a989..8b67c36 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_pp.ts
+++ b/chrome/browser/resources/pdf/pdf_viewer_pp.ts
@@ -8,7 +8,7 @@
 import './elements/viewer-zoom-toolbar.js';
 import './pdf_viewer_shared_style.css.js';
 
-import {assertNotReached} from 'chrome://resources/js/assert_ts.js';
+import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
 import {isRTL} from 'chrome://resources/js/util_ts.js';
 
 import {BrowserApi} from './browser_api.js';
@@ -22,6 +22,8 @@
 import {DestinationMessageData, DocumentDimensionsMessageData, hasCtrlModifier, shouldIgnoreKeyEvents} from './pdf_viewer_utils.js';
 import {ToolbarManager} from './toolbar_manager.js';
 
+let pluginLoaderPolicy: TrustedTypePolicy|null = null;
+
 export interface PdfViewerPpElement {
   $: {
     content: HTMLElement,
@@ -54,6 +56,39 @@
     return PRINT_PREVIEW_BACKGROUND_COLOR;
   }
 
+  private getStreamUrl_(): TrustedScriptURL {
+    if (pluginLoaderPolicy === null) {
+      pluginLoaderPolicy =
+          window.trustedTypes!.createPolicy('print-preview-plugin-loader', {
+            createScriptURL: (_ignore: string) => {
+              const url = new URL(this.browserApi!.getStreamInfo().streamUrl);
+
+              // Checks based on data_request_filter.cc.
+              assert(url.origin === 'chrome-untrusted://print');
+              if (url.pathname.endsWith('test.pdf')) {
+                return url.toString();
+              }
+
+              const paths = url.pathname.split('/');
+              assert(paths.length === 4);
+              assert(paths[3] === 'print.pdf');
+              // Valid Print Preview UI ID
+              assert(!Number.isNaN(parseInt(paths[1])));
+              // Valid page index (can be negative for PDFs).
+              assert(!Number.isNaN(parseInt(paths[2])));
+              return url.toString();
+            },
+            createHTML: () => assertNotReached(),
+            createScript: () => assertNotReached(),
+          });
+    }
+    return pluginLoaderPolicy.createScriptURL('');
+  }
+
+  setPluginSrc(plugin: HTMLEmbedElement) {
+    plugin.src = this.getStreamUrl_() as unknown as string;
+  }
+
   init(browserApi: BrowserApi) {
     this.initInternal(
         browserApi, document.documentElement, this.$.sizer, this.$.content);
diff --git a/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.html b/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.html
index 5db29fc3..2fa3a88 100644
--- a/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.html
+++ b/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.html
@@ -130,7 +130,7 @@
 <div id="destinationDropdown" on-keydown="onKeyDown_" tabindex="0"
     on-blur="onBlur_" on-click="onClick_" role="button" aria-haspopup="true"
     aria-label="$i18n{destinationLabel} [[value.displayName]]"
-    aria-description$="[[destinationStatusText]]">
+    aria-description$="[[getAriaDescription_(destinationStatusText)]]">
   <div id="destination-display-container">
     <div id="destination-icon-box">
       <iron-icon icon="[[destinationIcon]]"></iron-icon>
diff --git a/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.ts b/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.ts
index 90cb71af..7c3cbdb 100644
--- a/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.ts
@@ -95,7 +95,7 @@
   noDestinations: boolean;
   pdfDestinationKey: string;
   pdfPrinterDisabled: boolean;
-  destinationStatusText: string;
+  destinationStatusText: TrustedHTML;
   private isDarkModeActive_: boolean;
   private highlightedIndex_: number;
   private dropdownLength_: number;
@@ -119,6 +119,10 @@
     this.$.destinationDropdown.focus();
   }
 
+  private getAriaDescription_(): string {
+    return this.destinationStatusText.toString();
+  }
+
   private fireDropdownValueSelected_(element: Element) {
     this.dispatchEvent(new CustomEvent(
         'dropdown-value-selected',
diff --git a/chrome/browser/resources/print_preview/ui/destination_select_cros.html b/chrome/browser/resources/print_preview/ui/destination_select_cros.html
index 3337f38c..44cb7a3 100644
--- a/chrome/browser/resources/print_preview/ui/destination_select_cros.html
+++ b/chrome/browser/resources/print_preview/ui/destination_select_cros.html
@@ -48,11 +48,12 @@
   </div>
 </print-preview-settings-section>
 <print-preview-settings-section class="destination-additional-info"
-    hidden$="[[!statusText_]]">
+    hidden$="[[hideDestinationAdditionalInfo_(statusText_)]]">
   <div slot="title"></div>
   <div slot="controls">
     <div id="statusText" class="destination-status" title="[[statusText_]]"
-        aria-hidden="[[isCurrentDestinationCrosLocal_]]">
+        aria-hidden="[[isCurrentDestinationCrosLocal_]]"
+        inner-h-t-m-l="[[statusText_]]">
     </div>
   </div>
 </print-preview-settings-section>
diff --git a/chrome/browser/resources/print_preview/ui/destination_select_cros.ts b/chrome/browser/resources/print_preview/ui/destination_select_cros.ts
index b48050b..bd7e213 100644
--- a/chrome/browser/resources/print_preview/ui/destination_select_cros.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_select_cros.ts
@@ -78,7 +78,6 @@
         type: String,
         computed:
             'computeStatusText_(destination, destination.printerStatusReason)',
-        observer: 'onStatusTextSet_',
       },
 
       destinationIcon_: {
@@ -105,7 +104,7 @@
   pdfPrinterDisabled: boolean;
   recentDestinationList: Destination[];
   private pdfDestinationKey_: string;
-  private statusText_: string;
+  private statusText_: TrustedHTML;
   private destinationIcon_: string;
   private isCurrentDestinationCrosLocal_: boolean;
   private isDarkModeActive_: boolean;
@@ -167,6 +166,10 @@
     return 'print-preview:print';
   }
 
+  private hideDestinationAdditionalInfo_(): boolean {
+    return this.statusText_ === window.trustedTypes!.emptyHTML;
+  }
+
   private fireSelectedOptionChange_(value: string) {
     this.dispatchEvent(new CustomEvent(
         'selected-option-change',
@@ -228,34 +231,32 @@
    * @return An error status for the current destination. If no error
    *     status exists, an empty string.
    */
-  private computeStatusText_(): string {
+  private computeStatusText_(): TrustedHTML {
     // |destination| can be either undefined, or null here.
     if (!this.destination) {
-      return '';
+      return window.trustedTypes!.emptyHTML;
     }
 
     // Non-local printers do not show an error status.
     if (this.destination.origin !== DestinationOrigin.CROS) {
-      return '';
+      return window.trustedTypes!.emptyHTML;
     }
 
     const printerStatusReason = this.destination.printerStatusReason;
     if (printerStatusReason === null ||
         printerStatusReason === PrinterStatusReason.NO_ERROR ||
         printerStatusReason === PrinterStatusReason.UNKNOWN_REASON) {
-      return '';
+      return window.trustedTypes!.emptyHTML;
     }
 
     return this.getErrorString_(printerStatusReason);
   }
 
-  private onStatusTextSet_() {
-    this.shadowRoot!.querySelector('#statusText')!.innerHTML = this.statusText_;
-  }
-
-  private getErrorString_(printerStatusReason: PrinterStatusReason): string {
+  private getErrorString_(printerStatusReason: PrinterStatusReason):
+      TrustedHTML {
     const errorStringKey = ERROR_STRING_KEY_MAP.get(printerStatusReason);
-    return errorStringKey ? this.i18n(errorStringKey) : '';
+    return errorStringKey ? this.i18nAdvanced(errorStringKey) :
+                            window.trustedTypes!.emptyHTML;
   }
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/select_to_speak_subpage.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/select_to_speak_subpage.html
index 9aef70b9..11a4921 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/select_to_speak_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/select_to_speak_subpage.html
@@ -7,6 +7,7 @@
 <style include="settings-shared">
   :host {
     --cr-section-indent-width: 60px;
+    --focus-ring-highlight-color: rgb(218, 54, 232);
   }
 
   h2 {
@@ -68,6 +69,43 @@
     width: 100%;
     z-index: 10;
   }
+
+  .background-preview {
+    background-color: RGBA(0, 0, 0, .4);
+    border-radius: 3px;
+    line-height: 1.5;
+    margin-inline-end: var(--cr-section-padding);
+    padding: 16px 24px 16px 24px;
+    position: relative;
+  }
+
+  .background-preview-selected {
+    background-color: white;
+    border-radius: 6px;
+    box-shadow: 0 0 4px 4px var(--focus-ring-highlight-color);
+    color: black;
+    display: inline-block;
+    margin: 0;
+    padding: 6px 4px 6px 4px;
+  }
+
+  .background-preview-unselected {
+    margin: 2px 0 2px 0;
+    padding: 0 4px 0 4px;
+  }
+
+  @media (prefers-color-scheme: dark) {
+    .background-preview-selected {
+      background-color: var(--google-grey-900);
+      color: var(--cros-text-color-primary);
+    }
+
+    .background-preview-unselected {
+      color: var(--cros-text-color-secondary);
+      margin: 2px 0 2px 0;
+      padding: 0 4px 0 4px;
+    }
+  }
 </style>
 
 <h2>$i18n{selectToSpeakOptionsHighlight}</h2>
@@ -108,6 +146,17 @@
       </div>
     </div>
   </template>
+  <div class="background-preview" aria-hidden=true>
+    <p class="background-preview-unselected">
+      $i18n{selectToSpeakOptionsSampleText}
+    </p>
+    <p class="background-preview-selected">
+      $i18n{selectToSpeakOptionsSampleText}
+    </p>
+    <p class="background-preview-unselected">
+      $i18n{selectToSpeakOptionsSampleText}
+    </p>
+  </div>
   <settings-toggle-button
       id="backgroundShadingToggle"
       class="settings-box continuation"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 5c0ec06..c9164ad 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -518,6 +518,7 @@
     "//components/live_caption:utils",
     "//components/local_state",
     "//components/lookalikes/core",
+    "//components/metrics/debug",
     "//components/metrics/debug:resources",
     "//components/metrics_services_manager",
     "//components/navigation_metrics",
@@ -3158,6 +3159,7 @@
       "//chrome/browser/ash/crostini:crostini_installer_types_mojom",
       "//chrome/browser/ash/login/oobe_quick_start",
       "//chrome/browser/ash/system_web_apps",
+      "//chrome/browser/ash/video_conference",
       "//chrome/browser/chromeos",
       "//chrome/browser/chromeos/extensions/vpn_provider",
       "//chrome/browser/chromeos/launcher_search:search_util",
diff --git a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java
index 33d40835..6ae28d8 100644
--- a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java
+++ b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java
@@ -104,8 +104,7 @@
 
     @IntDef({NativePageType.NONE, NativePageType.CANDIDATE, NativePageType.NTP,
             NativePageType.BOOKMARKS, NativePageType.RECENT_TABS, NativePageType.DOWNLOADS,
-            NativePageType.HISTORY, NativePageType.EXPLORE, NativePageType.LAUNCHPAD,
-            NativePageType.MANAGEMENT})
+            NativePageType.HISTORY, NativePageType.EXPLORE, NativePageType.MANAGEMENT})
     @Retention(RetentionPolicy.SOURCE)
     public @interface NativePageType {
         int NONE = 0;
@@ -116,8 +115,7 @@
         int DOWNLOADS = 5;
         int HISTORY = 6;
         int EXPLORE = 7;
-        int LAUNCHPAD = 8;
-        int MANAGEMENT = 9;
+        int MANAGEMENT = 8;
     }
 
     /**
@@ -172,8 +170,6 @@
             return NativePageType.RECENT_TABS;
         } else if (UrlConstants.EXPLORE_HOST.equals(host)) {
             return NativePageType.EXPLORE;
-        } else if (UrlConstants.LAUNCHPAD_HOST.equals(host)) {
-            return NativePageType.LAUNCHPAD;
         } else if (UrlConstants.MANAGEMENT_HOST.equals(host)) {
             return NativePageType.MANAGEMENT;
         } else {
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index ddf61ee..307d5f3 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -4518,15 +4518,6 @@
       <message name="IDS_WEBAPK_NETWORK_ERROR_MESSAGE_TUNNEL_CONNECTION_FAILED" desc="The error message for ERROR_TUNNEL_CONNECTION_FAILED.">
         Establishing a tunnel via proxy server failed
       </message>
-      <message name="IDS_LAUNCHPAD_TITLE" desc="Title of WebAPKs launchpad page">
-        Apps
-      </message>
-      <message name="IDS_LAUNCHPAD_MENU_UNINSTALL" desc="Text that displays on the Launchpad app management menu that allows user to open the setting to uninstall the app. ">
-        Uninstall
-      </message>
-      <message name="IDS_LAUNCHPAD_MENU_SITE_SETTINGS" desc="Text that displays on the Launchpad app management menu that allows the user to open the site setting page.">
-        Site settings
-      </message>
 
       <!-- Keyboard shortcuts in Android N-->
       <message name="IDS_KEYBOARD_SHORTCUT_OPEN_NEW_TAB" desc="A text label that appears next to the keyboard shortcut to open a new tab in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR_LIMIT=55]">
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LAUNCHPAD_MENU_SITE_SETTINGS.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LAUNCHPAD_MENU_SITE_SETTINGS.png.sha1
deleted file mode 100644
index 8fd16d41..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LAUNCHPAD_MENU_SITE_SETTINGS.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-76f8a6644d33691a8d1c6594e35489308351f71d
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LAUNCHPAD_MENU_UNINSTALL.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LAUNCHPAD_MENU_UNINSTALL.png.sha1
deleted file mode 100644
index 8fd16d41..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LAUNCHPAD_MENU_UNINSTALL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-76f8a6644d33691a8d1c6594e35489308351f71d
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LAUNCHPAD_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LAUNCHPAD_TITLE.png.sha1
deleted file mode 100644
index 09f6ca0..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LAUNCHPAD_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ab4b5e8c96b3f12734c31d7c53cddbdf1bfb0249
\ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarFeatures.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarFeatures.java
index 2ef718f..65b0c01a 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarFeatures.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarFeatures.java
@@ -18,8 +18,6 @@
     private static final MutableFlagWithSafeDefault sSuppressionFlag =
             new MutableFlagWithSafeDefault(ChromeFeatureList.SUPPRESS_TOOLBAR_CAPTURES, false);
 
-    private static Boolean sSuppressionEnabled;
-
     /** Private constructor to avoid instantiation. */
     private ToolbarFeatures() {}
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index f5d22af..577df894b 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -405,13 +405,6 @@
     }
 
     /**
-     * @return The helper for menu button UI interactions.
-     */
-    AppMenuButtonHelper getMenuButtonHelper() {
-        return mAppMenuButtonHelper;
-    }
-
-    /**
      * @return Whether or not the native library is loaded and ready.
      */
     boolean isNativeLibraryReady() {
@@ -737,37 +730,6 @@
     protected void onNavigatedToDifferentPage() {}
 
     /**
-     * Starts load progress.
-     */
-    void startLoadProgress() {
-        mProgressBar.start();
-    }
-
-    /**
-     * Sets load progress.
-     * @param progress The load progress between 0 and 1.
-     */
-    void setLoadProgress(float progress) {
-        mProgressBar.setProgress(progress);
-    }
-
-    /**
-     * Finishes load progress.
-     * @param delayed Whether hiding progress bar should be delayed to give enough time for user to
-     *                        recognize the last state.
-     */
-    void finishLoadProgress(boolean delayed) {
-        mProgressBar.finish(delayed);
-    }
-
-    /**
-     * @return True if the progress bar is started.
-     */
-    boolean isProgressStarted() {
-        return mProgressBar.isStarted();
-    }
-
-    /**
      * Finish any toolbar animations.
      */
     void finishAnimations() {}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotState.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotState.java
index 7b677a58..7ece667c 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotState.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotState.java
@@ -122,7 +122,10 @@
             return ToolbarSnapshotDifference.LOCATION_BAR_WIDTH;
         } else if (!isVisibleUrlTextSame(that)) {
             return ToolbarSnapshotDifference.URL_TEXT;
-        } else if (!Objects.equals(mColorStateList, that.mColorStateList)) {
+        } else if (mColorStateList.getDefaultColor() != that.mColorStateList.getDefaultColor()) {
+            // While there's more to the ColorStateList than just the default color, there's no
+            // great way to check for equality. Currently default colors should be sufficient for
+            // detecting changes to the toolbar.
             return ToolbarSnapshotDifference.HOME_BUTTON_COLOR;
         }
         return ToolbarSnapshotDifference.NONE;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotStateTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotStateTest.java
index 91d8e25..992dd8c 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotStateTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotStateTest.java
@@ -172,6 +172,20 @@
     }
 
     @Test
+    public void testSameColorStateList() {
+        // Create the ColorStateList by hand. ColorStateList.valueOf will inconsistently reuse
+        // objects, but this ColorStateList should never have reference equality with the default.
+        ColorStateList colorStateList =
+                new ColorStateList(new int[][] {new int[] {}}, new int[] {DEFAULT_TINT});
+        Assert.assertNotEquals(mDefaultColorStateList, colorStateList);
+
+        ToolbarSnapshotState otherToolbarSnapshotState =
+                new ToolbarSnapshotStateBuilder().setColorStateList(colorStateList).build();
+        Assert.assertEquals(ToolbarSnapshotDifference.NONE,
+                otherToolbarSnapshotState.getAnyDifference(mDefaultToolbarSnapshotState));
+    }
+
+    @Test
     public void testDifferentIsShowingUpdateBadgeDuringLastCapture() {
         ToolbarSnapshotState otherToolbarSnapshotState =
                 new ToolbarSnapshotStateBuilder()
diff --git a/chrome/browser/ui/app_list/search/types.cc b/chrome/browser/ui/app_list/search/types.cc
index 85c79b0..5440087 100644
--- a/chrome/browser/ui/app_list/search/types.cc
+++ b/chrome/browser/ui/app_list/search/types.cc
@@ -11,7 +11,7 @@
 namespace app_list {
 
 double Scoring::FinalScore() const {
-  if (filter)
+  if (filter && !override_filter_for_test)
     return -1.0;
   return ftrl_result_score;
 }
diff --git a/chrome/browser/ui/app_list/search/types.h b/chrome/browser/ui/app_list/search/types.h
index 7c01e9168..de666cf 100644
--- a/chrome/browser/ui/app_list/search/types.h
+++ b/chrome/browser/ui/app_list/search/types.h
@@ -34,6 +34,11 @@
   double normalized_relevance = 0.0;
   double mrfu_result_score = 0.0;
   double ftrl_result_score = 0.0;
+  // TODO(b/259607603) remove 'override_filter_for_test'. This field is used
+  // to temporarily disable filtering for a specific result. This is needed
+  // due to a race condition with the test beginning before the
+  // RemovedResultsRanker is initialized.
+  bool override_filter_for_test = false;
 
   // Used only for results in the Continue section. Continue results are first
   // ordered by |continue_rank|, and then by their display score. -1 indicates
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 02438eb..7aa7aae 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -12,6 +12,7 @@
 #include "ash/public/cpp/window_properties.h"
 #include "ash/quick_pair/keyed_service/quick_pair_mediator.h"
 #include "ash/shell.h"
+#include "ash/system/video_conference/fake_video_conference_tray_controller.h"
 #include "base/command_line.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
@@ -23,6 +24,7 @@
 #include "chrome/browser/ash/policy/display/display_settings_handler.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/sync/sync_error_notifier_factory.h"
+#include "chrome/browser/ash/video_conference/video_conference_tray_controller_impl.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/tablet_mode/tablet_mode_page_behavior.h"
@@ -62,6 +64,7 @@
 #include "chrome/browser/ui/views/select_file_dialog_extension.h"
 #include "chrome/browser/ui/views/select_file_dialog_extension_factory.h"
 #include "chrome/browser/ui/views/tabs/tab_scrubber_chromeos.h"
+#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/network/network_connect.h"
 #include "chromeos/ash/components/network/portal_detector/network_portal_detector.h"
 #include "chromeos/ash/services/bluetooth_config/fast_pair_delegate.h"
@@ -236,6 +239,23 @@
 
   desks_client_ = std::make_unique<DesksClient>();
 
+  // This controller MUST be initialized before the UI is constructed. The
+  // video conferencing views will have their own reference to this controller,
+  // may be observers of it, and will assume it exists for as long as they
+  // themselves exist.
+  if (ash::features::IsVcControlsUiEnabled()) {
+    // `VideoConferenceTrayController` relies on audio and camera services to
+    // function properly, so we will use the fake version when system bus is not
+    // available so that this works on linux-chromeos and unit tests.
+    if (ash::DBusThreadManager::Get()->GetSystemBus()) {
+      video_conference_tray_controller_ =
+          std::make_unique<ash::VideoConferenceTrayControllerImpl>();
+    } else {
+      video_conference_tray_controller_ =
+          std::make_unique<ash::FakeVideoConferenceTrayController>();
+    }
+  }
+
   ash::bluetooth_config::FastPairDelegate* delegate =
       ash::features::IsFastPairEnabled()
           ? ash::Shell::Get()->quick_pair_mediator()->GetFastPairDelegate()
@@ -317,6 +337,9 @@
   exo_parts_.reset();
 #endif
 
+  if (ash::features::IsVcControlsUiEnabled())
+    video_conference_tray_controller_.reset();
+
   night_light_client_.reset();
   mobile_data_notifications_.reset();
   chrome_shelf_controller_initializer_.reset();
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
index 7e0c3b80..5c593e05 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
@@ -15,6 +15,7 @@
 class NetworkPortalNotificationController;
 class NewWindowDelegateProvider;
 class NightLightClient;
+class VideoConferenceTrayController;
 }  // namespace ash
 
 namespace game_mode {
@@ -117,6 +118,8 @@
   std::unique_ptr<game_mode::GameModeController> game_mode_controller_;
   std::unique_ptr<ash::NetworkPortalNotificationController>
       network_portal_notification_controller_;
+  std::unique_ptr<ash::VideoConferenceTrayController>
+      video_conference_tray_controller_;
 
   std::unique_ptr<internal::ChromeShelfControllerInitializer>
       chrome_shelf_controller_initializer_;
diff --git a/chrome/browser/ui/commander/open_url_command_source.cc b/chrome/browser/ui/commander/open_url_command_source.cc
index c29adb7..3d446d99 100644
--- a/chrome/browser/ui/commander/open_url_command_source.cc
+++ b/chrome/browser/ui/commander/open_url_command_source.cc
@@ -7,6 +7,7 @@
 #include "base/i18n/case_conversion.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/branding_buildflags.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/commander/fuzzy_finder.h"
 #include "chrome/grit/locale_settings.h"
diff --git a/chrome/browser/ui/commander/simple_command_source.cc b/chrome/browser/ui/commander/simple_command_source.cc
index e0a6430..614afe3 100644
--- a/chrome/browser/ui/commander/simple_command_source.cc
+++ b/chrome/browser/ui/commander/simple_command_source.cc
@@ -9,6 +9,7 @@
 #include "base/i18n/case_conversion.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/ui/accelerator_utils.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/commander/fuzzy_finder.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/dialogs/outdated_upgrade_bubble.cc b/chrome/browser/ui/dialogs/outdated_upgrade_bubble.cc
index 8f5afec..598436e3 100644
--- a/chrome/browser/ui/dialogs/outdated_upgrade_bubble.cc
+++ b/chrome/browser/ui/dialogs/outdated_upgrade_bubble.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ui/dialogs/outdated_upgrade_bubble.h"
 
 #include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/strcat.h"
 #include "base/task/thread_pool.h"
@@ -32,12 +31,6 @@
 
 namespace {
 
-// The maximum number of ignored bubble we track in the NumLaterPerReinstall
-// histogram.
-constexpr int kMaxIgnored = 50;
-// The number of buckets we want the NumLaterPerReinstall histogram to use.
-constexpr int kNumIgnoredBuckets = 5;
-
 // For ChromeOS Lacros, browser updates are done via system services, thus
 // we redirect to the safetyCheck page that interacts with these. On other
 // platforms it may be possible to download an updated browser via a site.
@@ -54,29 +47,15 @@
 
 bool g_upgrade_bubble_is_showing = false;
 
-// The number of times the user ignored the bubble before finally choosing to
-// reinstall.
-int g_num_ignored_bubbles = 0;
-
 void OnWindowClosing() {
   g_upgrade_bubble_is_showing = false;
-
-  // Increment the ignored bubble count (if this bubble wasn't ignored, this
-  // increment is offset by a decrement in OnDialogAccepted()).
-  if (g_num_ignored_bubbles < kMaxIgnored)
-    ++g_num_ignored_bubbles;
 }
 
 void OnDialogAccepted(content::PageNavigator* navigator,
                       bool auto_update_enabled,
                       const char* update_browser_redirect_url) {
-  // Offset the +1 in OnWindowClosing().
-  --g_num_ignored_bubbles;
   if (auto_update_enabled) {
     DCHECK(UpgradeDetector::GetInstance()->is_outdated_install());
-    UMA_HISTOGRAM_CUSTOM_COUNTS("OutdatedUpgradeBubble.NumLaterPerReinstall",
-                                g_num_ignored_bubbles, 1, kMaxIgnored,
-                                kNumIgnoredBuckets);
     base::RecordAction(
         base::UserMetricsAction("OutdatedUpgradeBubble.Reinstall"));
 
@@ -87,9 +66,6 @@
 #if BUILDFLAG(IS_WIN)
   } else {
     DCHECK(UpgradeDetector::GetInstance()->is_outdated_install_no_au());
-    UMA_HISTOGRAM_CUSTOM_COUNTS("OutdatedUpgradeBubble.NumLaterPerEnableAU",
-                                g_num_ignored_bubbles, 1, kMaxIgnored,
-                                kNumIgnoredBuckets);
     base::RecordAction(
         base::UserMetricsAction("OutdatedUpgradeBubble.EnableAU"));
     // Record that the autoupdate flavour of the dialog has been shown.
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
index 3679dcb0..9d20031 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
@@ -202,11 +202,6 @@
     event_waiter_->OnEvent(DialogEvent::SHIPPING_OPTION_SECTION_OPENED);
 }
 
-void PaymentRequestBrowserTestBase::OnCreditCardEditorOpened() {
-  if (event_waiter_)
-    event_waiter_->OnEvent(DialogEvent::CREDIT_CARD_EDITOR_OPENED);
-}
-
 void PaymentRequestBrowserTestBase::OnShippingAddressEditorOpened() {
   if (event_waiter_)
     event_waiter_->OnEvent(DialogEvent::SHIPPING_ADDRESS_EDITOR_OPENED);
@@ -247,11 +242,6 @@
     event_waiter_->OnEvent(DialogEvent::SPEC_DONE_UPDATING);
 }
 
-void PaymentRequestBrowserTestBase::OnCvcPromptShown() {
-  if (event_waiter_)
-    event_waiter_->OnEvent(DialogEvent::CVC_PROMPT_SHOWN);
-}
-
 void PaymentRequestBrowserTestBase::OnProcessingSpinnerShown() {
   if (event_waiter_)
     event_waiter_->OnEvent(DialogEvent::PROCESSING_SPINNER_SHOWN);
@@ -665,11 +655,6 @@
 }
 
 void PaymentRequestBrowserTestBase::OpenCVCPromptWithCVC(
-    const std::u16string& cvc) {
-  OpenCVCPromptWithCVC(cvc, delegate_->dialog_view());
-}
-
-void PaymentRequestBrowserTestBase::OpenCVCPromptWithCVC(
     const std::u16string& cvc,
     PaymentRequestDialogView* dialog_view) {
   ResetEventWaiter(DialogEvent::CVC_PROMPT_SHOWN);
@@ -681,22 +666,6 @@
   cvc_field->InsertOrReplaceText(cvc);
 }
 
-void PaymentRequestBrowserTestBase::PayWithCreditCardAndWait(
-    const std::u16string& cvc) {
-  PayWithCreditCardAndWait(cvc, delegate_->dialog_view());
-}
-
-void PaymentRequestBrowserTestBase::PayWithCreditCardAndWait(
-    const std::u16string& cvc,
-    PaymentRequestDialogView* dialog_view) {
-  OpenCVCPromptWithCVC(cvc, dialog_view);
-
-  ResetEventWaiterForSequence(
-      {DialogEvent::PROCESSING_SPINNER_SHOWN, DialogEvent::DIALOG_CLOSED});
-  ClickOnDialogViewAndWait(DialogViewID::CVC_PROMPT_CONFIRM_BUTTON,
-                           dialog_view);
-}
-
 void PaymentRequestBrowserTestBase::PayWithCreditCard(
     const std::u16string& cvc) {
   OpenCVCPromptWithCVC(cvc, delegate_->dialog_view());
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.h b/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
index 2c2bc43..f273202 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
@@ -133,7 +133,6 @@
   void OnPaymentMethodOpened() override;
   void OnShippingAddressSectionOpened() override;
   void OnShippingOptionSectionOpened() override;
-  void OnCreditCardEditorOpened() override;
   void OnShippingAddressEditorOpened() override;
   void OnContactInfoEditorOpened() override;
   void OnBackNavigation() override;
@@ -142,7 +141,6 @@
   void OnEditorViewUpdated() override;
   void OnErrorMessageShown() override;
   void OnSpecDoneUpdating() override;
-  void OnCvcPromptShown() override;
   void OnProcessingSpinnerShown() override;
   void OnProcessingSpinnerHidden() override;
   void OnPaymentHandlerWindowOpened() override;
@@ -218,13 +216,11 @@
   std::vector<std::u16string> GetShippingOptionLabelValues(
       DialogViewID parent_view_id);
 
-  void OpenCVCPromptWithCVC(const std::u16string& cvc);
+  // TODO(crbug.com/1209835): Remove remaining test usage and delete these.
   void OpenCVCPromptWithCVC(const std::u16string& cvc,
                             PaymentRequestDialogView* dialog_view);
-  void PayWithCreditCardAndWait(const std::u16string& cvc);
-  void PayWithCreditCardAndWait(const std::u16string& cvc,
-                                PaymentRequestDialogView* dialog_view);
   void PayWithCreditCard(const std::u16string& cvc);
+
   void RetryPaymentRequest(const std::string& validation_errors,
                            PaymentRequestDialogView* dialog_view);
   void RetryPaymentRequest(const std::string& validation_errors,
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.h b/chrome/browser/ui/views/payments/payment_request_dialog_view.h
index ff141f1..c1b6cbff 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view.h
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.h
@@ -68,8 +68,6 @@
 
     virtual void OnShippingOptionSectionOpened() = 0;
 
-    virtual void OnCreditCardEditorOpened() = 0;
-
     virtual void OnShippingAddressEditorOpened() = 0;
 
     virtual void OnContactInfoEditorOpened() = 0;
@@ -84,8 +82,6 @@
 
     virtual void OnSpecDoneUpdating() = 0;
 
-    virtual void OnCvcPromptShown() = 0;
-
     virtual void OnProcessingSpinnerShown() = 0;
 
     virtual void OnProcessingSpinnerHidden() = 0;
diff --git a/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc
index 353c99a..188f74c 100644
--- a/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc
@@ -8,6 +8,7 @@
 #include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
 #include "base/functional/callback_helpers.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/views/profiles/profile_management_step_controller.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac.cc
index f37b013..4144d136 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac.cc
@@ -376,5 +376,81 @@
   helper_.CheckWindowNotCreated();
 }
 
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerWindowed_118FileHandlerFoo_118FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kWindowed);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerBrowser_118FileHandlerFoo_118FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kBrowser);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
 }  // namespace
 }  // namespace web_app::integration_tests
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
index 1787af9..ffad6f58 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
@@ -64,29 +64,6 @@
   helper_.CheckSiteNotHandlesFile(Site::kStandalone, FileExtension::kBar);
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppIntegration, CheckLaunchFileExpectDialog) {
-  helper_.InstallMenuOption(InstallableSite::kFileHandler);
-  helper_.ClosePwa();
-  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
-                                 AllowDenyOptions::kAllow,
-                                 AskAgainOptions::kAskAgain);
-  helper_.CheckWindowCreated();
-}
-
-IN_PROC_BROWSER_TEST_F(WebAppIntegration, CheckLaunchFileExpectNoDialog_Allow) {
-  helper_.InstallOmniboxIcon(InstallableSite::kFileHandler);
-  helper_.ClosePwa();
-  // Open the file and set AskAgainOption to kRemember.
-  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
-                                 AllowDenyOptions::kAllow,
-                                 AskAgainOptions::kRemember);
-  helper_.ClosePwa();
-  // Open the file again.
-  helper_.LaunchFileExpectNoDialog(Site::kFileHandler,
-                                   FilesOptions::kOneFooFile);
-  helper_.CheckWindowCreated();
-}
-
 IN_PROC_BROWSER_TEST_F(WebAppIntegration, CheckLaunchFileExpectNoDialog_Deny) {
   helper_.InstallOmniboxIcon(InstallableSite::kFileHandler);
   helper_.ClosePwa();
@@ -1491,82 +1468,6 @@
 
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegration,
-    WAI_29FileHandlerBrowser_118FileHandlerFoo_118FileHandlerBar) {
-  // Test contents are generated by script. Please do not modify!
-  // See `docs/webapps/why-is-this-test-failing.md` or
-  // `docs/webapps/integration-testing-framework` for more info.
-  // Sheriffs: Disabling this test is supported.
-  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kBrowser);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    WebAppIntegration,
-    WAI_32FileHandlerWithShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar) {
-  // Test contents are generated by script. Please do not modify!
-  // See `docs/webapps/why-is-this-test-failing.md` or
-  // `docs/webapps/integration-testing-framework` for more info.
-  // Sheriffs: Disabling this test is supported.
-  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
-                           WindowOptions::kWindowed, InstallMode::kWebApp);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    WebAppIntegration,
-    WAI_32FileHandlerWithShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar) {
-  // Test contents are generated by script. Please do not modify!
-  // See `docs/webapps/why-is-this-test-failing.md` or
-  // `docs/webapps/integration-testing-framework` for more info.
-  // Sheriffs: Disabling this test is supported.
-  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
-                           WindowOptions::kBrowser, InstallMode::kWebApp);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    WebAppIntegration,
-    WAI_32FileHandlerNoShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar) {
-  // Test contents are generated by script. Please do not modify!
-  // See `docs/webapps/why-is-this-test-failing.md` or
-  // `docs/webapps/integration-testing-framework` for more info.
-  // Sheriffs: Disabling this test is supported.
-  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
-                           WindowOptions::kWindowed, InstallMode::kWebApp);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    WebAppIntegration,
-    WAI_32FileHandlerNoShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar) {
-  // Test contents are generated by script. Please do not modify!
-  // See `docs/webapps/why-is-this-test-failing.md` or
-  // `docs/webapps/integration-testing-framework` for more info.
-  // Sheriffs: Disabling this test is supported.
-  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
-                           WindowOptions::kBrowser, InstallMode::kWebApp);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    WebAppIntegration,
-    WAI_29FileHandlerWindowed_118FileHandlerFoo_118FileHandlerBar) {
-  // Test contents are generated by script. Please do not modify!
-  // See `docs/webapps/why-is-this-test-failing.md` or
-  // `docs/webapps/integration-testing-framework` for more info.
-  // Sheriffs: Disabling this test is supported.
-  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kWindowed);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
-  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    WebAppIntegration,
     WAI_32StandaloneNoShortcutWindowedWebApp_12Standalone_101Standalone_111Standalone) {
   // Test contents are generated by script. Please do not modify!
   // See `docs/webapps/why-is-this-test-failing.md` or
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_win_linux.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_win_linux.cc
index b7e2cfb..b4dcd7f 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_win_linux.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_win_linux.cc
@@ -11,6 +11,28 @@
 using WebAppIntegration = WebAppIntegrationTest;
 
 // Manual tests:
+IN_PROC_BROWSER_TEST_F(WebAppIntegration, CheckLaunchFileExpectDialog) {
+  helper_.InstallMenuOption(InstallableSite::kFileHandler);
+  helper_.ClosePwa();
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+  helper_.CheckWindowCreated();
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppIntegration, CheckLaunchFileExpectNoDialog_Allow) {
+  helper_.InstallOmniboxIcon(InstallableSite::kFileHandler);
+  helper_.ClosePwa();
+  // Open the file and set AskAgainOption to kRemember.
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kRemember);
+  helper_.CheckWindowCreated();
+  // Open the file again.
+  helper_.LaunchFileExpectNoDialog(Site::kFileHandler,
+                                   FilesOptions::kOneFooFile);
+  helper_.CheckWindowCreated();
+}
 
 // Generated tests:
 
@@ -319,5 +341,747 @@
   helper_.CheckWindowCreated();
 }
 
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerWindowed_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kWindowed);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerWindowed_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleFooFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kWindowed);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleFooFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerWindowed_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneBarFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kWindowed);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneBarFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerWindowed_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleBarFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kWindowed);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleBarFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerWindowed_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowRemember_121FileHandlerOneFooFile) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kWindowed);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kRemember);
+  helper_.LaunchFileExpectNoDialog(Site::kFileHandler,
+                                   FilesOptions::kOneFooFile);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerWindowed_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyAskAgain_127_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kWindowed);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kAskAgain);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerWindowed_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyRemember_127_122FileHandlerFoo_122FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kWindowed);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kRemember);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerBrowser_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kBrowser);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerBrowser_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleFooFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kBrowser);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleFooFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerBrowser_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneBarFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kBrowser);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneBarFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerBrowser_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleBarFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kBrowser);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleBarFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerBrowser_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowRemember_121FileHandlerOneFooFile) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kBrowser);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kRemember);
+  helper_.LaunchFileExpectNoDialog(Site::kFileHandler,
+                                   FilesOptions::kOneFooFile);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerBrowser_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyAskAgain_127_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kBrowser);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kAskAgain);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_29FileHandlerBrowser_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyRemember_127_122FileHandlerFoo_122FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.CreateShortcut(Site::kFileHandler, WindowOptions::kBrowser);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kRemember);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleFooFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleFooFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneBarFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneBarFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleBarFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleBarFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowRemember_121FileHandlerOneFooFile) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kRemember);
+  helper_.LaunchFileExpectNoDialog(Site::kFileHandler,
+                                   FilesOptions::kOneFooFile);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyAskAgain_127_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kAskAgain);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyRemember_127_122FileHandlerFoo_122FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kRemember);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleFooFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleFooFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneBarFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneBarFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleBarFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleBarFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowRemember_121FileHandlerOneFooFile) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kRemember);
+  helper_.LaunchFileExpectNoDialog(Site::kFileHandler,
+                                   FilesOptions::kOneFooFile);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyAskAgain_127_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kAskAgain);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerWithShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyRemember_127_122FileHandlerFoo_122FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kWithShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kRemember);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleFooFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleFooFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneBarFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneBarFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleBarFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleBarFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowRemember_121FileHandlerOneFooFile) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kRemember);
+  helper_.LaunchFileExpectNoDialog(Site::kFileHandler,
+                                   FilesOptions::kOneFooFile);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyAskAgain_127_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kAskAgain);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutWindowedWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyRemember_127_122FileHandlerFoo_122FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kWindowed, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kRemember);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleFooFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleFooFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneBarFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneBarFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerMultipleBarFilesAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(
+      Site::kFileHandler, FilesOptions::kMultipleBarFiles,
+      AllowDenyOptions::kAllow, AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowRemember_121FileHandlerOneFooFile) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kRemember);
+  helper_.LaunchFileExpectNoDialog(Site::kFileHandler,
+                                   FilesOptions::kOneFooFile);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyAskAgain_127_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileAllowAskAgain) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kAskAgain);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kAllow,
+                                 AskAgainOptions::kAskAgain);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    WebAppIntegration,
+    WAI_32FileHandlerNoShortcutBrowserWebApp_118FileHandlerFoo_118FileHandlerBar_120FileHandlerOneFooFileDenyRemember_127_122FileHandlerFoo_122FileHandlerBar) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyApp(Site::kFileHandler, ShortcutOptions::kNoShortcut,
+                           WindowOptions::kBrowser, InstallMode::kWebApp);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kFileHandler, FileExtension::kBar);
+  helper_.LaunchFileExpectDialog(Site::kFileHandler, FilesOptions::kOneFooFile,
+                                 AllowDenyOptions::kDeny,
+                                 AskAgainOptions::kRemember);
+  helper_.CheckWindowNotCreated();
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kFoo);
+  helper_.CheckSiteNotHandlesFile(Site::kFileHandler, FileExtension::kBar);
+}
+
 }  // namespace
 }  // namespace web_app::integration_tests
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index ed367f1..073a55a 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -274,7 +274,9 @@
       .relative_manifest_id = "webapps_integration/file_handler/basic.html",
       .app_name = "File Handler",
       .wco_not_enabled_title = u"File Handler",
-      .icon_color = SK_ColorBLACK}},
+      .icon_color = SK_ColorBLACK,
+      .alternate_titles = {"File Handler - Text Handler",
+                           "File Handler - Image Handler"}}},
     {Site::kNoServiceWorker,
      {.relative_url = "/webapps_integration/site_no_service_worker/basic.html",
       .relative_manifest_id =
@@ -1184,10 +1186,10 @@
   FileHandlerLaunchDialogView::SetDefaultRememberSelectionForTesting(
       ask_again == AskAgainOptions::kRemember);
 
+  base::RunLoop run_loop;
+  web_app::startup::SetStartupDoneCallbackForTesting(run_loop.QuitClosure());
   LaunchFile(site, files_options);
 
-  BrowserAddedWaiter browser_added_waiter;
-
   // Check the file handling dialog shows up.
   views::Widget* widget = waiter.WaitIfNeededAndGet();
   ASSERT_TRUE(widget != nullptr);
@@ -1206,12 +1208,7 @@
   widget->CloseWithReason(close_reason);
   destroyed_waiter.Wait();
 
-  if (allow_deny == AllowDenyOptions::kAllow) {
-    browser_added_waiter.Wait();
-    app_browser_ = browser_added_waiter.browser_added();
-    ActivateBrowserAndWait(app_browser_);
-    EXPECT_EQ(app_browser()->app_controller()->app_id(), app_id);
-  }
+  run_loop.Run();
   AfterStateChangeAction();
 }
 
@@ -1220,17 +1217,15 @@
     FilesOptions files_options) {
   BeforeStateChangeAction(__FUNCTION__);
   AppId app_id = GetAppIdBySiteMode(site);
-  BrowserAddedWaiter browser_added_waiter;
+  base::RunLoop run_loop;
+  web_app::startup::SetStartupDoneCallbackForTesting(run_loop.QuitClosure());
   LaunchFile(site, files_options);
 
   // If the user previously denied access to open files with this app, a window
   // is still opened for the app. The only difference is that no files would
   // have been passed to the app. Either way, we should always wait for a
-  // browser to be added.
-  browser_added_waiter.Wait();
-  app_browser_ = browser_added_waiter.browser_added();
-  ActivateBrowserAndWait(app_browser_);
-  EXPECT_EQ(app_browser()->app_controller()->app_id(), app_id);
+  // window / browser to be added.
+  run_loop.Run();
 
   AfterStateChangeAction();
 }
diff --git a/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc b/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc
index 00ca936..2011d22 100644
--- a/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc
+++ b/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc
@@ -14,7 +14,6 @@
 #include "ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h"
 #include "ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h"
 #include "chrome/browser/ash/web_applications/camera_app/chrome_camera_app_ui_delegate.h"
-#include "chrome/browser/ash/web_applications/shortcut_customization_ui/chrome_shortcut_customization_delegate.h"
 #include "chrome/browser/ui/webui/ash/account_manager/account_manager_error_ui.h"
 #include "chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_ui.h"
 #include "chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui.h"
@@ -91,11 +90,6 @@
   map.AddWebUIConfig(
       MakeComponentConfig<ash::CameraAppUIConfig, ash::CameraAppUI,
                           ChromeCameraAppUIDelegate>());
-  map.AddWebUIConfig(
-      MakeComponentConfig<ash::ShortcutCustomizationAppUIConfig,
-                          ash::ShortcutCustomizationAppUI,
-                          ChromeShortcutCustomizationDelegate>());
-
   map.AddWebUIConfig(std::make_unique<ash::AccountManagerErrorUIConfig>());
   map.AddWebUIConfig(std::make_unique<ash::AccountMigrationWelcomeUIConfig>());
   map.AddWebUIConfig(std::make_unique<ash::AddSupervisionUIConfig>());
@@ -139,6 +133,7 @@
   map.AddWebUIConfig(std::make_unique<ash::PasswordChangeUIConfig>());
   map.AddWebUIConfig(std::make_unique<ash::PowerUIConfig>());
   map.AddWebUIConfig(std::make_unique<ash::SetTimeUIConfig>());
+  map.AddWebUIConfig(std::make_unique<ash::ShortcutCustomizationAppUIConfig>());
   map.AddWebUIConfig(std::make_unique<ash::SlowTraceControllerConfig>());
   map.AddWebUIConfig(std::make_unique<ash::SlowUIConfig>());
   map.AddWebUIConfig(
diff --git a/chrome/browser/ui/webui/metrics_internals/metrics_internals_handler.cc b/chrome/browser/ui/webui/metrics_internals/metrics_internals_handler.cc
index a918703..f155434d 100644
--- a/chrome/browser/ui/webui/metrics_internals/metrics_internals_handler.cc
+++ b/chrome/browser/ui/webui/metrics_internals/metrics_internals_handler.cc
@@ -5,22 +5,85 @@
 #include "chrome/browser/ui/webui/metrics_internals/metrics_internals_handler.h"
 
 #include "base/bind.h"
+#include "base/functional/bind.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
+#include "components/metrics/debug/metrics_internals_utils.h"
 #include "components/metrics/metrics_service.h"
+#include "components/metrics/metrics_service_observer.h"
+
+MetricsInternalsHandler::MetricsInternalsHandler()
+    : uma_log_observer_(
+          metrics::MetricsServiceObserver::MetricsServiceType::UMA) {
+  g_browser_process->metrics_service()->AddLogsObserver(&uma_log_observer_);
+}
+
+MetricsInternalsHandler::~MetricsInternalsHandler() {
+  g_browser_process->metrics_service()->RemoveLogsObserver(&uma_log_observer_);
+}
+
+void MetricsInternalsHandler::OnJavascriptAllowed() {
+  uma_log_notified_subscription_ = uma_log_observer_.AddNotifiedCallback(
+      base::BindRepeating(&MetricsInternalsHandler::OnUmaLogCreatedOrEvent,
+                          weak_ptr_factory_.GetWeakPtr()));
+}
+
+void MetricsInternalsHandler::OnJavascriptDisallowed() {
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  uma_log_notified_subscription_ = {};
+}
 
 void MetricsInternalsHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
-      "fetchClientId",
-      base::BindRepeating(&MetricsInternalsHandler::HandleFetchClientId,
+      "fetchVariationsSummary",
+      base::BindRepeating(
+          &MetricsInternalsHandler::HandleFetchVariationsSummary,
+          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "fetchUmaSummary",
+      base::BindRepeating(&MetricsInternalsHandler::HandleFetchUmaSummary,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "fetchUmaLogsData",
+      base::BindRepeating(&MetricsInternalsHandler::HandleFetchUmaLogsData,
                           base::Unretained(this)));
 }
 
-void MetricsInternalsHandler::HandleFetchClientId(
+void MetricsInternalsHandler::HandleFetchVariationsSummary(
+    const base::Value::List& args) {
+  AllowJavascript();
+  const base::Value& callback_id = args[0];
+  ResolveJavascriptCallback(
+      callback_id, metrics::GetVariationsSummary(
+                       g_browser_process->GetMetricsServicesManager()));
+}
+
+void MetricsInternalsHandler::HandleFetchUmaSummary(
     const base::Value::List& args) {
   AllowJavascript();
   const base::Value& callback_id = args[0];
   ResolveJavascriptCallback(
       callback_id,
-      base::Value(g_browser_process->metrics_service()->GetClientId()));
+      metrics::GetUmaSummary(
+          g_browser_process->GetMetricsServicesManager()->GetMetricsService()));
+}
+
+void MetricsInternalsHandler::HandleFetchUmaLogsData(
+    const base::Value::List& args) {
+  AllowJavascript();
+  // |args| should have two elements: the callback ID, and a bool parameter that
+  // determines whether we should include log proto data.
+  DCHECK_EQ(args.size(), 2U);
+  const base::Value& callback_id = args[0];
+  const bool include_log_proto_data = args[1].GetBool();
+
+  std::string logs_json;
+  bool result =
+      uma_log_observer_.ExportLogsAsJson(include_log_proto_data, &logs_json);
+  DCHECK(result);
+  ResolveJavascriptCallback(callback_id, base::Value(std::move(logs_json)));
+}
+
+void MetricsInternalsHandler::OnUmaLogCreatedOrEvent() {
+  FireWebUIListener("uma-log-created-or-event");
 }
diff --git a/chrome/browser/ui/webui/metrics_internals/metrics_internals_handler.h b/chrome/browser/ui/webui/metrics_internals/metrics_internals_handler.h
index 3499d047..b3155137 100644
--- a/chrome/browser/ui/webui/metrics_internals/metrics_internals_handler.h
+++ b/chrome/browser/ui/webui/metrics_internals/metrics_internals_handler.h
@@ -5,23 +5,41 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_METRICS_INTERNALS_METRICS_INTERNALS_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_METRICS_INTERNALS_METRICS_INTERNALS_HANDLER_H_
 
+#include "base/callback_list.h"
+#include "base/memory/weak_ptr.h"
+#include "components/metrics/metrics_service_observer.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
 // UI Handler for chrome://metrics-internals.
 class MetricsInternalsHandler : public content::WebUIMessageHandler {
  public:
-  MetricsInternalsHandler() = default;
+  MetricsInternalsHandler();
 
   MetricsInternalsHandler(const MetricsInternalsHandler&) = delete;
   MetricsInternalsHandler& operator=(const MetricsInternalsHandler&) = delete;
 
-  ~MetricsInternalsHandler() override = default;
+  ~MetricsInternalsHandler() override;
 
   // content::WebUIMessageHandler:
+  void OnJavascriptAllowed() override;
+  void OnJavascriptDisallowed() override;
   void RegisterMessages() override;
 
  private:
-  void HandleFetchClientId(const base::Value::List& args);
+  void HandleFetchVariationsSummary(const base::Value::List& args);
+  void HandleFetchUmaSummary(const base::Value::List& args);
+  void HandleFetchUmaLogsData(const base::Value::List& args);
+  void OnUmaLogCreatedOrEvent();
+
+  // This UMA log observer keeps track of logs since its creation.
+  metrics::MetricsServiceObserver uma_log_observer_;
+
+  // The callback subscription to |uma_log_observer_| that notifies the WebUI
+  // of changes. When this subscription is destroyed, it is automatically
+  // de-registered from the callback list.
+  base::CallbackListSubscription uma_log_notified_subscription_;
+
+  base::WeakPtrFactory<MetricsInternalsHandler> weak_ptr_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_METRICS_INTERNALS_METRICS_INTERNALS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 72ebe05..140c7aa 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -372,6 +372,7 @@
       source,
       base::make_span(kPrintPreviewResources, kPrintPreviewResourcesSize),
       IDR_PRINT_PREVIEW_PRINT_PREVIEW_HTML);
+  webui::EnableTrustedTypesCSP(source);
   AddPrintPreviewStrings(source);
   SetupPrintPreviewPlugin(source);
   AddPrintPreviewFlags(source, profile);
diff --git a/chrome/browser/ui/webui/settings/ash/accessibility_section.cc b/chrome/browser/ui/webui/settings/ash/accessibility_section.cc
index 6d34b3d..a6761a98 100644
--- a/chrome/browser/ui/webui/settings/ash/accessibility_section.cc
+++ b/chrome/browser/ui/webui/settings/ash/accessibility_section.cc
@@ -737,6 +737,8 @@
        IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_HIGHLIGHT_LIGHT},
       {"selectToSpeakOptionsBackgroundShadingDescription",
        IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_BACKGROUND_SHADING_DESCRIPTION},
+      {"selectToSpeakOptionsSampleText",
+       IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_SAMPLE_TEXT},
       {"selectToSpeakOptionsNavigationControlsDescription",
        IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_NAVIGATION_CONTROLS_DESCRIPTION},
       {"selectToSpeakOptionsNavigationControlsSubtitle",
diff --git a/chrome/browser/ui/webui/settings/ash/os_settings_sections.cc b/chrome/browser/ui/webui/settings/ash/os_settings_sections.cc
index 73475bb..a6fa3735 100644
--- a/chrome/browser/ui/webui/settings/ash/os_settings_sections.cc
+++ b/chrome/browser/ui/webui/settings/ash/os_settings_sections.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/settings/ash/os_settings_sections.h"
 
 #include "ash/components/phonehub/phone_hub_manager.h"
+#include "base/containers/contains.h"
 #include "build/branding_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/settings/ash/about_section.h"
@@ -47,108 +48,84 @@
     CupsPrintersManager* printers_manager,
     apps::AppServiceProxy* app_service_proxy,
     eche_app::EcheAppManager* eche_app_manager) {
+  auto* prefs = profile->GetPrefs();
+
   // Special case: Main section does not have an associated enum value.
   sections_.push_back(
       std::make_unique<MainSection>(profile, search_tag_registry));
 
-  auto internet_section =
-      std::make_unique<InternetSection>(profile, search_tag_registry);
-  sections_map_[mojom::Section::kNetwork] = internet_section.get();
-  sections_.push_back(std::move(internet_section));
+  AddSection(mojom::Section::kNetwork,
+             std::make_unique<InternetSection>(profile, search_tag_registry));
 
-  auto bluetooth_section = std::make_unique<BluetoothSection>(
-      profile, search_tag_registry, profile->GetPrefs());
-  sections_map_[mojom::Section::kBluetooth] = bluetooth_section.get();
-  sections_.push_back(std::move(bluetooth_section));
+  AddSection(
+      mojom::Section::kBluetooth,
+      std::make_unique<BluetoothSection>(profile, search_tag_registry, prefs));
 
-  auto multidevice_section = std::make_unique<MultiDeviceSection>(
-      profile, search_tag_registry, multidevice_setup_client, phone_hub_manager,
-      android_sms_service, profile->GetPrefs(), eche_app_manager);
-  sections_map_[mojom::Section::kMultiDevice] = multidevice_section.get();
-  sections_.push_back(std::move(multidevice_section));
+  AddSection(
+      mojom::Section::kMultiDevice,
+      std::make_unique<MultiDeviceSection>(
+          profile, search_tag_registry, multidevice_setup_client,
+          phone_hub_manager, android_sms_service, prefs, eche_app_manager));
 
-  auto people_section = std::make_unique<PeopleSection>(
-      profile, search_tag_registry, sync_service, supervised_user_service,
-      identity_manager, profile->GetPrefs());
-  sections_map_[mojom::Section::kPeople] = people_section.get();
-  sections_.push_back(std::move(people_section));
+  AddSection(mojom::Section::kPeople,
+             std::make_unique<PeopleSection>(
+                 profile, search_tag_registry, sync_service,
+                 supervised_user_service, identity_manager, prefs));
 
-  auto device_section = std::make_unique<DeviceSection>(
-      profile, search_tag_registry, profile->GetPrefs());
-  sections_map_[mojom::Section::kDevice] = device_section.get();
-  sections_.push_back(std::move(device_section));
+  AddSection(mojom::Section::kDevice, std::make_unique<DeviceSection>(
+                                          profile, search_tag_registry, prefs));
 
-  auto personalization_section = std::make_unique<PersonalizationSection>(
-      profile, search_tag_registry, profile->GetPrefs());
-  sections_map_[mojom::Section::kPersonalization] =
-      personalization_section.get();
-  sections_.push_back(std::move(personalization_section));
+  AddSection(mojom::Section::kPersonalization,
+             std::make_unique<PersonalizationSection>(
+                 profile, search_tag_registry, prefs));
 
-  auto search_section =
-      std::make_unique<SearchSection>(profile, search_tag_registry);
-  sections_map_[mojom::Section::kSearchAndAssistant] = search_section.get();
-  sections_.push_back(std::move(search_section));
+  AddSection(mojom::Section::kSearchAndAssistant,
+             std::make_unique<SearchSection>(profile, search_tag_registry));
 
-  auto apps_section = std::make_unique<AppsSection>(
-      profile, search_tag_registry, profile->GetPrefs(), arc_app_list_prefs,
-      app_service_proxy);
-  sections_map_[mojom::Section::kApps] = apps_section.get();
-  sections_.push_back(std::move(apps_section));
+  AddSection(mojom::Section::kApps, std::make_unique<AppsSection>(
+                                        profile, search_tag_registry, prefs,
+                                        arc_app_list_prefs, app_service_proxy));
 
-  auto crostini_section = std::make_unique<CrostiniSection>(
-      profile, search_tag_registry, profile->GetPrefs());
-  sections_map_[mojom::Section::kCrostini] = crostini_section.get();
-  sections_.push_back(std::move(crostini_section));
+  AddSection(
+      mojom::Section::kCrostini,
+      std::make_unique<CrostiniSection>(profile, search_tag_registry, prefs));
 
-  auto date_time_section =
-      std::make_unique<DateTimeSection>(profile, search_tag_registry);
-  sections_map_[mojom::Section::kDateAndTime] = date_time_section.get();
-  sections_.push_back(std::move(date_time_section));
+  AddSection(mojom::Section::kDateAndTime,
+             std::make_unique<DateTimeSection>(profile, search_tag_registry));
 
-  auto privacy_section = std::make_unique<PrivacySection>(
-      profile, search_tag_registry, profile->GetPrefs());
-  sections_map_[mojom::Section::kPrivacyAndSecurity] = privacy_section.get();
-  sections_.push_back(std::move(privacy_section));
+  AddSection(
+      mojom::Section::kPrivacyAndSecurity,
+      std::make_unique<PrivacySection>(profile, search_tag_registry, prefs));
 
-  auto language_section = std::make_unique<LanguagesSection>(
-      profile, search_tag_registry, profile->GetPrefs());
-  sections_map_[mojom::Section::kLanguagesAndInput] = language_section.get();
-  sections_.push_back(std::move(language_section));
+  AddSection(
+      mojom::Section::kLanguagesAndInput,
+      std::make_unique<LanguagesSection>(profile, search_tag_registry, prefs));
 
-  auto files_section =
-      std::make_unique<FilesSection>(profile, search_tag_registry);
-  sections_map_[mojom::Section::kFiles] = files_section.get();
-  sections_.push_back(std::move(files_section));
+  AddSection(mojom::Section::kFiles,
+             std::make_unique<FilesSection>(profile, search_tag_registry));
 
-  auto printing_section = std::make_unique<PrintingSection>(
-      profile, search_tag_registry, printers_manager);
-  sections_map_[mojom::Section::kPrinting] = printing_section.get();
-  sections_.push_back(std::move(printing_section));
+  AddSection(mojom::Section::kPrinting,
+             std::make_unique<PrintingSection>(profile, search_tag_registry,
+                                               printers_manager));
 
-  auto accessibility_section = std::make_unique<AccessibilitySection>(
-      profile, search_tag_registry, profile->GetPrefs());
-  sections_map_[mojom::Section::kAccessibility] = accessibility_section.get();
-  sections_.push_back(std::move(accessibility_section));
+  AddSection(mojom::Section::kAccessibility,
+             std::make_unique<AccessibilitySection>(
+                 profile, search_tag_registry, prefs));
 
-  auto reset_section =
-      std::make_unique<ResetSection>(profile, search_tag_registry);
-  sections_map_[mojom::Section::kReset] = reset_section.get();
-  sections_.push_back(std::move(reset_section));
+  AddSection(mojom::Section::kReset,
+             std::make_unique<ResetSection>(profile, search_tag_registry));
 
+  AddSection(mojom::Section::kAboutChromeOs,
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  auto about_section = std::make_unique<AboutSection>(
-      profile, search_tag_registry, profile->GetPrefs());
+             std::make_unique<AboutSection>(profile, search_tag_registry, prefs)
 #else
-  auto about_section =
-      std::make_unique<AboutSection>(profile, search_tag_registry);
+             std::make_unique<AboutSection>(profile, search_tag_registry)
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  sections_map_[mojom::Section::kAboutChromeOs] = about_section.get();
-  sections_.push_back(std::move(about_section));
+  );
 
-  auto kerberos_section = std::make_unique<KerberosSection>(
-      profile, search_tag_registry, kerberos_credentials_manager);
-  sections_map_[mojom::Section::kKerberos] = kerberos_section.get();
-  sections_.push_back(std::move(kerberos_section));
+  AddSection(mojom::Section::kKerberos,
+             std::make_unique<KerberosSection>(profile, search_tag_registry,
+                                               kerberos_credentials_manager));
 }
 
 OsSettingsSections::OsSettingsSections() = default;
@@ -162,4 +139,13 @@
   return it->second;
 }
 
+void OsSettingsSections::AddSection(
+    chromeos::settings::mojom::Section section_id,
+    std::unique_ptr<OsSettingsSection> section) {
+  DCHECK(!base::Contains(sections_map_, section_id));
+
+  sections_map_[section_id] = section.get();
+  sections_.push_back(std::move(section));
+}
+
 }  // namespace ash::settings
diff --git a/chrome/browser/ui/webui/settings/ash/os_settings_sections.h b/chrome/browser/ui/webui/settings/ash/os_settings_sections.h
index c2cca4c4..40501d23e 100644
--- a/chrome/browser/ui/webui/settings/ash/os_settings_sections.h
+++ b/chrome/browser/ui/webui/settings/ash/os_settings_sections.h
@@ -79,6 +79,10 @@
   std::unordered_map<chromeos::settings::mojom::Section, OsSettingsSection*>
       sections_map_;
   std::vector<std::unique_ptr<OsSettingsSection>> sections_;
+
+ private:
+  void AddSection(chromeos::settings::mojom::Section section_id,
+                  std::unique_ptr<OsSettingsSection> section);
 };
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/webui_util.cc b/chrome/browser/ui/webui/webui_util.cc
index 089e337f..cf53734 100644
--- a/chrome/browser/ui/webui/webui_util.cc
+++ b/chrome/browser/ui/webui/webui_util.cc
@@ -73,6 +73,8 @@
       "lottie-worker-script-loader "
       // Add TrustedTypes policies used during tests.
       "webui-test-script webui-test-html "
+      // Add TrustedTypes policy for creating the PDF plugin.
+      "print-preview-plugin-loader "
       // Add TrustedTypes policies necessary for using Polymer.
       "polymer-html-literal polymer-template-event-attribute-policy;");
 }
diff --git a/chrome/browser/web_applications/os_integration/os_integration_manager.cc b/chrome/browser/web_applications/os_integration/os_integration_manager.cc
index 272ec37..0dfdbabe 100644
--- a/chrome/browser/web_applications/os_integration/os_integration_manager.cc
+++ b/chrome/browser/web_applications/os_integration/os_integration_manager.cc
@@ -24,6 +24,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/os_integration/os_integration_sub_manager.h"
 #include "chrome/browser/web_applications/os_integration/web_app_shortcut.h"
 #include "chrome/browser/web_applications/os_integration/web_app_uninstallation_via_os_settings_registration.h"
 #include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.h"
@@ -150,36 +151,23 @@
     protocol_handler_manager_->Start();
 
   // Start all sub managers that need to be started.
-  for (const auto& sm : sub_managers_) {
-    sm->Start();
+  for (const auto& sub_manager : sub_managers_) {
+    sub_manager->Start();
   }
 }
 
 void OsIntegrationManager::Synchronize(const AppId& app_id,
-                                       SubManagerCompletedCallback callback) {
-  if (registrar_->GetAppById(app_id)->is_uninstalling()) {
-    // For uninstallation purposes, we perform unregistration directly without
-    // doing reads and writes from the DB.
-    auto uninstall_barrier = base::BarrierClosure(
-        sub_managers_.size(),
-        base::BindOnce(&OsIntegrationManager::OnSynchronizationComplete,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-    for (const auto& sm : sub_managers_) {
-      sm->Unregister(app_id, uninstall_barrier);
-    }
-  } else {
-    proto::WebAppOsIntegrationState desired_state;
-    const absl::optional<proto::WebAppOsIntegrationState> current_state =
-        registrar_->GetAppById(app_id)->current_os_integration_states();
-    auto configure_barrier = base::BarrierClosure(
-        sub_managers_.size(),
-        base::BindOnce(
-            &OsIntegrationManager::ExecuteAllSubManagerConfigurations,
-            weak_ptr_factory_.GetWeakPtr(), app_id, desired_state,
-            current_state, std::move(callback)));
-    for (const auto& sm : sub_managers_) {
-      sm->Configure(app_id, desired_state, configure_barrier);
-    }
+                                       base::OnceClosure callback) {
+  proto::WebAppOsIntegrationState desired_state;
+  const absl::optional<proto::WebAppOsIntegrationState> current_state =
+      registrar_->GetAppById(app_id)->current_os_integration_states();
+  auto configure_barrier = base::BarrierClosure(
+      sub_managers_.size(),
+      base::BindOnce(&OsIntegrationManager::ExecuteAllSubManagerConfigurations,
+                     weak_ptr_factory_.GetWeakPtr(), app_id, desired_state,
+                     current_state, std::move(callback)));
+  for (const auto& sub_manager : sub_managers_) {
+    sub_manager->Configure(app_id, desired_state, configure_barrier);
   }
 }
 
@@ -790,32 +778,35 @@
     const AppId& app_id,
     const proto::WebAppOsIntegrationState& desired_state,
     const absl::optional<proto::WebAppOsIntegrationState>& current_state,
-    SubManagerCompletedCallback callback) {
-  auto execution_barrier = base::BarrierClosure(
+    base::OnceClosure callback) {
+  auto write_state_callback = base::BarrierClosure(
       sub_managers_.size(),
       base::BindOnce(&OsIntegrationManager::WriteStateToDB,
                      weak_ptr_factory_.GetWeakPtr(), app_id, desired_state,
                      std::move(callback)));
-  for (const auto& sm : sub_managers_) {
-    sm->Execute(app_id, desired_state, current_state, execution_barrier);
+
+  for (const auto& sub_manager : sub_managers_) {
+    sub_manager->Execute(app_id, desired_state, current_state,
+                         write_state_callback);
   }
 }
 
 void OsIntegrationManager::WriteStateToDB(
     const AppId& app_id,
     const proto::WebAppOsIntegrationState& desired_state,
-    SubManagerCompletedCallback callback) {
+    base::OnceClosure callback) {
+  // Exit early if the app is scheduled to be uninstalled.
+  if (registrar_->GetAppById(app_id)->is_uninstalling()) {
+    std::move(callback).Run();
+    return;
+  }
+
   {
     ScopedRegistryUpdate update(sync_bridge_);
     WebApp* web_app = update->UpdateApp(app_id);
     web_app->SetCurrentOsIntegrationStates(desired_state);
   }
-  OnSynchronizationComplete(std::move(callback));
-}
-
-void OsIntegrationManager::OnSynchronizationComplete(
-    SubManagerCompletedCallback callback) {
-  std::move(callback).Run(OsHooksErrors());
+  std::move(callback).Run();
 }
 
 void OsIntegrationManager::OnShortcutsCreated(
diff --git a/chrome/browser/web_applications/os_integration/os_integration_manager.h b/chrome/browser/web_applications/os_integration/os_integration_manager.h
index 0a0ee41..f4249d0 100644
--- a/chrome/browser/web_applications/os_integration/os_integration_manager.h
+++ b/chrome/browser/web_applications/os_integration/os_integration_manager.h
@@ -16,7 +16,6 @@
 #include "base/scoped_observation.h"
 #include "base/strings/string_piece_forward.h"
 #include "chrome/browser/web_applications/app_registrar_observer.h"
-#include "chrome/browser/web_applications/os_integration/os_integration_sub_manager.h"
 #include "chrome/browser/web_applications/os_integration/url_handler_manager.h"
 #include "chrome/browser/web_applications/os_integration/web_app_file_handler_manager.h"
 #include "chrome/browser/web_applications/os_integration/web_app_protocol_handler_manager.h"
@@ -39,6 +38,7 @@
 class WebAppIconManager;
 class WebAppRegistrar;
 class WebAppSyncBridge;
+class OsIntegrationSubManager;
 class WebAppUiManager;
 
 // OsHooksErrors contains the result of all Os hook deployments.
@@ -75,10 +75,6 @@
 using UpdateOsHooksCallback =
     base::OnceCallback<void(OsHooksErrors os_hooks_errors)>;
 
-// Callback made when sub-managers are called
-using SubManagerCompletedCallback =
-    base::OnceCallback<void(OsHooksErrors os_hooks_errors)>;
-
 using BarrierCallback =
     base::RepeatingCallback<void(OsHookType::Type os_hook, bool completed)>;
 
@@ -113,8 +109,9 @@
 
   virtual void Start();
 
-  virtual void Synchronize(const AppId& app_id,
-                           SubManagerCompletedCallback callback);
+  // Start OS Integration synchronization from external points. This should be
+  // the only point of call into OsIntegrationManager from external places.
+  virtual void Synchronize(const AppId& app_id, base::OnceClosure callback);
 
   // Install all needed OS hooks for the web app.
   // If provided |web_app_info| is a nullptr, it will read icons data from disk,
@@ -294,15 +291,12 @@
       const AppId& app_id,
       const proto::WebAppOsIntegrationState& desired_state,
       const absl::optional<proto::WebAppOsIntegrationState>& expected_state,
-      SubManagerCompletedCallback callback);
+      base::OnceClosure callback);
 
   virtual void WriteStateToDB(
       const AppId& app_id,
       const proto::WebAppOsIntegrationState& desired_state,
-      SubManagerCompletedCallback done_callback);
-
-  virtual void OnSynchronizationComplete(
-      SubManagerCompletedCallback done_callback);
+      base::OnceClosure callback);
 
   void OnShortcutsCreated(const AppId& app_id,
                           std::unique_ptr<WebAppInstallInfo> web_app_info,
@@ -336,11 +330,11 @@
   std::unique_ptr<WebAppProtocolHandlerManager> protocol_handler_manager_;
   std::unique_ptr<UrlHandlerManager> url_handler_manager_;
 
+  std::vector<std::unique_ptr<OsIntegrationSubManager>> sub_managers_;
+
   base::ScopedObservation<WebAppRegistrar, AppRegistrarObserver>
       registrar_observation_{this};
 
-  std::vector<std::unique_ptr<OsIntegrationSubManager>> sub_managers_;
-
   base::WeakPtrFactory<OsIntegrationManager> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/web_applications/os_integration/os_integration_sub_manager.h b/chrome/browser/web_applications/os_integration/os_integration_sub_manager.h
index 4aba0c1..3041b38 100644
--- a/chrome/browser/web_applications/os_integration/os_integration_sub_manager.h
+++ b/chrome/browser/web_applications/os_integration/os_integration_sub_manager.h
@@ -5,11 +5,8 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_OS_INTEGRATION_OS_INTEGRATION_SUB_MANAGER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_OS_INTEGRATION_OS_INTEGRATION_SUB_MANAGER_H_
 
-#include "base/callback_forward.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
+#include "base/functional/callback_forward.h"
 #include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.h"
-#include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_id.h"
 
 namespace web_app {
@@ -26,9 +23,7 @@
       const AppId& app_id,
       const proto::WebAppOsIntegrationState& desired_state,
       const absl::optional<proto::WebAppOsIntegrationState>& current_state,
-      base::OnceClosure execute_done) = 0;
-  virtual void Unregister(const AppId& app_id,
-                          base::OnceClosure uninstall_done) = 0;
+      base::OnceClosure callback) = 0;
 };
 }  // namespace web_app
 
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index f546b91..d929044 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1669204592-d146cf7ed9baf7a94e8936b1837e3f20a3acfe1d.profdata
+chrome-mac-arm-main-1669226354-a033b34198972286de177e47c7f34244d10f7533.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 9e1972c..381b74f 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1669204592-015775ee22b7d02e316385784fe11b72b0b6157b.profdata
+chrome-win32-main-1669215337-f8119f6a2c27dea3d944627c637d7a6359d27ba4.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index e8290d2..267f6d3a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1669204592-388d54b71d4fd290a96ba6dc7433e1f955114d15.profdata
+chrome-win64-main-1669226354-2dc46fdc1f9ddcef2c0e19d351d1c4d52f1b0d50.profdata
diff --git a/chrome/common/profiler/unwind_util.cc b/chrome/common/profiler/unwind_util.cc
index 867b619..0add095 100644
--- a/chrome/common/profiler/unwind_util.cc
+++ b/chrome/common/profiler/unwind_util.cc
@@ -211,6 +211,19 @@
 bool AreUnwindPrerequisitesAvailable(
     version_info::Channel channel,
     UnwindPrerequisitesDelegate* prerequites_delegate) {
+// While non-Android platforms do not need any specific prerequisites beyond
+// what is already bundled and available with Chrome for their platform-specific
+// unwinders to work, Android, in particular, requires a DFM to be installed.
+//
+// Therefore, unwind prerequisites for non-supported Android platforms are not
+// considered to be available by default, but prerequisites for non-Android
+// platforms are considered to be available by default.
+//
+// This is also why we do not need to check `prerequites_delegate` for
+// non-Android platforms. Regardless of the provided delegate, unwind
+// prerequisites are always considered to be available for non-Android
+// platforms.
+#if BUILDFLAG(IS_ANDROID)
 #if ANDROID_ARM32_UNWINDING_SUPPORTED
 #if defined(OFFICIAL_BUILD) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
   // Sometimes, DFMs can be installed even if not requested by Chrome
@@ -229,8 +242,11 @@
   }
   return prerequites_delegate->AreAvailable(channel);
 #else   // ANDROID_ARM32_UNWINDING_SUPPORTED
-  return true;
+  return false;
 #endif  // ANDROID_ARM32_UNWINDING_SUPPORTED
+#else   // BUILDFLAG(IS_ANDROID)
+  return true;
+#endif  // BUILDFLAG(IS_ANDROID)
 }
 
 #if ANDROID_ARM32_UNWINDING_SUPPORTED
diff --git a/chrome/common/profiler/unwind_util.h b/chrome/common/profiler/unwind_util.h
index d42c064..00bac04 100644
--- a/chrome/common/profiler/unwind_util.h
+++ b/chrome/common/profiler/unwind_util.h
@@ -50,8 +50,8 @@
 // context?
 //
 // If `prerequites_delegate` is provided, it is used to check availability of
-// unwind prerequisites, on certain Android platforms only. Intended for unit
-// testing.
+// unwind prerequisites, on certain Android platforms only. This is intended for
+// unit testing so that tests can provide a mocked delegate, if needed.
 bool AreUnwindPrerequisitesAvailable(
     version_info::Channel channel,
     UnwindPrerequisitesDelegate* prerequites_delegate = nullptr);
diff --git a/chrome/common/profiler/unwind_util_unittest.cc b/chrome/common/profiler/unwind_util_unittest.cc
index e131fda4..c1fa0b2 100644
--- a/chrome/common/profiler/unwind_util_unittest.cc
+++ b/chrome/common/profiler/unwind_util_unittest.cc
@@ -102,34 +102,54 @@
     raw_ptr<UnwindPrerequisitesDelegate> delegate;
     bool are_unwind_prerequisites_expected;
   } test_cases[] = {
-    {version_info::Channel::CANARY, &true_mock_delegate, true},
-    {version_info::Channel::DEV, &true_mock_delegate, true},
-    {version_info::Channel::BETA, &true_mock_delegate, true},
-#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARMEL) && \
-    BUILDFLAG(ENABLE_ARM_CFI_TABLE)
+#if BUILDFLAG(IS_ANDROID)
+    // Android unwinders require the presence of the unwinder module.
     {version_info::Channel::CANARY, &false_mock_delegate, false},
     {version_info::Channel::DEV, &false_mock_delegate, false},
     {version_info::Channel::BETA, &false_mock_delegate, false},
     {version_info::Channel::STABLE, &false_mock_delegate, false},
     {version_info::Channel::UNKNOWN, &false_mock_delegate, false},
+
+#if defined(ARCH_CPU_ARMEL) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
+    {version_info::Channel::CANARY, &true_mock_delegate, true},
+    {version_info::Channel::DEV, &true_mock_delegate, true},
+    {version_info::Channel::BETA, &true_mock_delegate, true},
 #if defined(OFFICIAL_BUILD) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+    // Since DFMs can be installed even if not requested by Chrome explicitly
+    // (for instance, in some app stores), for official builds, we
+    // only consider the unwinder module to be available for specific channels
+    // (which does not include `STABLE` and `UNKNOWN`).
     {version_info::Channel::STABLE, &true_mock_delegate, false},
     {version_info::Channel::UNKNOWN, &true_mock_delegate, false},
 #else  // defined(OFFICIAL_BUILD) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
     {version_info::Channel::STABLE, &true_mock_delegate, true},
     {version_info::Channel::UNKNOWN, &true_mock_delegate, true},
 #endif  // defined(OFFICIAL_BUILD) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-#else   // BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARMEL) &&
-        // BUILDFLAG(ENABLE_ARM_CFI_TABLE)
+#else   // defined(ARCH_CPU_ARMEL) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
+    // Unwinding on non-ARM32 platforms is not currently supported for Android.
+    {version_info::Channel::CANARY, &true_mock_delegate, false},
+    {version_info::Channel::DEV, &true_mock_delegate, false},
+    {version_info::Channel::BETA, &true_mock_delegate, false},
+    {version_info::Channel::STABLE, &true_mock_delegate, false},
+    {version_info::Channel::UNKNOWN, &true_mock_delegate, false},
+#endif  // defined(ARCH_CPU_ARMEL) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
+#else   // BUILDFLAG(IS_ANDROID)
+    // Non-Android platforms' unwinders do not need any specific prerequisites
+    // beyond what is already bundled and available with Chrome. Therefore,
+    // regardless of the provided delegate or channel, unwind prerequisites are
+    // always considered to be available.
     {version_info::Channel::CANARY, &false_mock_delegate, true},
     {version_info::Channel::DEV, &false_mock_delegate, true},
     {version_info::Channel::BETA, &false_mock_delegate, true},
-    {version_info::Channel::STABLE, &true_mock_delegate, true},
     {version_info::Channel::STABLE, &false_mock_delegate, true},
-    {version_info::Channel::UNKNOWN, &true_mock_delegate, true},
     {version_info::Channel::UNKNOWN, &false_mock_delegate, true},
-#endif  // BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARMEL) &&
-        // BUILDFLAG(ENABLE_ARM_CFI_TABLE)
+
+    {version_info::Channel::CANARY, &true_mock_delegate, true},
+    {version_info::Channel::DEV, &true_mock_delegate, true},
+    {version_info::Channel::BETA, &true_mock_delegate, true},
+    {version_info::Channel::STABLE, &true_mock_delegate, true},
+    {version_info::Channel::UNKNOWN, &true_mock_delegate, true},
+#endif  // BUILDFLAG(IS_ANDROID)
   };
 
   for (const auto& test_case : test_cases) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 162561c..b00cfbb 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2379,6 +2379,8 @@
       "data/webui/chrome_send_browsertest.h",
       "data/webui/history_ui_browsertest.cc",
       "data/webui/history_ui_browsertest.h",
+      "data/webui/metrics_internals/metrics_internals_ui_browsertest.cc",
+      "data/webui/metrics_internals/metrics_internals_ui_browsertest.h",
       "data/webui/mojo/mojo_file_system_access_browsertest.cc",
       "data/webui/mojo/mojo_js_interface_broker_browsertest.cc",
       "data/webui/mojo/mojo_web_ui_controller_browsertest.cc",
diff --git a/chrome/test/data/webapps_integration/file_handler/image_handler.html b/chrome/test/data/webapps_integration/file_handler/bar_handler.html
similarity index 100%
rename from chrome/test/data/webapps_integration/file_handler/image_handler.html
rename to chrome/test/data/webapps_integration/file_handler/bar_handler.html
diff --git a/chrome/test/data/webapps_integration/file_handler/text_handler.html b/chrome/test/data/webapps_integration/file_handler/foo_handler.html
similarity index 100%
rename from chrome/test/data/webapps_integration/file_handler/text_handler.html
rename to chrome/test/data/webapps_integration/file_handler/foo_handler.html
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 24b2673..5f629dc 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -105,6 +105,7 @@
       "js2gtest_browsertest.js",
       "management/a11y/management_a11y_test.js",
       "media_internals/media_internals_ui_browsertest.js",
+      "metrics_internals/metrics_internals_ui_browsertest.js",
       "net_internals/net_internals_browsertest.js",
       "ntp4.js",
       "password_manager/password_manager_browsertest.js",
@@ -493,6 +494,7 @@
     "gaia_auth_host:build_grdp",
     "history:build_grdp",
     "js:build_grdp",
+    "metrics_internals:build_grdp",
     "metrics_reporter:build_grdp",
     "net_internals:build_grdp",
     "new_tab_page:build_grdp",
@@ -519,6 +521,7 @@
     "$target_gen_dir/new_tab_page/resources.grdp",
     "$target_gen_dir/side_panel/resources.grdp",
     "$target_gen_dir/side_panel/customize_chrome/resources.grdp",
+    "$target_gen_dir/metrics_internals/resources.grdp",
     "$target_gen_dir/metrics_reporter/resources.grdp",
     "$target_gen_dir/mocha_resources.grdp",
     "$target_gen_dir/settings/resources.grdp",
diff --git a/chrome/test/data/webui/cr_elements/cr_view_manager_test.ts b/chrome/test/data/webui/cr_elements/cr_view_manager_test.ts
index b3a95af..a32a0dc2 100644
--- a/chrome/test/data/webui/cr_elements/cr_view_manager_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_view_manager_test.ts
@@ -7,8 +7,6 @@
 
 import {CrViewManagerElement} from 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js';
 
-import {assert} from 'chrome://resources/js/assert.js';
-
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {isChildVisible} from 'chrome://webui-test/test_util.js';
 // clang-format on
@@ -42,7 +40,7 @@
     viewManager = document.body.querySelector('#viewManager')!;
   });
 
-  test(assert(TestNames.VISIBILITY), function() {
+  test(TestNames.VISIBILITY, function() {
     function assertViewVisible(id: string, expectIsVisible: boolean) {
       assertEquals(
           expectIsVisible,
@@ -68,7 +66,7 @@
         });
   });
 
-  test(assert(TestNames.EVENT_FIRING), function() {
+  test(TestNames.EVENT_FIRING, function() {
     const viewOne = viewManager.querySelector('#viewOne')!;
 
     let fired = new Set();
diff --git a/chrome/test/data/webui/cr_elements/find_shortcut_mixin_test.ts b/chrome/test/data/webui/cr_elements/find_shortcut_mixin_test.ts
index 6fe2125d..d8409e6 100644
--- a/chrome/test/data/webui/cr_elements/find_shortcut_mixin_test.ts
+++ b/chrome/test/data/webui/cr_elements/find_shortcut_mixin_test.ts
@@ -6,12 +6,11 @@
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 
 import {FindShortcutManager, FindShortcutMixin} from 'chrome://resources/cr_elements/find_shortcut_mixin.js';
-import {assert} from 'chrome://resources/js/assert.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {assertEquals, assertThrows, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertEquals, assertFalse, assertThrows, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 // clang-format on
@@ -70,7 +69,7 @@
     }
 
     override handleFindShortcut(modalContextOpen: boolean) {
-      assert(!resolved);
+      assertFalse(resolved);
       wait.resolve({modalContextOpen, self: this});
       return true;
     }
@@ -98,7 +97,7 @@
     }
 
     override handleFindShortcut(modalContextOpen: boolean) {
-      assert(!resolved);
+      assertFalse(resolved);
       wait.resolve({modalContextOpen, self: this});
       return this.handledResponse;
     }
diff --git a/chrome/test/data/webui/metrics_internals/BUILD.gn b/chrome/test/data/webui/metrics_internals/BUILD.gn
new file mode 100644
index 0000000..fc76b79
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/BUILD.gn
@@ -0,0 +1,41 @@
+# 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("//tools/typescript/ts_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+ts_library("build_ts") {
+  root_dir = "."
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  path_mappings = [
+    "chrome://metrics-internals/*|" +
+        rebase_path("$root_gen_dir/components/metrics/debug/tsc/*",
+                    target_gen_dir),
+    "chrome://webui-test/*|" +
+        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
+                    target_gen_dir),
+  ]
+
+  in_files = [
+    "no_logs_test.ts",
+    "with_log_test.ts",
+    "utils.ts",
+  ]
+
+  deps = [
+    "..:build_ts",
+    "//components/metrics/debug:build_ts",
+  ]
+}
+
+generate_grd("build_grdp") {
+  grd_prefix = "webui_metrics_internals"
+  out_grd = "$target_gen_dir/resources.grdp"
+
+  deps = [ ":build_ts" ]
+  manifest_files =
+      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
+  resource_path_prefix = "metrics_internals"
+}
diff --git a/chrome/test/data/webui/metrics_internals/DIR_METADATA b/chrome/test/data/webui/metrics_internals/DIR_METADATA
new file mode 100644
index 0000000..7e7dcdb
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+  component: "Internals>Metrics"
+}
\ No newline at end of file
diff --git a/chrome/test/data/webui/metrics_internals/OWNERS b/chrome/test/data/webui/metrics_internals/OWNERS
new file mode 100644
index 0000000..7dc1e6e2
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/OWNERS
@@ -0,0 +1,2 @@
+lucnguyen@google.com
+rkaplow@chromium.org
\ No newline at end of file
diff --git a/chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.cc b/chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.cc
new file mode 100644
index 0000000..abd5a0f
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.cc
@@ -0,0 +1,46 @@
+// 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/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/metrics/metrics_service.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "url/gurl.h"
+
+void MetricsInternalsUIBrowserTest::SetUp() {
+  // Make metrics reporting work the same as in Chrome branded builds, for test
+  // consistency between Chromium and Chrome builds.
+  ChromeMetricsServiceAccessor::SetForceIsMetricsReportingEnabledPrefLookup(
+      true);
+  ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting(
+      &metrics_enabled_);
+
+  WebUIBrowserTest::SetUp();
+}
+
+void MetricsInternalsUIBrowserTest::SetUpOnMainThread() {
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  DCHECK(web_contents);
+  // Note that we stop observing automatically in the destructor of
+  // content::WebContentsObserver, so no need to do it manually.
+  content::WebContentsObserver::Observe(web_contents);
+
+  WebUIBrowserTest::SetUpOnMainThread();
+}
+
+void MetricsInternalsUIBrowserTest::DOMContentLoaded(
+    content::RenderFrameHost* render_frame_host) {
+  // Close and stage a log upon finishing loading chrome://metrics-internals.
+  // This will allow us to have a log ready for the JS browsertest.
+  if (render_frame_host->GetLastCommittedURL().host() ==
+      chrome::kChromeUIMetricsInternalsHost) {
+    g_browser_process->metrics_service()->StageCurrentLogForTest();
+  }
+}
diff --git a/chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.h b/chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.h
new file mode 100644
index 0000000..8305324
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.h
@@ -0,0 +1,31 @@
+// 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_TEST_DATA_WEBUI_METRICS_INTERNALS_METRICS_INTERNALS_UI_BROWSERTEST_H_
+#define CHROME_TEST_DATA_WEBUI_METRICS_INTERNALS_METRICS_INTERNALS_UI_BROWSERTEST_H_
+
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents_observer.h"
+
+// Test fixture used in JS browser tests that closes a log upon navigating to
+// chrome://metrics-internals.
+class MetricsInternalsUIBrowserTest : public WebUIBrowserTest,
+                                      public content::WebContentsObserver {
+ public:
+  MetricsInternalsUIBrowserTest() = default;
+  ~MetricsInternalsUIBrowserTest() override = default;
+
+  // WebUIBrowserTest:
+  void SetUp() override;
+  void SetUpOnMainThread() override;
+
+ private:
+  // content::WebContentsObserver:
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
+
+  bool metrics_enabled_ = true;
+};
+
+#endif  // CHROME_TEST_DATA_WEBUI_METRICS_INTERNALS_METRICS_INTERNALS_UI_BROWSERTEST_H_
diff --git a/chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.js b/chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.js
new file mode 100644
index 0000000..73533c7
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.js
@@ -0,0 +1,53 @@
+// 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.
+
+/**
+ * @fileoverview Test suite for the chrome://metrics-internals WebUI page.
+ * Unlike most other WebUI tests under the chrome/test/data/webui directory,
+ * this file tests both the frontend and the backend intentionally.
+ */
+
+GEN('#include "chrome/test/data/webui/metrics_internals/metrics_internals_ui_browsertest.h"');
+GEN('#include "content/public/test/browser_test.h"');
+
+var MetricsInternalsUIBrowserTest = class extends testing.Test {
+  /** @override */
+  get isAsync() {
+    return true;
+  }
+
+  /** @override */
+  get webuiHost() {
+    return 'metrics-internals';
+  }
+};
+
+var MetricsInternalsUIBrowserTestWithoutLog =
+    class extends MetricsInternalsUIBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://metrics-internals/test_loader.html?module=metrics_internals/no_logs_test.js';
+  }
+}
+
+TEST_F('MetricsInternalsUIBrowserTestWithoutLog', 'All', function() {
+  mocha.run();
+});
+
+var MetricsInternalsUIBrowserTestWithLog =
+    class extends MetricsInternalsUIBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://metrics-internals/test_loader.html?module=metrics_internals/with_log_test.js';
+  }
+
+  /** @override */
+  get typedefCppFixture() {
+    return 'MetricsInternalsUIBrowserTest';
+  }
+};
+
+TEST_F('MetricsInternalsUIBrowserTestWithLog', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/metrics_internals/no_logs_test.ts b/chrome/test/data/webui/metrics_internals/no_logs_test.ts
new file mode 100644
index 0000000..4d77087
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/no_logs_test.ts
@@ -0,0 +1,74 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://metrics-internals/app.js';
+
+import {MetricsInternalsAppElement} from 'chrome://metrics-internals/app.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {assertEquals, assertGT, assertTrue} from 'chrome://webui-test/chai_assert.js';
+
+import {getTableRowAsStringArray} from './utils.js';
+
+suite('NoLogsModuleTest', function() {
+  let app: MetricsInternalsAppElement;
+
+  setup(() => {
+    app = document.createElement('metrics-internals-app');
+    document.body.appendChild(app);
+    return app.initPromise;
+  });
+
+  test('UMA summary table should have info', function() {
+    const umaSummaryTable =
+        app.shadowRoot!.querySelector<HTMLElement>('#uma-summary-body');
+    assert(umaSummaryTable);
+    const rows = umaSummaryTable.querySelectorAll('tr');
+
+    // The table should be non-empty. Test for a few rows that should be there.
+    assertGT(rows.length, 0);
+    const rowsKeys = Array.from(rows).map(
+        el => (el!.firstElementChild as HTMLElement).innerText);
+    assertTrue(rowsKeys.includes('Client ID'));
+    assertTrue(rowsKeys.includes('Metrics Reporting Enabled'));
+  });
+
+  test('variations summary table should have info', function() {
+    const variationsSummaryTable =
+        app.shadowRoot!.querySelector<HTMLElement>('#variations-summary-body');
+    assert(variationsSummaryTable);
+    const rows = variationsSummaryTable.querySelectorAll('tr');
+
+    // The table should be non-empty. Test for a few rows that should be
+    // there.
+    assertGT(rows.length, 0);
+    const rowsKeys = Array.from(rows).map(
+        el => (el!.firstElementChild as HTMLElement).innerText);
+    assertTrue(rowsKeys.includes('Channel'));
+    assertTrue(rowsKeys.includes('Version'));
+    assertTrue(rowsKeys.includes('Platform'));
+  });
+
+  test('table should show an empty log if there are no logs', function() {
+    const umaLogsTable =
+        app.shadowRoot!.querySelector<HTMLElement>('#uma-logs-body');
+    assert(umaLogsTable);
+
+    // All 5 columns of the first row should be filled with 'N/A'.
+    const firstRow = getTableRowAsStringArray(umaLogsTable, 0);
+    assertEquals(firstRow.length, 5);
+    firstRow.forEach((el: string) => {
+      assertEquals(el, 'N/A');
+    });
+  });
+
+  test('exported logs should be empty if there are no logs', async function() {
+    const exportedLogs = await app.getUmaLogsExportContent();
+    const exportedLogsObj = JSON.parse(exportedLogs);
+
+    // The exported logs should contain no logs.
+    assertEquals(exportedLogsObj.logType, 'UMA');
+    assertTrue(!!exportedLogsObj.logs);
+    assertEquals(exportedLogsObj.logs.length, 0);
+  });
+});
diff --git a/chrome/test/data/webui/metrics_internals/tsconfig_base.json b/chrome/test/data/webui/metrics_internals/tsconfig_base.json
new file mode 100644
index 0000000..2c47cd74
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/tsconfig_base.json
@@ -0,0 +1,9 @@
+{
+  "extends": "../../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "typeRoots": [
+       "./../../../../../third_party/node/node_modules/@types"
+    ],
+    "types": ["mocha"]
+  }
+}
\ No newline at end of file
diff --git a/chrome/test/data/webui/metrics_internals/utils.ts b/chrome/test/data/webui/metrics_internals/utils.ts
new file mode 100644
index 0000000..72237c5
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/utils.ts
@@ -0,0 +1,18 @@
+// 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 {assert} from 'chrome://resources/js/assert_ts.js';
+import {assertGT} from 'chrome://webui-test/chai_assert.js';
+
+/**
+ * Gets the column text contents of a specific row of a table body element.
+ */
+export function getTableRowAsStringArray(
+    table: HTMLElement, row: number): string[] {
+  const rows = table.querySelectorAll('tr');
+  assertGT(rows.length, row);
+  const rowEl = rows[row];
+  assert(rowEl);
+  return Array.from(rowEl.querySelectorAll('td')).map(el => el.innerText);
+}
diff --git a/chrome/test/data/webui/metrics_internals/with_log_test.ts b/chrome/test/data/webui/metrics_internals/with_log_test.ts
new file mode 100644
index 0000000..418f8d9
--- /dev/null
+++ b/chrome/test/data/webui/metrics_internals/with_log_test.ts
@@ -0,0 +1,74 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://metrics-internals/app.js';
+
+import {MetricsInternalsAppElement} from 'chrome://metrics-internals/app.js';
+import {getEventsPeekString, sizeToString, timestampToString} from 'chrome://metrics-internals/log_utils.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {assertEquals, assertGT, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+
+import {getTableRowAsStringArray} from './utils.js';
+
+suite('WithLogModuleTest', function() {
+  let app: MetricsInternalsAppElement;
+
+  setup(() => {
+    app = document.createElement('metrics-internals-app');
+    document.body.appendChild(app);
+    return app.initPromise;
+  });
+
+  test('table should show log info if there is one', function() {
+    const umaLogsTable =
+        app.shadowRoot!.querySelector<HTMLElement>('#uma-logs-body');
+    assert(umaLogsTable);
+
+    // None of the 5 columns of the first row should be filled with 'N/A' or
+    // be empty.
+    const firstRow = getTableRowAsStringArray(umaLogsTable, 0);
+    assertEquals(firstRow.length, 5);
+    firstRow.forEach((el: string) => {
+      assertNotEquals(el, 'N/A');
+      assertGT(el.length, 0);
+    });
+  });
+
+  test('Exported logs should match existing logs', async function() {
+    const exportedLogs = await app.getUmaLogsExportContent();
+    const exportedLogsObj = JSON.parse(exportedLogs);
+
+    // There should be one log in the exported logs.
+    assertEquals(exportedLogsObj.logType, 'UMA');
+    assertTrue(!!exportedLogsObj.logs);
+    assertEquals(exportedLogsObj.logs.length, 1);
+
+    const exportedLog = exportedLogsObj.logs[0];
+    const umaLogsTable =
+        app.shadowRoot!.querySelector<HTMLElement>('#uma-logs-body');
+    assert(umaLogsTable);
+    const firstRow = getTableRowAsStringArray(umaLogsTable, 0);
+
+    // Verify that the exported log matches what is displayed on the page.
+    assertTrue(!!exportedLog.type);
+    assertEquals(exportedLog.type, firstRow[0]);
+
+    assertTrue(!!exportedLog.hash);
+    assertEquals(exportedLog.hash, firstRow[1]);
+
+    assertTrue(!!exportedLog.timestamp);
+    assertEquals(timestampToString(exportedLog.timestamp), firstRow[2]);
+
+    assertTrue(!!exportedLog.size);
+    assertEquals(sizeToString(exportedLog.size), firstRow[3]);
+
+    assertTrue(!!exportedLog.events);
+    assertGT(exportedLog.events.length, 0);
+    assertEquals(getEventsPeekString(exportedLog.events), firstRow[4]);
+
+    // Verify that the exported log has proto data.
+    assertTrue(!!exportedLog.data);
+    assertGT(exportedLog.data.length, 0);
+  });
+});
diff --git a/chrome/test/data/webui/print_preview/advanced_dialog_test.ts b/chrome/test/data/webui/print_preview/advanced_dialog_test.ts
index dc1600f..53591955 100644
--- a/chrome/test/data/webui/print_preview/advanced_dialog_test.ts
+++ b/chrome/test/data/webui/print_preview/advanced_dialog_test.ts
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import {Destination, DestinationOrigin, PrintPreviewAdvancedSettingsDialogElement, PrintPreviewModelElement} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.js';
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
@@ -95,26 +94,22 @@
 
   // Tests that the search box does not appear when there is only one option,
   // and that the vendor item is correctly displayed.
-  test(
-      assert(advanced_dialog_test.TestNames.AdvancedSettings1Option),
-      function() {
-        setupDialog(1);
-        verifyListWithItemCount(1);
-      });
+  test(advanced_dialog_test.TestNames.AdvancedSettings1Option, function() {
+    setupDialog(1);
+    verifyListWithItemCount(1);
+  });
 
   // Tests that the search box appears when there are two options, and that
   // the items are correctly displayed.
-  test(
-      assert(advanced_dialog_test.TestNames.AdvancedSettings2Options),
-      function() {
-        setupDialog(2);
-        verifyListWithItemCount(2);
-      });
+  test(advanced_dialog_test.TestNames.AdvancedSettings2Options, function() {
+    setupDialog(2);
+    verifyListWithItemCount(2);
+  });
 
   // Tests that the advanced settings dialog correctly updates the settings
   // value for vendor items when the apply button is clicked.
   test(
-      assert(advanced_dialog_test.TestNames.AdvancedSettingsApply), function() {
+      advanced_dialog_test.TestNames.AdvancedSettingsApply, function() {
         setupDialog(3);
         setItemValues();
 
@@ -138,7 +133,7 @@
   // Tests that the advanced settings dialog updates the settings value for
   // vendor items if Enter is pressed on a cr-input.
   test(
-      assert(advanced_dialog_test.TestNames.AdvancedSettingsApplyWithEnter),
+      advanced_dialog_test.TestNames.AdvancedSettingsApplyWithEnter,
       function() {
         setupDialog(3);
         setItemValues();
@@ -168,7 +163,7 @@
   // Tests that the advanced settings dialog does not update the settings
   // value for vendor items when the close button is clicked.
   test(
-      assert(advanced_dialog_test.TestNames.AdvancedSettingsClose), function() {
+      advanced_dialog_test.TestNames.AdvancedSettingsClose, function() {
         setupDialog(3);
         setItemValues();
 
@@ -191,8 +186,7 @@
   // Tests that the dialog correctly shows and hides settings based on the
   // value of the search query.
   test(
-      assert(advanced_dialog_test.TestNames.AdvancedSettingsFilter),
-      function() {
+      advanced_dialog_test.TestNames.AdvancedSettingsFilter, function() {
         setupDialog(3);
         const searchBox = dialog.$.searchBox;
         const items = dialog.shadowRoot!.querySelectorAll(
diff --git a/chrome/test/data/webui/print_preview/destination_dropdown_cros_test.ts b/chrome/test/data/webui/print_preview/destination_dropdown_cros_test.ts
index 2cc70323..4383cd4 100644
--- a/chrome/test/data/webui/print_preview/destination_dropdown_cros_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_dropdown_cros_test.ts
@@ -3,10 +3,8 @@
 // found in the LICENSE file.
 
 import {Destination, DestinationOrigin, PrintPreviewDestinationDropdownCrosElement} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.js';
 import {keyDownOn, move} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
@@ -175,7 +173,7 @@
 
         // Key press does not directly update |value| so it is expected for the
         // |value| to not change here in this test.
-        assert(dropdown.value === pdfDestination);
+        assertEquals(dropdown.value, pdfDestination);
 
         // Verify a down press sends the Save to Google Drive destination as the
         // next value selected.
diff --git a/chrome/test/data/webui/print_preview/destination_item_test_cros.ts b/chrome/test/data/webui/print_preview/destination_item_test_cros.ts
index 6ef8592e..cc4d34c 100644
--- a/chrome/test/data/webui/print_preview/destination_item_test_cros.ts
+++ b/chrome/test/data/webui/print_preview/destination_item_test_cros.ts
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import {Destination, DestinationOrigin, NativeLayerCrosImpl, PrinterStatusReason, PrinterStatusSeverity, PrintPreviewDestinationListItemElement} from 'chrome://print/print_preview.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
 import {MockController} from 'chrome://webui-test/mock_controller.js';
@@ -69,7 +70,7 @@
     // ensure iron-media-query uses mock.
     configureMatchMediaMock();
 
-    document.body.innerHTML = `
+    document.body.innerHTML = getTrustedHTML`
           <print-preview-destination-list-item id="listItem">
           </print-preview-destination-list-item>`;
 
diff --git a/chrome/test/data/webui/print_preview/destination_list_test.ts b/chrome/test/data/webui/print_preview/destination_list_test.ts
index 97aaa30c0..8f79b2d 100644
--- a/chrome/test/data/webui/print_preview/destination_list_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_list_test.ts
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import {Destination, DestinationOrigin, PrintPreviewDestinationListElement} from 'chrome://print/print_preview.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
@@ -40,7 +41,7 @@
     ];
 
     // Set up list
-    document.body.innerHTML = `
+    document.body.innerHTML = getTrustedHTML`
           <print-preview-destination-list id="testList" has-action-link=true
               loading-destinations=false list-name="test">
           </print-preview-destination-list>`;
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.ts b/chrome/test/data/webui/print_preview/destination_settings_test.ts
index 0c8c9d7..fe316d2 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.ts
@@ -3,20 +3,20 @@
 // found in the LICENSE file.
 
 import {Destination, DestinationErrorType, DestinationOrigin, DestinationState, DestinationStoreEventType, Error, GooglePromotedDestinationId, LocalDestinationInfo, makeRecentDestination, NativeLayerImpl, NUM_PERSISTED_DESTINATIONS, PrintPreviewDestinationSettingsElement, RecentDestination, State} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {fakeDataBind, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
+// clang-format off
 // <if expr="is_chromeos">
 import {NativeLayerCrosStub, setNativeLayerCrosInstance} from './native_layer_cros_stub.js';
+import {getGoogleDriveDestination} from './print_preview_test_utils.js';
 // </if>
 import {NativeLayerStub} from './native_layer_stub.js';
 import {getDestinations, getSaveAsPdfDestination, setupTestListenerElement} from './print_preview_test_utils.js';
-// <if expr="is_chromeos">
-import {getGoogleDriveDestination} from './print_preview_test_utils.js';
-// </if>
+// clang-format on
 
 const destination_settings_test = {
   suiteName: 'DestinationSettingsTest',
@@ -627,8 +627,9 @@
     const storeDestination =
         destinationSettings.getDestinationStoreForTest().destinations().find(
             d => d.key === destination.key);
+    assert(storeDestination);
     destinationSettings.getDestinationStoreForTest().selectDestination(
-        assert(storeDestination!));
+        storeDestination);
     flush();
   }
 
diff --git a/chrome/test/data/webui/print_preview/dpi_settings_test.ts b/chrome/test/data/webui/print_preview/dpi_settings_test.ts
index de6cc77..ae21840 100644
--- a/chrome/test/data/webui/print_preview/dpi_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/dpi_settings_test.ts
@@ -5,7 +5,7 @@
 import 'chrome://print/print_preview.js';
 
 import {LabelledDpiCapability, PrintPreviewDpiSettingsElement} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {assertDeepEquals, assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
 import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 
@@ -14,13 +14,12 @@
 suite('DpiSettingsTest', function() {
   let dpiSection: PrintPreviewDpiSettingsElement;
 
-  const dpiCapability: LabelledDpiCapability =
-      assert(getCddTemplate('FooPrinter')!.capabilities!.printer!.dpi!) as
-      LabelledDpiCapability;
+  const dpi = getCddTemplate('FooPrinter')!.capabilities!.printer!.dpi;
+  assert(dpi);
 
-  const expectedCapabilityWithLabels: LabelledDpiCapability =
-      assert(getCddTemplate('FooPrinter')!.capabilities!.printer!.dpi!) as
-      LabelledDpiCapability;
+  const dpiCapability: LabelledDpiCapability = dpi as LabelledDpiCapability;
+
+  const expectedCapabilityWithLabels: LabelledDpiCapability = dpiCapability;
 
   expectedCapabilityWithLabels.option.forEach(option => {
     option.name = option.horizontal_dpi.toString() + ' dpi';
diff --git a/chrome/test/data/webui/print_preview/native_layer_cros_stub.ts b/chrome/test/data/webui/print_preview/native_layer_cros_stub.ts
index e7342b1..9ae1415 100644
--- a/chrome/test/data/webui/print_preview/native_layer_cros_stub.ts
+++ b/chrome/test/data/webui/print_preview/native_layer_cros_stub.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {NativeLayerCros, NativeLayerCrosImpl, PrinterSetupResponse, PrinterStatus, PrinterStatusReason, PrinterStatusSeverity, PrintServersConfig} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
@@ -58,9 +58,10 @@
 
   setupPrinter(printerId: string) {
     this.methodCalled('setupPrinter', printerId);
+    assert(this.setupPrinterResponse_);
     return this.shouldRejectPrinterSetup_ ?
-        Promise.reject(assert(this.setupPrinterResponse_!)) :
-        Promise.resolve(assert(this.setupPrinterResponse_!));
+        Promise.reject(this.setupPrinterResponse_) :
+        Promise.resolve(this.setupPrinterResponse_);
   }
 
   grantExtensionPrinterAccess(provisionalId: string) {
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.ts b/chrome/test/data/webui/print_preview/native_layer_stub.ts
index 560f6366..bd73a09 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.ts
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {CapabilitiesResponse, ExtensionDestinationInfo, GooglePromotedDestinationId, LocalDestinationInfo, NativeInitialSettings, NativeLayer, PageLayoutInfo, PrinterType} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
@@ -77,7 +77,8 @@
 
   getInitialSettings() {
     this.methodCalled('getInitialSettings');
-    return Promise.resolve(assert(this.initialSettings_!));
+    assert(this.initialSettings_);
+    return Promise.resolve(this.initialSettings_);
   }
 
   getPrinters(type: PrinterType) {
diff --git a/chrome/test/data/webui/print_preview/number_settings_section_interactive_test.ts b/chrome/test/data/webui/print_preview/number_settings_section_interactive_test.ts
index 683c89b..0507f17b 100644
--- a/chrome/test/data/webui/print_preview/number_settings_section_interactive_test.ts
+++ b/chrome/test/data/webui/print_preview/number_settings_section_interactive_test.ts
@@ -5,6 +5,7 @@
 import 'chrome://print/print_preview.js';
 
 import {PrintPreviewNumberSettingsSectionElement} from 'chrome://print/print_preview.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {triggerInputEvent} from './print_preview_test_utils.js';
@@ -25,7 +26,7 @@
   let numberSettings: PrintPreviewNumberSettingsSectionElement;
 
   setup(function() {
-    document.body.innerHTML = `
+    document.body.innerHTML = getTrustedHTML`
           <print-preview-number-settings-section
               min-value="1" max-value="100" default-value="50"
               current-value="10" hint-message="incorrect value entered"
diff --git a/chrome/test/data/webui/print_preview/number_settings_section_test.ts b/chrome/test/data/webui/print_preview/number_settings_section_test.ts
index e9552076..ffefc6f 100644
--- a/chrome/test/data/webui/print_preview/number_settings_section_test.ts
+++ b/chrome/test/data/webui/print_preview/number_settings_section_test.ts
@@ -5,6 +5,7 @@
 import 'chrome://print/print_preview.js';
 
 import {PrintPreviewNumberSettingsSectionElement} from 'chrome://print/print_preview.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
@@ -27,7 +28,7 @@
   let parentElement: HTMLElement;
 
   setup(function() {
-    document.body.innerHTML = `
+    document.body.innerHTML = getTrustedHTML`
         <div>
           <print-preview-number-settings-section
               min-value="1" max-value="100" default-value="50"
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
index bae61b2..c42d69b 100644
--- a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
+++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
@@ -4,9 +4,9 @@
 
 import {CapabilitiesResponse, Cdd, DEFAULT_MAX_COPIES, Destination, DestinationOrigin, DestinationStore, ExtensionDestinationInfo, GooglePromotedDestinationId, LocalDestinationInfo, MeasurementSystemUnitType, MediaSizeCapability, MediaSizeOption, NativeInitialSettings, VendorCapabilityValueType} from 'chrome://print/print_preview.js';
 import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js';
-import {assert} from 'chrome://resources/js/assert.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
@@ -223,7 +223,9 @@
  */
 export function getDefaultOrientation(device: CapabilitiesResponse): string {
   const options = device.capabilities!.printer.page_orientation!.option;
-  return assert(options!.find(opt => !!opt.is_default)!.type!);
+  const orientation = options!.find(opt => !!opt.is_default)!.type;
+  assert(orientation);
+  return orientation;
 }
 
 interface ExtensionPrinters {
diff --git a/chrome/test/data/webui/print_preview/test_plugin_proxy.ts b/chrome/test/data/webui/print_preview/test_plugin_proxy.ts
index f4f13201..8551abde6 100644
--- a/chrome/test/data/webui/print_preview/test_plugin_proxy.ts
+++ b/chrome/test/data/webui/print_preview/test_plugin_proxy.ts
@@ -4,7 +4,7 @@
 
 import {PdfPlugin} from 'chrome://print/pdf/pdf_scripting_api.js';
 import {PluginProxy, ViewportChangedCallback} from 'chrome://print/print_preview.js';
-import {assert} from 'chrome://resources/js/assert.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 /**
diff --git a/chrome/test/webapps/coverage/coverage_linux.tsv b/chrome/test/webapps/coverage/coverage_linux.tsv
index 564e7282..10e9698 100644
--- a/chrome/test/webapps/coverage/coverage_linux.tsv
+++ b/chrome/test/webapps/coverage/coverage_linux.tsv
@@ -1,5 +1,5 @@
 # This is a generated file.
-# Full coverage: 62%, with partial coverage: 85%
+# Full coverage: 66%, with partial coverage: 89%
 create_shortcut_Standalone_Windowed🌕	launch_from_menu_option_Standalone🌕	check_app_title_Standalone_StandaloneOriginal🌑
 create_shortcut_Standalone_Windowed🌕	launch_from_launch_icon_Standalone🌕	check_app_title_Standalone_StandaloneOriginal🌑
 create_shortcut_Standalone_Windowed🌕	launch_from_chrome_apps_Standalone🌓	check_app_title_Standalone_StandaloneOriginal🌑
@@ -1168,136 +1168,136 @@
 install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
 install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
 install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
 create_shortcut_FileHandler_Windowed🌕	add_file_handling_policy_approval_FileHandler🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
 create_shortcut_FileHandler_Browser🌕	add_file_handling_policy_approval_FileHandler🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
 install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	add_file_handling_policy_approval_FileHandler🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
diff --git a/chrome/test/webapps/coverage/coverage_win.tsv b/chrome/test/webapps/coverage/coverage_win.tsv
index eb76fe2..2e83db7 100644
--- a/chrome/test/webapps/coverage/coverage_win.tsv
+++ b/chrome/test/webapps/coverage/coverage_win.tsv
@@ -1,5 +1,5 @@
 # This is a generated file.
-# Full coverage: 63%, with partial coverage: 87%
+# Full coverage: 68%, with partial coverage: 91%
 create_shortcut_Standalone_Windowed🌕	launch_from_menu_option_Standalone🌕	check_app_title_Standalone_StandaloneOriginal🌑
 create_shortcut_Standalone_Windowed🌕	launch_from_launch_icon_Standalone🌕	check_app_title_Standalone_StandaloneOriginal🌑
 create_shortcut_Standalone_Windowed🌕	launch_from_chrome_apps_Standalone🌓	check_app_title_Standalone_StandaloneOriginal🌑
@@ -1168,136 +1168,136 @@
 install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
 install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
 install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌑	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_FileHandler_Foo🌑	check_site_handles_file_FileHandler_Bar🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌑	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌑
-create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
-install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_FileHandler_Foo🌑	check_site_not_handles_file_FileHandler_Bar🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneFooFile🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleFooFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_MultipleFooFiles🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneBarFile_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_One🌑	check_files_loaded_in_site_FileHandler_OneBarFile🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_MultipleBarFiles_Allow_AskAgain🌕	check_pwa_window_created_FileHandler_Two🌑	check_files_loaded_in_site_FileHandler_MultipleBarFiles🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_Remember🌕	launch_file_expect_no_dialog_FileHandler_OneFooFile🌕	check_pwa_window_created_FileHandler_One🌑
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	check_window_not_created🌕	check_site_handles_file_FileHandler_Foo🌕	check_site_handles_file_FileHandler_Bar🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_AskAgain🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Allow_AskAgain🌕
+create_shortcut_FileHandler_Windowed🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+create_shortcut_FileHandler_Browser🌕	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Windowed_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_WithShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
+install_policy_app_FileHandler_NoShortcut_Browser_WebApp🌓	launch_file_expect_dialog_FileHandler_OneFooFile_Deny_Remember🌕	check_window_not_created🌕	check_site_not_handles_file_FileHandler_Foo🌕	check_site_not_handles_file_FileHandler_Bar🌕
 create_shortcut_FileHandler_Windowed🌕	add_file_handling_policy_approval_FileHandler🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
 create_shortcut_FileHandler_Browser🌕	add_file_handling_policy_approval_FileHandler🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
 install_policy_app_FileHandler_WithShortcut_Windowed_WebApp🌓	add_file_handling_policy_approval_FileHandler🌑	launch_file_expect_no_dialog_FileHandler_OneFooFile🌑	check_pwa_window_created_FileHandler_One🌑
diff --git a/chrome/test/webapps/data/framework_supported_actions.csv b/chrome/test/webapps/data/framework_supported_actions.csv
index f35e27e9..f410897c 100644
--- a/chrome/test/webapps/data/framework_supported_actions.csv
+++ b/chrome/test/webapps/data/framework_supported_actions.csv
@@ -37,6 +37,8 @@
 install_menu_option,                                   🌕, 🌕,  🌕,   🌕,
 install_omnibox_icon,                                  🌕, 🌕,  🌕,   🌕,
 install_policy_app,                                    🌓, 🌓,  🌓,   🌓,
+launch_file_expect_dialog,                             🌑, 🌕,  🌕,   🌑,
+launch_file_expect_no_dialog,                          🌑, 🌕,  🌕,   🌑,
 launch_from_chrome_apps,                               🌓, 🌓,  🌓,   🌓,
 launch_from_launch_icon,                               🌕, 🌕,  🌕,   🌕,
 launch_from_menu_option,                               🌕, 🌕,  🌕,   🌕,
diff --git a/chrome/updater/unittest_util.cc b/chrome/updater/unittest_util.cc
index 57dd0d330..fd80c9da 100644
--- a/chrome/updater/unittest_util.cc
+++ b/chrome/updater/unittest_util.cc
@@ -144,6 +144,11 @@
 }
 
 base::FilePath StartProcmonLogging() {
+  if (base::win::GetVersion() <= base::win::Version::WIN7) {
+    LOG(WARNING) << __func__ << ": skipping procmon logging on Win7.";
+    return {};
+  }
+
   if (!::IsUserAnAdmin()) {
     LOG(WARNING) << __func__
                  << ": user is not an admin, skipping procmon logging";
diff --git a/chromecast/media/cma/backend/mixer/mixer_pipeline.cc b/chromecast/media/cma/backend/mixer/mixer_pipeline.cc
index d542c5bf..267508c 100644
--- a/chromecast/media/cma/backend/mixer/mixer_pipeline.cc
+++ b/chromecast/media/cma/backend/mixer/mixer_pipeline.cc
@@ -56,20 +56,22 @@
 
   // Create "stream" processor groups:
   for (auto& stream_pipeline : config->GetStreamPipelines()) {
-    const base::Value* device_ids = stream_pipeline.stream_types;
+    const base::Value::List& device_ids =
+        stream_pipeline.stream_types->GetList();
     int input_channels = (stream_pipeline.num_input_channels.has_value()
                               ? stream_pipeline.num_input_channels.value()
                               : expected_input_channels);
-    const std::string& name = device_ids->GetListDeprecated()[0].GetString();
+    DCHECK(!device_ids.empty());
+    DCHECK(device_ids[0].is_string());
+    const std::string& name = device_ids[0].GetString();
     LOG(INFO) << input_channels << " input channels to '" << name << "' group";
 
-    DCHECK(!device_ids->GetListDeprecated().empty());
-    DCHECK(device_ids->GetListDeprecated()[0].is_string());
     filter_groups_.push_back(std::make_unique<FilterGroup>(
         input_channels, name, stream_pipeline.prerender_pipeline.Clone(),
         &stream_pipeline.pipeline, factory, stream_pipeline.volume_limits));
 
-    if (!SetGroupDeviceIds(device_ids, filter_groups_.back().get())) {
+    if (!SetGroupDeviceIds(stream_pipeline.stream_types,
+                           filter_groups_.back().get())) {
       return false;
     }
 
@@ -138,7 +140,7 @@
   DCHECK(filter_group);
   DCHECK(ids->is_list());
 
-  for (const base::Value& stream_type_val : ids->GetListDeprecated()) {
+  for (const base::Value& stream_type_val : ids->GetList()) {
     DCHECK(stream_type_val.is_string());
     const std::string& stream_type = stream_type_val.GetString();
     if (!IsOutputDeviceId(stream_type)) {
diff --git a/chromecast/media/cma/backend/mixer/mock_post_processor_factory.cc b/chromecast/media/cma/backend/mixer/mock_post_processor_factory.cc
index 2330311..a843325 100644
--- a/chromecast/media/cma/backend/mixer/mock_post_processor_factory.cc
+++ b/chromecast/media/cma/backend/mixer/mock_post_processor_factory.cc
@@ -31,31 +31,27 @@
   }
 
   // Parse |filter_description_list| for parameters.
-  for (const base::Value& elem : filter_description_list->GetListDeprecated()) {
+  for (const base::Value& elem : filter_description_list->GetList()) {
     CHECK(elem.is_dict());
-    const base::Value* processor_val =
-        elem.FindKeyOfType("processor", base::Value::Type::STRING);
-    CHECK(processor_val);
-    std::string solib = processor_val->GetString();
+    const std::string* solib = elem.GetDict().FindString("processor");
+    CHECK(solib);
 
-    if (solib == "delay.so") {
-      const base::Value* processor_config_dict =
-          elem.FindKeyOfType("config", base::Value::Type::DICTIONARY);
+    if (*solib == "delay.so") {
+      const base::Value::Dict* processor_config_dict =
+          elem.GetDict().FindDict("config");
       CHECK(processor_config_dict);
-      const base::Value* delay_val = processor_config_dict->FindKeyOfType(
-          "delay", base::Value::Type::INTEGER);
-      CHECK(delay_val);
-      rendering_delay_frames_ += delay_val->GetInt();
-      const base::Value* ringing_val = processor_config_dict->FindKeyOfType(
-          "ringing", base::Value::Type::BOOLEAN);
-      if (ringing_val) {
-        ringing_ = ringing_val->GetBool();
+      absl::optional<int> delay = processor_config_dict->FindInt("delay");
+      CHECK(delay.has_value());
+      rendering_delay_frames_ += *delay;
+      absl::optional<bool> ringing = processor_config_dict->FindBool("ringing");
+      if (ringing.has_value()) {
+        ringing_ = *ringing;
       }
 
-      const base::Value* output_ch_val = processor_config_dict->FindKeyOfType(
-          "output_channels", base::Value::Type::INTEGER);
-      if (output_ch_val) {
-        num_output_channels_ = output_ch_val->GetInt();
+      absl::optional<int> output_ch =
+          processor_config_dict->FindInt("output_channels");
+      if (output_ch.has_value()) {
+        num_output_channels_ = *output_ch;
       }
     }
   }
diff --git a/chromecast/media/cma/backend/mixer/post_processing_pipeline_impl.cc b/chromecast/media/cma/backend/mixer/post_processing_pipeline_impl.cc
index c699633..7ee5f36 100644
--- a/chromecast/media/cma/backend/mixer/post_processing_pipeline_impl.cc
+++ b/chromecast/media/cma/backend/mixer/post_processing_pipeline_impl.cc
@@ -57,15 +57,17 @@
   }
 
   LOG(INFO) << "Create pipeline for " << channels << " input channels";
-  for (const base::Value& processor_description_dict :
-       filter_description_list->GetListDeprecated()) {
-    DCHECK(processor_description_dict.is_dict());
+  for (const base::Value& processor_description_value :
+       filter_description_list->GetList()) {
+    DCHECK(processor_description_value.is_dict());
+    const base::Value::Dict& processor_description_dict =
+        processor_description_value.GetDict();
 
     std::string processor_name;
-    const base::Value* name_val = processor_description_dict.FindKeyOfType(
-        kJsonKeyName, base::Value::Type::STRING);
+    const std::string* name_val =
+        processor_description_dict.FindString(kJsonKeyName);
     if (name_val) {
-      processor_name = name_val->GetString();
+      processor_name = *name_val;
     }
 
     if (!processor_name.empty()) {
@@ -81,23 +83,22 @@
     std::string library_path;
 
     // Keys for AudioPostProcessor2:
-    const base::Value* library_val = processor_description_dict.FindKeyOfType(
-        kJsonKeyLib, base::Value::Type::STRING);
+    const std::string* library_val =
+        processor_description_dict.FindString(kJsonKeyLib);
     if (library_val) {
-      library_path = library_val->GetString();
+      library_path = *library_val;
     } else {
       // Keys for AudioPostProcessor
       // TODO(bshaya): Remove when AudioPostProcessor support is removed.
-      library_val = processor_description_dict.FindKeyOfType(
-          kJsonKeyProcessor, base::Value::Type::STRING);
+      library_val = processor_description_dict.FindString(kJsonKeyProcessor);
       DCHECK(library_val) << "Post processor description is missing key "
                           << kJsonKeyLib;
-      library_path = library_val->GetString();
+      library_path = *library_val;
     }
 
     std::string processor_config_string;
     const base::Value* processor_config_val =
-        processor_description_dict.FindKey(kJsonKeyConfig);
+        processor_description_dict.Find(kJsonKeyConfig);
     if (processor_config_val) {
       DCHECK(processor_config_val->is_dict() ||
              processor_config_val->is_string());
diff --git a/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc b/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc
index fe4b2bb..d8d8aa8 100644
--- a/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc
+++ b/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc
@@ -29,16 +29,14 @@
 const char kStreamsKey[] = "streams";
 const char kVolumeLimitsKey[] = "volume_limits";
 
-void SplitPipeline(const base::Value* processors_list,
+void SplitPipeline(const base::Value::List& processors_list,
                    base::Value& prerender_pipeline,
                    base::Value& postrender_pipeline) {
-  DCHECK(processors_list->is_list());
   DCHECK(prerender_pipeline.is_list());
   DCHECK(postrender_pipeline.is_list());
 
   bool has_render = false;
-  for (const base::Value& processor_description_dict :
-       processors_list->GetList()) {
+  for (const base::Value& processor_description_dict : processors_list) {
     DCHECK(processor_description_dict.is_dict());
     std::string processor_name;
     const base::Value* name_val = processor_description_dict.FindKeyOfType(
@@ -51,8 +49,7 @@
 
   bool is_prerender = has_render;
 
-  for (const base::Value& processor_description_dict :
-       processors_list->GetList()) {
+  for (const base::Value& processor_description_dict : processors_list) {
     const base::Value* name_val = processor_description_dict.FindKeyOfType(
         kNameKey, base::Value::Type::STRING);
     if (name_val && name_val->GetString() == kRenderNameTag) {
@@ -128,36 +125,37 @@
   if (!postprocessor_config_) {
     return descriptors;
   }
-  const base::Value* pipelines_list = postprocessor_config_->FindKeyOfType(
-      kOutputStreamsKey, base::Value::Type::LIST);
+  const base::Value::List* pipelines_list =
+      postprocessor_config_->GetDict().FindList(kOutputStreamsKey);
   if (!pipelines_list) {
     LOG(WARNING) << "No post-processors found for streams (key = "
                  << kOutputStreamsKey
                  << ").\n No stream-specific processing will occur.";
     return descriptors;
   }
-  for (const base::Value& pipeline_description_dict :
-       pipelines_list->GetListDeprecated()) {
-    CHECK(pipeline_description_dict.is_dict());
+  for (const base::Value& pipeline_description_val : *pipelines_list) {
+    CHECK(pipeline_description_val.is_dict());
+    const base::Value::Dict& pipeline_description_dict =
+        pipeline_description_val.GetDict();
 
-    const base::Value* processors_list =
-        pipeline_description_dict.FindKeyOfType(kProcessorsKey,
-                                                base::Value::Type::LIST);
+    const base::Value::List* processors_list =
+        pipeline_description_dict.FindList(kProcessorsKey);
     CHECK(processors_list);
 
     base::Value prerender_pipeline(base::Value::Type::LIST);
     base::Value postrender_pipeline(base::Value::Type::LIST);
-    SplitPipeline(processors_list, prerender_pipeline, postrender_pipeline);
+    SplitPipeline(*processors_list, prerender_pipeline, postrender_pipeline);
 
-    const base::Value* streams_list = pipeline_description_dict.FindKeyOfType(
-        kStreamsKey, base::Value::Type::LIST);
-    CHECK(streams_list);
+    const base::Value* streams_list =
+        pipeline_description_dict.Find(kStreamsKey);
+    CHECK(streams_list && streams_list->is_list());
 
     auto num_input_channels =
-        pipeline_description_dict.FindIntKey(kNumInputChannelsKey);
+        pipeline_description_dict.FindInt(kNumInputChannelsKey);
 
-    const base::Value* volume_limits = pipeline_description_dict.FindKeyOfType(
-        kVolumeLimitsKey, base::Value::Type::DICTIONARY);
+    const base::Value* volume_limits =
+        pipeline_description_dict.Find(kVolumeLimitsKey);
+    CHECK(!volume_limits || volume_limits->is_list());
 
     descriptors.emplace_back(std::move(prerender_pipeline),
                              std::move(postrender_pipeline), streams_list,
@@ -186,13 +184,13 @@
                                     nullptr, absl::nullopt, nullptr);
   }
 
-  const base::Value* processors_list =
-      stream_dict->FindKeyOfType(kProcessorsKey, base::Value::Type::LIST);
+  const base::Value::List* processors_list =
+      stream_dict->GetDict().FindList(kProcessorsKey);
   CHECK(processors_list);
 
   base::Value prerender_pipeline(base::Value::Type::LIST);
   base::Value postrender_pipeline(base::Value::Type::LIST);
-  SplitPipeline(processors_list, prerender_pipeline, postrender_pipeline);
+  SplitPipeline(*processors_list, prerender_pipeline, postrender_pipeline);
 
   const base::Value* streams_list =
       stream_dict->FindKeyOfType(kStreamsKey, base::Value::Type::LIST);
diff --git a/chromecast/renderer/BUILD.gn b/chromecast/renderer/BUILD.gn
index 0561289..e16152a 100644
--- a/chromecast/renderer/BUILD.gn
+++ b/chromecast/renderer/BUILD.gn
@@ -21,7 +21,6 @@
     "cast_demo_bindings.h",
     "cast_url_loader_throttle_provider.cc",
     "cast_url_loader_throttle_provider.h",
-    "cast_url_rewrite_rules_store.h",
     "cast_websocket_handshake_throttle_provider.cc",
     "cast_websocket_handshake_throttle_provider.h",
     "cast_window_manager_bindings.cc",
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index c9005c8..0f528b69 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -22,7 +22,7 @@
 #include "chromecast/renderer/cast_websocket_handshake_throttle_provider.h"
 #include "chromecast/renderer/media/key_systems_cast.h"
 #include "chromecast/renderer/media/media_caps_observer_impl.h"
-#include "components/cast_receiver/renderer/public/url_rewrite_rules_provider.h"
+#include "components/cast_receiver/renderer/public/content_renderer_client_mixins.h"
 #include "components/media_control/renderer/media_playback_options.h"
 #include "components/network_hints/renderer/web_prescient_networking_impl.h"
 #include "components/on_load_script_injector/renderer/on_load_script_injector.h"
@@ -85,7 +85,9 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 CastContentRendererClient::CastContentRendererClient()
-    : supported_profiles_(
+    : cast_receiver_mixins_(cast_receiver::ContentRendererClientMixins::Create(
+          base::BindRepeating(&IsCorsExemptHeader))),
+      supported_profiles_(
           std::make_unique<media::SupportedCodecProfileLevelsMemo>()),
       activity_url_filter_manager_(
           std::make_unique<CastActivityUrlFilterManager>()) {
@@ -138,6 +140,8 @@
     content::RenderFrame* render_frame) {
   DCHECK(render_frame);
 
+  cast_receiver_mixins_->RenderFrameCreated(*render_frame);
+
   // Lifetime is tied to |render_frame| via content::RenderFrameObserver.
   if (render_frame->IsMainFrame()) {
     main_frame_feature_manager_on_associated_interface_ =
@@ -145,11 +149,6 @@
   } else {
     new FeatureManagerOnAssociatedInterface(render_frame);
   }
-  new media_control::MediaPlaybackOptions(render_frame);
-
-  // Add script injection support to the RenderFrame, used by Cast platform
-  // APIs. The injector's lifetime is bound to the RenderFrame's lifetime.
-  new on_load_script_injector::OnLoadScriptInjector(render_frame);
 
   if (!app_media_capabilities_observer_receiver_.is_bound()) {
     mojo::Remote<mojom::ApplicationMediaCapabilities> app_media_capabilities;
@@ -160,16 +159,6 @@
   }
 
   activity_url_filter_manager_->OnRenderFrameCreated(render_frame);
-
-  // |base::Unretained| is safe here since the callback is triggered before the
-  // destruction of UrlRewriteRulesProvider by which point
-  // CastContentRendererClient should be alive.
-  url_rewrite_rules_providers_.emplace(
-      render_frame->GetRoutingID(),
-      std::make_unique<cast_receiver::UrlRewriteRulesProvider>(
-          render_frame,
-          base::BindOnce(&CastContentRendererClient::OnRenderFrameRemoved,
-                         base::Unretained(this))));
 }
 
 void CastContentRendererClient::RunScriptsAtDocumentStart(
@@ -290,7 +279,8 @@
     content::RenderFrame* render_frame,
     bool render_frame_has_played_media_before,
     base::OnceClosure closure) {
-  return RunWhenInForeground(render_frame, std::move(closure));
+  return cast_receiver_mixins_->DeferMediaLoad(*render_frame,
+                                               std::move(closure));
 }
 
 std::unique_ptr<::media::Demuxer>
@@ -307,15 +297,6 @@
   return nullptr;
 }
 
-bool CastContentRendererClient::RunWhenInForeground(
-    content::RenderFrame* render_frame,
-    base::OnceClosure closure) {
-  auto* playback_options =
-      media_control::MediaPlaybackOptions::Get(render_frame);
-  DCHECK(playback_options);
-  return playback_options->RunWhenInForeground(std::move(closure));
-}
-
 bool CastContentRendererClient::IsIdleMediaSuspendEnabled() {
   return false;
 }
@@ -343,9 +324,10 @@
 std::unique_ptr<blink::URLLoaderThrottleProvider>
 CastContentRendererClient::CreateURLLoaderThrottleProvider(
     blink::URLLoaderThrottleProviderType type) {
-  return std::make_unique<CastURLLoaderThrottleProvider>(
-      type, activity_url_filter_manager(), this,
-      base::BindRepeating(&IsCorsExemptHeader));
+  auto throttle_provider = std::make_unique<CastURLLoaderThrottleProvider>(
+      type, activity_url_filter_manager());
+  return cast_receiver_mixins_->ExtendURLLoaderThrottleProvider(
+      std::move(throttle_provider));
 }
 
 absl::optional<::media::AudioRendererAlgorithmParameters>
@@ -367,27 +349,5 @@
 #endif
 }
 
-scoped_refptr<url_rewrite::UrlRequestRewriteRules>
-CastContentRendererClient::GetUrlRequestRewriteRules(
-    int render_frame_id) const {
-  auto it = url_rewrite_rules_providers_.find(render_frame_id);
-  if (it == url_rewrite_rules_providers_.end()) {
-    LOG(WARNING)
-        << "Can't find the URL rewrite rules provider for render frame: "
-        << render_frame_id;
-    return nullptr;
-  }
-  return it->second->GetCachedRules();
-}
-
-void CastContentRendererClient::OnRenderFrameRemoved(int render_frame_id) {
-  size_t result = url_rewrite_rules_providers_.erase(render_frame_id);
-  if (result != 1U) {
-    LOG(WARNING)
-        << "Can't find the URL rewrite rules provider for render frame: "
-        << render_frame_id;
-  }
-}
-
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/renderer/cast_content_renderer_client.h b/chromecast/renderer/cast_content_renderer_client.h
index 45a143c..78cb57d 100644
--- a/chromecast/renderer/cast_content_renderer_client.h
+++ b/chromecast/renderer/cast_content_renderer_client.h
@@ -13,7 +13,6 @@
 #include "chromecast/chromecast_buildflags.h"
 #include "chromecast/common/mojom/application_media_capabilities.mojom.h"
 #include "chromecast/renderer/cast_activity_url_filter_manager.h"
-#include "chromecast/renderer/cast_url_rewrite_rules_store.h"
 #include "chromecast/renderer/feature_manager_on_associated_interface.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "media/base/audio_codecs.h"
@@ -21,7 +20,7 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 
 namespace cast_receiver {
-class UrlRewriteRulesProvider;
+class ContentRendererClientMixins;
 }  // namespace cast_receiver
 
 namespace chromecast {
@@ -39,8 +38,7 @@
 
 class CastContentRendererClient
     : public content::ContentRendererClient,
-      public mojom::ApplicationMediaCapabilitiesObserver,
-      public CastURLRewriteRulesStore {
+      public mojom::ApplicationMediaCapabilitiesObserver {
  public:
   // Creates an implementation of CastContentRendererClient. Platform should
   // link in an implementation as needed.
@@ -84,11 +82,6 @@
  protected:
   CastContentRendererClient();
 
-  // Returns true if running is deferred until in foreground; false if running
-  // occurs immediately.
-  virtual bool RunWhenInForeground(content::RenderFrame* render_frame,
-                                   base::OnceClosure closure);
-
   CastActivityUrlFilterManager* activity_url_filter_manager() {
     return activity_url_filter_manager_.get();
   }
@@ -103,15 +96,11 @@
   void OnSupportedBitstreamAudioCodecsChanged(
       const BitstreamAudioCodecsInfo& info) override;
 
-  // CastURLRewriteRulesStore implementation:
-  scoped_refptr<url_rewrite::UrlRequestRewriteRules> GetUrlRequestRewriteRules(
-      int render_frame_id) const override;
-
   bool CheckSupportedBitstreamAudioCodec(::media::AudioCodec codec,
                                          bool check_spatial_rendering);
 
-  // Called when a render frame is removed.
-  void OnRenderFrameRemoved(int render_frame_id);
+  std::unique_ptr<cast_receiver::ContentRendererClientMixins>
+      cast_receiver_mixins_;
 
   std::unique_ptr<media::MediaCapsObserverImpl> media_caps_observer_;
   std::unique_ptr<media::SupportedCodecProfileLevelsMemo> supported_profiles_;
@@ -127,11 +116,6 @@
 
   BitstreamAudioCodecsInfo supported_bitstream_audio_codecs_info_;
 
-  // TODO(crbug.com/1382903): Clean this up by moving RenderFrame details
-  // into cast_receiver.
-  base::flat_map<int /* render_frame_id */,
-                 std::unique_ptr<cast_receiver::UrlRewriteRulesProvider>>
-      url_rewrite_rules_providers_;
   std::unique_ptr<CastActivityUrlFilterManager> activity_url_filter_manager_;
 };
 
diff --git a/chromecast/renderer/cast_url_loader_throttle_provider.cc b/chromecast/renderer/cast_url_loader_throttle_provider.cc
index b412b0f33..f40dbe0 100644
--- a/chromecast/renderer/cast_url_loader_throttle_provider.cc
+++ b/chromecast/renderer/cast_url_loader_throttle_provider.cc
@@ -11,7 +11,6 @@
 #include "base/memory/ptr_util.h"
 #include "chromecast/common/activity_filtering_url_loader_throttle.h"
 #include "chromecast/renderer/cast_activity_url_filter_manager.h"
-#include "chromecast/renderer/cast_url_rewrite_rules_store.h"
 #include "components/url_rewrite/common/url_loader_throttle.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 
@@ -19,16 +18,8 @@
 
 CastURLLoaderThrottleProvider::CastURLLoaderThrottleProvider(
     blink::URLLoaderThrottleProviderType type,
-    CastActivityUrlFilterManager* url_filter_manager,
-    CastURLRewriteRulesStore* url_rewrite_rules_store,
-    base::RepeatingCallback<bool(base::StringPiece)>
-        is_cors_exempt_header_callback)
-    : type_(type),
-      cast_activity_url_filter_manager_(url_filter_manager),
-      url_rewrite_rules_store_(url_rewrite_rules_store),
-      is_cors_exempt_header_callback_(
-          std::move(is_cors_exempt_header_callback)) {
-  DCHECK(url_rewrite_rules_store_);
+    CastActivityUrlFilterManager* url_filter_manager)
+    : type_(type), cast_activity_url_filter_manager_(url_filter_manager) {
   DETACH_FROM_THREAD(thread_checker_);
 }
 
@@ -40,9 +31,7 @@
     const chromecast::CastURLLoaderThrottleProvider& other)
     : type_(other.type_),
       cast_activity_url_filter_manager_(
-          other.cast_activity_url_filter_manager_),
-      url_rewrite_rules_store_(other.url_rewrite_rules_store_),
-      is_cors_exempt_header_callback_(other.is_cors_exempt_header_callback_) {
+          other.cast_activity_url_filter_manager_) {
   DETACH_FROM_THREAD(thread_checker_);
 }
 
@@ -70,13 +59,6 @@
     }
   }
 
-  auto rules =
-      url_rewrite_rules_store_->GetUrlRequestRewriteRules(render_frame_id);
-  if (rules) {
-    throttles.emplace_back(std::make_unique<url_rewrite::URLLoaderThrottle>(
-        rules, is_cors_exempt_header_callback_));
-  }
-
   return throttles;
 }
 
diff --git a/chromecast/renderer/cast_url_loader_throttle_provider.h b/chromecast/renderer/cast_url_loader_throttle_provider.h
index 55d48588..6e1d3e6 100644
--- a/chromecast/renderer/cast_url_loader_throttle_provider.h
+++ b/chromecast/renderer/cast_url_loader_throttle_provider.h
@@ -16,16 +16,12 @@
 
 namespace chromecast {
 class CastActivityUrlFilterManager;
-class CastURLRewriteRulesStore;
 
 class CastURLLoaderThrottleProvider : public blink::URLLoaderThrottleProvider {
  public:
   CastURLLoaderThrottleProvider(
       blink::URLLoaderThrottleProviderType type,
-      CastActivityUrlFilterManager* url_filter_manager,
-      CastURLRewriteRulesStore* url_rewrite_rules_store,
-      base::RepeatingCallback<bool(base::StringPiece)>
-          is_cors_exempt_header_callback);
+      CastActivityUrlFilterManager* url_filter_manager);
   ~CastURLLoaderThrottleProvider() override;
   CastURLLoaderThrottleProvider& operator=(
       const CastURLLoaderThrottleProvider&) = delete;
@@ -44,9 +40,6 @@
 
   blink::URLLoaderThrottleProviderType type_;
   CastActivityUrlFilterManager* const cast_activity_url_filter_manager_;
-  CastURLRewriteRulesStore* const url_rewrite_rules_store_;
-  base::RepeatingCallback<bool(base::StringPiece)>
-      is_cors_exempt_header_callback_;
 
   THREAD_CHECKER(thread_checker_);
 };
diff --git a/chromecast/renderer/cast_url_rewrite_rules_store.h b/chromecast/renderer/cast_url_rewrite_rules_store.h
deleted file mode 100644
index aa90f18..0000000
--- a/chromecast/renderer/cast_url_rewrite_rules_store.h
+++ /dev/null
@@ -1,25 +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 CHROMECAST_RENDERER_CAST_URL_REWRITE_RULES_STORE_H_
-#define CHROMECAST_RENDERER_CAST_URL_REWRITE_RULES_STORE_H_
-
-#include "base/memory/scoped_refptr.h"
-
-#include "components/url_rewrite/common/url_request_rewrite_rules.h"
-
-namespace chromecast {
-
-class CastURLRewriteRulesStore {
- public:
-  virtual scoped_refptr<url_rewrite::UrlRequestRewriteRules>
-  GetUrlRequestRewriteRules(int render_frame_id) const = 0;
-
- protected:
-  virtual ~CastURLRewriteRulesStore() = default;
-};
-
-}  // namespace chromecast
-
-#endif  // CHROMECAST_RENDERER_CAST_URL_REWRITE_RULES_STORE_H_
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc
index 7637f54..7d0b7e6 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -190,19 +190,6 @@
   return "";
 }
 
-std::string GetCreditCardTypeSuffix(
-    AutofillClient::PaymentsRpcCardType card_type) {
-  switch (card_type) {
-    case AutofillClient::PaymentsRpcCardType::kServerCard:
-      return ".ServerCard";
-    case AutofillClient::PaymentsRpcCardType::kVirtualCard:
-      return ".VirtualCard";
-    case AutofillClient::PaymentsRpcCardType::kUnknown:
-      NOTREACHED();
-      return std::string();
-  }
-}
-
 }  // namespace
 
 // First, translates |field_type| to the corresponding logical |group| from
@@ -973,7 +960,7 @@
   base::UmaHistogramLongTimes("Autofill.BetterAuth.CardUnmaskDuration.Fido",
                               duration);
   base::UmaHistogramLongTimes("Autofill.BetterAuth.CardUnmaskDuration.Fido" +
-                                  GetCreditCardTypeSuffix(card_type) +
+                                  GetHistogramStringForCardType(card_type) +
                                   PaymentsRpcResultToMetricsSuffix(result),
                               duration);
 }
@@ -994,7 +981,8 @@
 void AutofillMetrics::LogServerCardUnmaskAttempt(
     AutofillClient::PaymentsRpcCardType card_type) {
   base::UmaHistogramBoolean("Autofill.ServerCardUnmask" +
-                                GetCreditCardTypeSuffix(card_type) + ".Attempt",
+                                GetHistogramStringForCardType(card_type) +
+                                ".Attempt",
                             true);
 }
 
@@ -1020,7 +1008,7 @@
   }
 
   base::UmaHistogramEnumeration("Autofill.ServerCardUnmask" +
-                                    GetCreditCardTypeSuffix(card_type) +
+                                    GetHistogramStringForCardType(card_type) +
                                     ".Result" + flow_type_suffix,
                                 unmask_result);
 }
@@ -1029,7 +1017,7 @@
 void AutofillMetrics::LogServerCardUnmaskFormSubmission(
     AutofillClient::PaymentsRpcCardType card_type) {
   base::UmaHistogramBoolean("Autofill.ServerCardUnmask" +
-                                GetCreditCardTypeSuffix(card_type) +
+                                GetHistogramStringForCardType(card_type) +
                                 ".FormSubmission",
                             true);
 }
@@ -3399,7 +3387,7 @@
   return result_suffix;
 }
 
-// // static
+// static
 void AutofillMetrics::LogNumericQuantityCollidesWithServerPrediction(
     bool collision) {
   base::UmaHistogramBoolean(
@@ -3415,4 +3403,35 @@
       accepted);
 }
 
+// static
+std::string AutofillMetrics::GetHistogramStringForCardType(
+    absl::variant<AutofillClient::PaymentsRpcCardType, CreditCard::RecordType>
+        card_type) {
+  if (absl::holds_alternative<AutofillClient::PaymentsRpcCardType>(card_type)) {
+    switch (absl::get<AutofillClient::PaymentsRpcCardType>(card_type)) {
+      case AutofillClient::PaymentsRpcCardType::kServerCard:
+        return ".ServerCard";
+      case AutofillClient::PaymentsRpcCardType::kVirtualCard:
+        return ".VirtualCard";
+      case AutofillClient::PaymentsRpcCardType::kUnknown:
+        NOTREACHED();
+        break;
+    }
+  } else if (absl::holds_alternative<CreditCard::RecordType>(card_type)) {
+    switch (absl::get<CreditCard::RecordType>(card_type)) {
+      case CreditCard::FULL_SERVER_CARD:
+      case CreditCard::MASKED_SERVER_CARD:
+        return ".ServerCard";
+      case CreditCard::VIRTUAL_CARD:
+        return ".VirtualCard";
+      case CreditCard::LOCAL_CARD:
+        // We do not offer CVC auth for local cards.
+        NOTREACHED();
+        break;
+    }
+  }
+
+  return "";
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.h b/components/autofill/core/browser/metrics/autofill_metrics.h
index 6ab71e6..a5eb69f 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.h
+++ b/components/autofill/core/browser/metrics/autofill_metrics.h
@@ -22,6 +22,7 @@
 #include "components/autofill/core/browser/autofill_profile_import_process.h"
 #include "components/autofill/core/browser/autofill_progress_dialog_type.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/form_types.h"
 #include "components/autofill/core/browser/metrics/form_events/form_events.h"
@@ -1808,6 +1809,13 @@
   static void LogAcceptedFilledFieldWithNumericQuantityHeuristicPrediction(
       bool accepted);
 
+  // Returns the histogram string for the passed in
+  // `AutofillClient::PaymentsRpcCardType` or `CreditCard::RecordType`, starting
+  // with a period.
+  static std::string GetHistogramStringForCardType(
+      absl::variant<AutofillClient::PaymentsRpcCardType, CreditCard::RecordType>
+          card_type);
+
   // Logs the context menu impressions based on the autofill type as well as
   // based on the autocomplete type.
   static void LogContextMenuImpressions(ServerFieldType field_type,
diff --git a/components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.cc b/components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.cc
index 874a94a1..e52fa49e 100644
--- a/components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.cc
+++ b/components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.cc
@@ -7,26 +7,31 @@
 #include <string>
 
 #include "base/metrics/histogram_functions.h"
+#include "components/autofill/core/browser/metrics/autofill_metrics.h"
 
 namespace autofill::autofill_metrics {
 
 void LogCvcAuthAttempt(CreditCard::RecordType card_type) {
-  std::string card_type_histogram_string;
-  switch (card_type) {
-    case CreditCard::FULL_SERVER_CARD:
-    case CreditCard::MASKED_SERVER_CARD:
-      card_type_histogram_string = ".ServerCard";
-      break;
-    case CreditCard::VIRTUAL_CARD:
-      card_type_histogram_string = ".VirtualCard";
-      break;
-    case CreditCard::LOCAL_CARD:
-      // We do not offer CVC auth for local cards.
-      NOTREACHED();
-      return;
-  }
+  std::string card_type_histogram_string =
+      AutofillMetrics::GetHistogramStringForCardType(card_type);
   base::UmaHistogramBoolean(
       "Autofill.CvcAuth" + card_type_histogram_string + ".Attempt", true);
 }
 
+void LogCvcAuthResult(CreditCard::RecordType card_type, CvcAuthEvent event) {
+  std::string card_type_histogram_string =
+      AutofillMetrics::GetHistogramStringForCardType(card_type);
+  base::UmaHistogramEnumeration(
+      "Autofill.CvcAuth" + card_type_histogram_string + ".Result", event);
+}
+
+void LogCvcAuthRetryableError(CreditCard::RecordType card_type,
+                              CvcAuthEvent event) {
+  std::string card_type_histogram_string =
+      AutofillMetrics::GetHistogramStringForCardType(card_type);
+  base::UmaHistogramEnumeration(
+      "Autofill.CvcAuth" + card_type_histogram_string + ".RetryableError",
+      event);
+}
+
 }  // namespace autofill::autofill_metrics
diff --git a/components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h b/components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h
index 9400e7c2..669e44c 100644
--- a/components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h
+++ b/components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h
@@ -10,9 +10,45 @@
 namespace autofill::autofill_metrics {
 
 // Card unmasking CVC authentication-related metrics.
+// CVC authentication-related events.
+enum class CvcAuthEvent {
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+
+  // Unknown result. Should not happen.
+  kUnknown = 0,
+  // The CVC auth succeeded.
+  kSuccess = 1,
+  // The CVC auth failed because the flow was cancelled.
+  kFlowCancelled = 2,
+  // The CVC auth failed because the UnmaskCard request failed due to
+  // authentication errors.
+  kUnmaskCardAuthError = 3,
+  // The CVC auth failed because the UnmaskCard request failed due to virtual
+  // card retrieval errors.
+  kUnmaskCardVirtualCardRetrievalError = 4,
+  // The flow failed for technical reasons, such as closing the page or lack of
+  // network connection.
+  kGenericError = 5,
+  // The CVC auth failed temporarily because the CVC didn't match the
+  // expected value. This is a retryable error.
+  kTemporaryErrorCvcMismatch = 6,
+  // The CVC auth failed temporarily because the card used was expired. This is
+  // a retryable error.
+  kTemporaryErrorExpiredCard = 7,
+  kMaxValue = kTemporaryErrorExpiredCard
+};
+
 // Logs when a CVC authentication starts.
 void LogCvcAuthAttempt(CreditCard::RecordType card_type);
 
+// Logs when a CVC authentication finishes.
+void LogCvcAuthResult(CreditCard::RecordType card_type, CvcAuthEvent event);
+
+// Logs when a retryable error occurs in the CVC authentication flow.
+void LogCvcAuthRetryableError(CreditCard::RecordType card_type,
+                              CvcAuthEvent event);
+
 }  // namespace autofill::autofill_metrics
 
 #endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_OFFERS_METRICS_H_
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
index 79f9080..ca2bb92a 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc
@@ -35,6 +35,7 @@
   requester_ = requester;
   if (!card) {
     return OnFullCardRequestFailed(
+        card->record_type(),
         payments::FullCardRequest::FailureType::GENERIC_FAILURE);
   }
   full_card_request_ = std::make_unique<payments::FullCardRequest>(
@@ -61,8 +62,8 @@
     // frame origin, end the card unmasking and treat it as a transient failure.
     if (!last_committed_primary_main_frame_origin.is_valid()) {
       return OnFullCardRequestFailed(
-          payments::FullCardRequest::FailureType::
-              VIRTUAL_CARD_RETRIEVAL_TRANSIENT_FAILURE);
+          card->record_type(), payments::FullCardRequest::FailureType::
+                                   VIRTUAL_CARD_RETRIEVAL_TRANSIENT_FAILURE);
     }
 
     return full_card_request_->GetFullVirtualCardViaCVC(
@@ -81,6 +82,9 @@
     const payments::FullCardRequest& full_card_request,
     const CreditCard& card,
     const std::u16string& cvc) {
+  autofill_metrics::LogCvcAuthResult(card.record_type(),
+                                     autofill_metrics::CvcAuthEvent::kSuccess);
+
   if (!requester_)
     return;
 
@@ -96,7 +100,34 @@
 }
 
 void CreditCardCVCAuthenticator::OnFullCardRequestFailed(
+    CreditCard::RecordType card_type,
     payments::FullCardRequest::FailureType failure_type) {
+  autofill_metrics::CvcAuthEvent event =
+      autofill_metrics::CvcAuthEvent::kUnknown;
+  switch (failure_type) {
+    case payments::FullCardRequest::FailureType::PROMPT_CLOSED:
+      event = autofill_metrics::CvcAuthEvent::kFlowCancelled;
+      break;
+    case payments::FullCardRequest::FailureType::VERIFICATION_DECLINED:
+      event = autofill_metrics::CvcAuthEvent::kUnmaskCardAuthError;
+      break;
+    case payments::FullCardRequest::FailureType::
+        VIRTUAL_CARD_RETRIEVAL_TRANSIENT_FAILURE:
+    case payments::FullCardRequest::FailureType::
+        VIRTUAL_CARD_RETRIEVAL_PERMANENT_FAILURE:
+      event =
+          autofill_metrics::CvcAuthEvent::kUnmaskCardVirtualCardRetrievalError;
+      break;
+    case payments::FullCardRequest::FailureType::GENERIC_FAILURE:
+      event = autofill_metrics::CvcAuthEvent::kGenericError;
+      break;
+    case payments::FullCardRequest::FailureType::UNKNOWN:
+      NOTREACHED();
+      event = autofill_metrics::CvcAuthEvent::kUnknown;
+      break;
+  }
+  autofill_metrics::LogCvcAuthResult(card_type, event);
+
   if (!requester_)
     return;
 
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
index e604320a..ec11d3db 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h
@@ -104,6 +104,7 @@
       const CreditCard& card,
       const std::u16string& cvc) override;
   void OnFullCardRequestFailed(
+      CreditCard::RecordType card_type,
       payments::FullCardRequest::FailureType failure_type) override;
 
   // payments::FullCardRequest::UIDelegate
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
index abfaa17d..fdb537f 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
@@ -33,6 +33,7 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/metrics/form_events/form_events.h"
+#include "components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h"
 #include "components/autofill/core/browser/payments/full_card_request.h"
 #include "components/autofill/core/browser/payments/test_authentication_requester.h"
 #include "components/autofill/core/browser/payments/test_payments_client.h"
@@ -176,6 +177,10 @@
   histogram_tester.ExpectUniqueSample("Autofill.CvcAuth.ServerCard.Attempt",
                                       /*sample=*/true,
                                       /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.ServerCard.Result",
+      /*sample=*/autofill_metrics::CvcAuthEvent::kSuccess,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(CreditCardCVCAuthenticatorTest, AuthenticateVirtualCardSuccess) {
@@ -204,12 +209,17 @@
   EXPECT_EQ(challenge_option->challenge_input_length, 3U);
   EXPECT_EQ(challenge_option->cvc_position, CvcPosition::kBackOfCard);
 
-  OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, kTestNumber);
+  OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, kTestNumber,
+                  /*is_virtual_card=*/true);
   EXPECT_TRUE((*requester_->did_succeed()));
   EXPECT_EQ(kTestNumber16, requester_->number());
   histogram_tester.ExpectUniqueSample("Autofill.CvcAuth.VirtualCard.Attempt",
                                       /*sample=*/true,
                                       /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.VirtualCard.Result",
+      /*sample=*/autofill_metrics::CvcAuthEvent::kSuccess,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(CreditCardCVCAuthenticatorTest, AuthenticateVirtualCard_InvalidURL) {
@@ -231,6 +241,11 @@
   histogram_tester.ExpectUniqueSample("Autofill.CvcAuth.VirtualCard.Attempt",
                                       /*sample=*/true,
                                       /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.VirtualCard.Result",
+      /*sample=*/
+      autofill_metrics::CvcAuthEvent::kUnmaskCardVirtualCardRetrievalError,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(CreditCardCVCAuthenticatorTest, AuthenticateNetworkError) {
@@ -246,6 +261,10 @@
   histogram_tester.ExpectUniqueSample("Autofill.CvcAuth.ServerCard.Attempt",
                                       /*sample=*/true,
                                       /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.ServerCard.Result",
+      /*sample=*/autofill_metrics::CvcAuthEvent::kGenericError,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(CreditCardCVCAuthenticatorTest, AuthenticatePermanentFailure) {
@@ -261,6 +280,10 @@
   histogram_tester.ExpectUniqueSample("Autofill.CvcAuth.ServerCard.Attempt",
                                       /*sample=*/true,
                                       /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.ServerCard.Result",
+      /*sample=*/autofill_metrics::CvcAuthEvent::kUnmaskCardAuthError,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(CreditCardCVCAuthenticatorTest, AuthenticateTryAgainFailure) {
@@ -280,6 +303,59 @@
   histogram_tester.ExpectUniqueSample("Autofill.CvcAuth.ServerCard.Attempt",
                                       /*sample=*/true,
                                       /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.ServerCard.RetryableError",
+      /*sample=*/autofill_metrics::CvcAuthEvent::kTemporaryErrorCvcMismatch,
+      /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.ServerCard.Result",
+      /*sample=*/autofill_metrics::CvcAuthEvent::kSuccess,
+      /*expected_bucket_count=*/1);
+}
+
+TEST_F(CreditCardCVCAuthenticatorTest, AuthenticatePromptClosed) {
+  base::HistogramTester histogram_tester;
+  CreditCard card = CreateServerCard(kTestGUID, kTestNumber);
+
+  cvc_authenticator_->Authenticate(&card, requester_->GetWeakPtr(),
+                                   &personal_data_manager_);
+
+  cvc_authenticator_->OnFullCardRequestFailed(
+      card.record_type(), payments::FullCardRequest::PROMPT_CLOSED);
+
+  histogram_tester.ExpectUniqueSample("Autofill.CvcAuth.ServerCard.Attempt",
+                                      /*sample=*/true,
+                                      /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.ServerCard.Result",
+      /*sample=*/autofill_metrics::CvcAuthEvent::kFlowCancelled,
+      /*expected_bucket_count=*/1);
+}
+
+TEST_F(CreditCardCVCAuthenticatorTest, VirtualCardAuthenticatePromptClosed) {
+  base::HistogramTester histogram_tester;
+  CreditCard card = CreateServerCard(kTestGUID, kTestNumber);
+  card.set_record_type(CreditCard::VIRTUAL_CARD);
+  autofill_client_.set_last_committed_primary_main_frame_url(
+      GURL("https://vcncvcretrievaltest.com/"));
+
+  cvc_authenticator_->Authenticate(
+      &card, requester_->GetWeakPtr(), &personal_data_manager_,
+      "test_vcn_context_token",
+      CardUnmaskChallengeOption{.id = "test_challenge_option_id",
+                                .type = CardUnmaskChallengeOptionType::kCvc,
+                                .challenge_input_length = 3U,
+                                .cvc_position = CvcPosition::kBackOfCard});
+  cvc_authenticator_->OnFullCardRequestFailed(
+      card.record_type(), payments::FullCardRequest::PROMPT_CLOSED);
+
+  histogram_tester.ExpectUniqueSample("Autofill.CvcAuth.VirtualCard.Attempt",
+                                      /*sample=*/true,
+                                      /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.VirtualCard.Result",
+      /*sample=*/autofill_metrics::CvcAuthEvent::kFlowCancelled,
+      /*expected_bucket_count=*/1);
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
index 70fb4a90..0ef6a73f 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -489,6 +489,7 @@
 }
 
 void CreditCardFIDOAuthenticator::OnFullCardRequestFailed(
+    CreditCard::RecordType card_type,
     payments::FullCardRequest::FailureType failure_type) {
   DCHECK_EQ(AUTHENTICATION_FLOW, current_flow_);
   current_flow_ = NONE_FLOW;
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
index 3ddd5a09..607da02 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
@@ -203,6 +203,7 @@
       const CreditCard& card,
       const std::u16string& cvc) override;
   void OnFullCardRequestFailed(
+      CreditCard::RecordType card_type,
       payments::FullCardRequest::FailureType failure_type) override;
 
   // Converts |request_options| from JSON to mojom pointer.
diff --git a/components/autofill/core/browser/payments/full_card_request.cc b/components/autofill/core/browser/payments/full_card_request.cc
index b55c6a4..2eb02e7a 100644
--- a/components/autofill/core/browser/payments/full_card_request.cc
+++ b/components/autofill/core/browser/payments/full_card_request.cc
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/metrics/autofill_metrics.h"
+#include "components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h"
 #include "components/autofill/core/browser/payments/payments_util.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/common/autofill_clock.h"
@@ -98,11 +99,14 @@
   DCHECK_NE(fido_assertion_info.has_value(), !!ui_delegate);
   DCHECK(result_delegate);
 
+  CreditCard::RecordType card_type = card.record_type();
+
   // Only one request can be active at a time. If the member variable
   // |result_delegate_| is already set, then immediately reject the new request
   // through the method parameter |result_delegate_|.
   if (result_delegate_) {
-    result_delegate_->OnFullCardRequestFailed(FailureType::GENERIC_FAILURE);
+    result_delegate_->OnFullCardRequestFailed(card_type,
+                                              FailureType::GENERIC_FAILURE);
     return;
   }
   result_delegate_ = result_delegate;
@@ -121,7 +125,7 @@
 
     if (result_delegate_) {
       result_delegate_->OnFullCardRequestFailed(
-          FailureType::VIRTUAL_CARD_RETRIEVAL_PERMANENT_FAILURE);
+          card_type, FailureType::VIRTUAL_CARD_RETRIEVAL_PERMANENT_FAILURE);
     }
 
     Reset();
@@ -138,9 +142,9 @@
     request_->selected_challenge_option = selected_challenge_option;
 
   should_unmask_card_ = card.masked() ||
-                        (card.record_type() == CreditCard::FULL_SERVER_CARD &&
+                        (card_type == CreditCard::FULL_SERVER_CARD &&
                          card.ShouldUpdateExpiration()) ||
-                        (card.record_type() == CreditCard::VIRTUAL_CARD);
+                        (card_type == CreditCard::VIRTUAL_CARD);
   if (should_unmask_card_) {
     payments_client_->Prepare();
     request_->billing_customer_number =
@@ -209,8 +213,10 @@
 }
 
 void FullCardRequest::OnUnmaskPromptClosed() {
-  if (result_delegate_)
-    result_delegate_->OnFullCardRequestFailed(FailureType::PROMPT_CLOSED);
+  if (result_delegate_) {
+    result_delegate_->OnFullCardRequestFailed(request_->card.record_type(),
+                                              FailureType::PROMPT_CLOSED);
+  }
 
   Reset();
 }
@@ -272,28 +278,36 @@
 
   switch (result) {
     // Wait for user retry.
-    case AutofillClient::PaymentsRpcResult::kTryAgainFailure:
+    case AutofillClient::PaymentsRpcResult::kTryAgainFailure: {
+      autofill_metrics::LogCvcAuthRetryableError(
+          request_->card.record_type(),
+          request_->card.ShouldUpdateExpiration()
+              ? autofill_metrics::CvcAuthEvent::kTemporaryErrorExpiredCard
+              : autofill_metrics::CvcAuthEvent::kTemporaryErrorCvcMismatch);
       break;
-
+    }
     // Neither PERMANENT_FAILURE, NETWORK_ERROR nor VCN retrieval errors allow
     // retry.
     case AutofillClient::PaymentsRpcResult::kPermanentFailure: {
       if (result_delegate_) {
         result_delegate_->OnFullCardRequestFailed(
-            FailureType::VERIFICATION_DECLINED);
+            request_->card.record_type(), FailureType::VERIFICATION_DECLINED);
       }
       Reset();
       break;
     }
     case AutofillClient::PaymentsRpcResult::kNetworkError: {
-      if (result_delegate_)
-        result_delegate_->OnFullCardRequestFailed(FailureType::GENERIC_FAILURE);
+      if (result_delegate_) {
+        result_delegate_->OnFullCardRequestFailed(request_->card.record_type(),
+                                                  FailureType::GENERIC_FAILURE);
+      }
       Reset();
       break;
     }
     case AutofillClient::PaymentsRpcResult::kVcnRetrievalTryAgainFailure: {
       if (result_delegate_) {
         result_delegate_->OnFullCardRequestFailed(
+            request_->card.record_type(),
             FailureType::VIRTUAL_CARD_RETRIEVAL_TRANSIENT_FAILURE);
       }
       Reset();
@@ -302,6 +316,7 @@
     case AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure: {
       if (result_delegate_) {
         result_delegate_->OnFullCardRequestFailed(
+            request_->card.record_type(),
             FailureType::VIRTUAL_CARD_RETRIEVAL_PERMANENT_FAILURE);
       }
       Reset();
@@ -336,9 +351,10 @@
       const std::u16string cvc = !response_details.dcvv.empty()
                                      ? base::UTF8ToUTF16(response_details.dcvv)
                                      : request_->user_response.cvc;
-      if (result_delegate_)
+      if (result_delegate_) {
         result_delegate_->OnFullCardRequestSucceeded(*this, request_->card,
                                                      cvc);
+      }
       Reset();
       break;
     }
diff --git a/components/autofill/core/browser/payments/full_card_request.h b/components/autofill/core/browser/payments/full_card_request.h
index e1e071f..98df4c0 100644
--- a/components/autofill/core/browser/payments/full_card_request.h
+++ b/components/autofill/core/browser/payments/full_card_request.h
@@ -72,7 +72,8 @@
         const payments::FullCardRequest& full_card_request,
         const CreditCard& card,
         const std::u16string& cvc) = 0;
-    virtual void OnFullCardRequestFailed(FailureType failure_type) = 0;
+    virtual void OnFullCardRequestFailed(CreditCard::RecordType card_type,
+                                         FailureType failure_type) = 0;
   };
 
   // The delegate responsible for displaying the unmask prompt UI.
diff --git a/components/autofill/core/browser/payments/full_card_request_unittest.cc b/components/autofill/core/browser/payments/full_card_request_unittest.cc
index 08440b8..552b6dc 100644
--- a/components/autofill/core/browser/payments/full_card_request_unittest.cc
+++ b/components/autofill/core/browser/payments/full_card_request_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -14,6 +15,7 @@
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
@@ -48,7 +50,7 @@
               (override));
   MOCK_METHOD(void,
               OnFullCardRequestFailed,
-              (payments::FullCardRequest::FailureType),
+              (CreditCard::RecordType, payments::FullCardRequest::FailureType),
               (override));
 };
 
@@ -438,7 +440,8 @@
 TEST_F(FullCardRequestTest, OneRequestAtATime) {
   EXPECT_CALL(
       *result_delegate(),
-      OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE));
+      OnFullCardRequestFailed(CreditCard::MASKED_SERVER_CARD,
+                              FullCardRequest::FailureType::GENERIC_FAILURE));
   EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
   EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(_)).Times(0);
 
@@ -454,7 +457,7 @@
 
 // After the first request completes, it's OK to start the second request.
 TEST_F(FullCardRequestTest, SecondRequestOkAfterFirstFinished) {
-  EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed(_)).Times(0);
+  EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed(_, _)).Times(0);
   EXPECT_CALL(
       *result_delegate(),
       OnFullCardRequestSucceeded(testing::Ref(*request()),
@@ -488,7 +491,8 @@
 TEST_F(FullCardRequestTest, ClosePromptWithoutUserInput) {
   EXPECT_CALL(
       *result_delegate(),
-      OnFullCardRequestFailed(FullCardRequest::FailureType::PROMPT_CLOSED));
+      OnFullCardRequestFailed(CreditCard::MASKED_SERVER_CARD,
+                              FullCardRequest::FailureType::PROMPT_CLOSED));
   EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
   EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(_)).Times(0);
 
@@ -504,6 +508,7 @@
 TEST_F(FullCardRequestTest, PermanentFailure) {
   EXPECT_CALL(*result_delegate(),
               OnFullCardRequestFailed(
+                  CreditCard::MASKED_SERVER_CARD,
                   FullCardRequest::FailureType::VERIFICATION_DECLINED));
   EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
   EXPECT_CALL(*ui_delegate(),
@@ -527,7 +532,8 @@
 TEST_F(FullCardRequestTest, VcnRetrievalTemporaryFailure) {
   EXPECT_CALL(
       *result_delegate(),
-      OnFullCardRequestFailed(FullCardRequest::FailureType::
+      OnFullCardRequestFailed(CreditCard::VIRTUAL_CARD,
+                              FullCardRequest::FailureType::
                                   VIRTUAL_CARD_RETRIEVAL_TRANSIENT_FAILURE));
   EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
   EXPECT_CALL(
@@ -557,7 +563,8 @@
 TEST_F(FullCardRequestTest, VcnRetrievalPermanentFailure) {
   EXPECT_CALL(
       *result_delegate(),
-      OnFullCardRequestFailed(FullCardRequest::FailureType::
+      OnFullCardRequestFailed(CreditCard::VIRTUAL_CARD,
+                              FullCardRequest::FailureType::
                                   VIRTUAL_CARD_RETRIEVAL_PERMANENT_FAILURE));
   EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
   EXPECT_CALL(
@@ -586,7 +593,8 @@
 TEST_F(FullCardRequestTest, NetworkError) {
   EXPECT_CALL(
       *result_delegate(),
-      OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE));
+      OnFullCardRequestFailed(CreditCard::MASKED_SERVER_CARD,
+                              FullCardRequest::FailureType::GENERIC_FAILURE));
   EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
   EXPECT_CALL(*ui_delegate(),
               OnUnmaskVerificationResult(
@@ -606,69 +614,98 @@
 // If the server provides an empty PAN with TRY_AGAIN_FAILURE, the user can
 // manually cancel out of the dialog.
 TEST_F(FullCardRequestTest, TryAgainFailureGiveUp) {
-  EXPECT_CALL(
-      *result_delegate(),
-      OnFullCardRequestFailed(FullCardRequest::FailureType::PROMPT_CLOSED));
-  EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
-  EXPECT_CALL(*ui_delegate(),
-              OnUnmaskVerificationResult(
-                  AutofillClient::PaymentsRpcResult::kTryAgainFailure));
-
-  request()->GetFullCard(
-      CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"),
-      AutofillClient::UnmaskCardReason::kAutofill,
-      result_delegate()->AsWeakPtr(), ui_delegate()->AsWeakPtr());
-  CardUnmaskDelegate::UserProvidedUnmaskDetails details;
-  details.cvc = u"123";
-  card_unmask_delegate()->OnUnmaskPromptAccepted(details);
-  OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kTryAgainFailure, "");
-  card_unmask_delegate()->OnUnmaskPromptClosed();
+  // We test all possible cases of a temporary error.
+  for (autofill_metrics::CvcAuthEvent test_event :
+       {autofill_metrics::CvcAuthEvent::kTemporaryErrorCvcMismatch,
+        autofill_metrics::CvcAuthEvent::kTemporaryErrorExpiredCard}) {
+    SCOPED_TRACE(::testing::Message()
+                 << "Iteration " << static_cast<int>(test_event));
+    base::HistogramTester histogram_tester;
+    EXPECT_CALL(
+        *result_delegate(),
+        OnFullCardRequestFailed(CreditCard::MASKED_SERVER_CARD,
+                                FullCardRequest::FailureType::PROMPT_CLOSED));
+    EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
+    EXPECT_CALL(*ui_delegate(),
+                OnUnmaskVerificationResult(
+                    AutofillClient::PaymentsRpcResult::kTryAgainFailure));
+    CreditCard card = test::GetMaskedServerCard();
+    if (test_event ==
+        autofill_metrics::CvcAuthEvent::kTemporaryErrorExpiredCard) {
+      card.SetExpirationMonth(01);
+      card.SetExpirationYear(2016);
+    }
+    request()->GetFullCard(card, AutofillClient::UnmaskCardReason::kAutofill,
+                           result_delegate()->AsWeakPtr(),
+                           ui_delegate()->AsWeakPtr());
+    CardUnmaskDelegate::UserProvidedUnmaskDetails details;
+    details.cvc = u"123";
+    card_unmask_delegate()->OnUnmaskPromptAccepted(details);
+    OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kTryAgainFailure, "");
+    card_unmask_delegate()->OnUnmaskPromptClosed();
+    histogram_tester.ExpectUniqueSample(
+        "Autofill.CvcAuth.ServerCard.RetryableError", test_event, 1);
+  }
 }
 
 // If the server provides an empty PAN with TRY_AGAIN_FAILURE, the user can
 // correct their mistake and resubmit.
 TEST_F(FullCardRequestTest, ServerCardTryAgainFailure) {
-  EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed(_)).Times(0);
-  EXPECT_CALL(*result_delegate(),
-              OnFullCardRequestSucceeded(
-                  testing::Ref(*request()),
-                  CardMatches(CreditCard::FULL_SERVER_CARD, "4111"),
-                  testing::Eq(u"123")));
-  EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
-  EXPECT_CALL(*ui_delegate(),
-              OnUnmaskVerificationResult(
-                  AutofillClient::PaymentsRpcResult::kTryAgainFailure));
-  EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(
-                                  AutofillClient::PaymentsRpcResult::kSuccess));
+  // We test all possible cases of a temporary error.
+  for (autofill_metrics::CvcAuthEvent test_event :
+       {autofill_metrics::CvcAuthEvent::kTemporaryErrorCvcMismatch,
+        autofill_metrics::CvcAuthEvent::kTemporaryErrorExpiredCard}) {
+    SCOPED_TRACE(::testing::Message()
+                 << "Iteration " << static_cast<int>(test_event));
+    base::HistogramTester histogram_tester;
+    EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed(_, _)).Times(0);
+    EXPECT_CALL(*result_delegate(),
+                OnFullCardRequestSucceeded(
+                    testing::Ref(*request()),
+                    CardMatches(CreditCard::FULL_SERVER_CARD, "4111"),
+                    testing::Eq(u"123")));
+    EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _));
+    EXPECT_CALL(*ui_delegate(),
+                OnUnmaskVerificationResult(
+                    AutofillClient::PaymentsRpcResult::kTryAgainFailure));
+    EXPECT_CALL(*ui_delegate(),
+                OnUnmaskVerificationResult(
+                    AutofillClient::PaymentsRpcResult::kSuccess));
 
-  request()->GetFullCard(
-      CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"),
-      AutofillClient::UnmaskCardReason::kAutofill,
-      result_delegate()->AsWeakPtr(), ui_delegate()->AsWeakPtr());
-  CardUnmaskDelegate::UserProvidedUnmaskDetails details;
-  details.cvc = u"789";
-  card_unmask_delegate()->OnUnmaskPromptAccepted(details);
-  OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kTryAgainFailure, "");
-  details.cvc = u"123";
-  card_unmask_delegate()->OnUnmaskPromptAccepted(details);
-  OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, "4111");
-  card_unmask_delegate()->OnUnmaskPromptClosed();
+    CreditCard card = test::GetMaskedServerCard();
+    if (test_event ==
+        autofill_metrics::CvcAuthEvent::kTemporaryErrorExpiredCard) {
+      card.SetExpirationMonth(01);
+      card.SetExpirationYear(2016);
+    }
+    request()->GetFullCard(card, AutofillClient::UnmaskCardReason::kAutofill,
+                           result_delegate()->AsWeakPtr(),
+                           ui_delegate()->AsWeakPtr());
+    CardUnmaskDelegate::UserProvidedUnmaskDetails details;
+    details.cvc = u"789";
+    card_unmask_delegate()->OnUnmaskPromptAccepted(details);
+    OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kTryAgainFailure, "");
+    details.cvc = u"123";
+    card_unmask_delegate()->OnUnmaskPromptAccepted(details);
+    OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, "4111");
+    card_unmask_delegate()->OnUnmaskPromptClosed();
+    histogram_tester.ExpectUniqueSample(
+        "Autofill.CvcAuth.ServerCard.RetryableError", test_event, 1);
+  }
 }
 
 // If the server provides an empty PAN with TRY_AGAIN_FAILURE for virtual card,
 // ensure it is handled the same way as a regular try again case.
 TEST_F(FullCardRequestTest, VirtualCardTryAgainFailure) {
+  base::HistogramTester histogram_tester;
   EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)).Times(1);
   EXPECT_CALL(*ui_delegate(),
               OnUnmaskVerificationResult(
                   AutofillClient::PaymentsRpcResult::kTryAgainFailure))
       .Times(1);
 
-  CreditCard virtual_card;
-  virtual_card.set_record_type(CreditCard::RecordType::VIRTUAL_CARD);
-  virtual_card.set_server_id("server_id");
   request()->GetFullVirtualCardViaCVC(
-      virtual_card, AutofillClient::UnmaskCardReason::kAutofill,
+      test::GetVirtualCard(), AutofillClient::UnmaskCardReason::kAutofill,
       result_delegate()->AsWeakPtr(), ui_delegate()->AsWeakPtr(),
       GURL("https://example.com/"), "test_context_token",
       CardUnmaskChallengeOption{.id = "test_challenge_option_id",
@@ -696,6 +733,9 @@
                 ->GetUnmaskRequestDetailsForTesting()
                 ->last_committed_primary_main_frame_origin->spec(),
             "https://example.com/");
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcAuth.VirtualCard.RetryableError",
+      autofill_metrics::CvcAuthEvent::kTemporaryErrorCvcMismatch, 1);
 }
 
 // Verify updating expiration date for a masked server card.
diff --git a/components/autofill_assistant/browser/full_card_requester.cc b/components/autofill_assistant/browser/full_card_requester.cc
index 6ae478a..88214624 100644
--- a/components/autofill_assistant/browser/full_card_requester.cc
+++ b/components/autofill_assistant/browser/full_card_requester.cc
@@ -49,7 +49,8 @@
   autofill::CreditCardCVCAuthenticator* cvc_authenticator =
       GetCVCAuthenticator(web_contents);
   if (!cvc_authenticator) {
-    OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE);
+    OnFullCardRequestFailed(card->record_type(),
+                            FullCardRequest::FailureType::GENERIC_FAILURE);
     return;
   }
   cvc_authenticator->GetFullCardRequest()->GetFullCard(
@@ -69,6 +70,7 @@
 }
 
 void FullCardRequester::OnFullCardRequestFailed(
+    const autofill::CreditCard::RecordType card_type,
     FullCardRequest::FailureType failure_type) {
   ClientStatus status(GET_FULL_CARD_FAILED);
   AutofillErrorInfoProto::GetFullCardFailureType error_type =
diff --git a/components/autofill_assistant/browser/full_card_requester.h b/components/autofill_assistant/browser/full_card_requester.h
index f1fb1427..01808b9 100644
--- a/components/autofill_assistant/browser/full_card_requester.h
+++ b/components/autofill_assistant/browser/full_card_requester.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/full_card_request.h"
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
 
@@ -34,8 +35,8 @@
       const autofill::CreditCard& card,
       const std::u16string& cvc) override;
   void OnFullCardRequestFailed(
-      autofill::payments::FullCardRequest::FailureType /* failure_type */)
-      override;
+      autofill::CreditCard::RecordType card_type,
+      autofill::payments::FullCardRequest::FailureType failure_type) override;
 
   ActionDelegate::GetFullCardCallback callback_;
 
diff --git a/components/cast_receiver/renderer/BUILD.gn b/components/cast_receiver/renderer/BUILD.gn
index f083f12..d5adc5b 100644
--- a/components/cast_receiver/renderer/BUILD.gn
+++ b/components/cast_receiver/renderer/BUILD.gn
@@ -5,21 +5,21 @@
 # TODO(crbug.com/1385152): Remove this condition once Windows builds are fixed.
 if (!is_win) {
   source_set("renderer") {
-    public = [
-      "public/content_renderer_client_mixins.h",
-      "public/url_rewrite_rules_provider.h",
-    ]
+    public = [ "public/content_renderer_client_mixins.h" ]
     sources = [
-      "content_renderer_client_mixins.cc",
+      "content_renderer_client_mixins_impl.cc",
+      "content_renderer_client_mixins_impl.h",
       "url_rewrite_rules_provider.cc",
+      "url_rewrite_rules_provider.h",
+      "wrapping_url_loader_throttle_provider.cc",
+      "wrapping_url_loader_throttle_provider.h",
     ]
-    public_deps = [
-      "//components/url_rewrite/renderer",
-      "//content/public/renderer",
-    ]
+    public_deps = [ "//base" ]
     deps = [
       "//components/media_control/renderer",
       "//components/on_load_script_injector/renderer",
+      "//components/url_rewrite/renderer",
+      "//content/public/renderer",
       "//third_party/blink/public:blink",
     ]
   }
diff --git a/components/cast_receiver/renderer/DEPS b/components/cast_receiver/renderer/DEPS
index 1cab500..f1487b03 100644
--- a/components/cast_receiver/renderer/DEPS
+++ b/components/cast_receiver/renderer/DEPS
@@ -1,7 +1,8 @@
 include_rules = [
   "+components/media_control/renderer",
   "+components/on_load_script_injector/renderer",
+  "+components/url_rewrite/common",
   "+components/url_rewrite/renderer",
   "+content/public/renderer",
-  "+third_party/blink/public/common",
+  "+third_party/blink/public",
 ]
diff --git a/components/cast_receiver/renderer/content_renderer_client_mixins.cc b/components/cast_receiver/renderer/content_renderer_client_mixins.cc
deleted file mode 100644
index c78e3eb..0000000
--- a/components/cast_receiver/renderer/content_renderer_client_mixins.cc
+++ /dev/null
@@ -1,31 +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 "components/cast_receiver/renderer/public/content_renderer_client_mixins.h"
-
-#include "components/media_control/renderer/media_playback_options.h"
-#include "components/on_load_script_injector/renderer/on_load_script_injector.h"
-
-namespace cast_receiver {
-
-void ContentRendererClientMixins::RenderFrameCreated(
-    content::RenderFrame* render_frame) {
-  // Add script injection support to the RenderFrame, used for bindings support
-  // APIs. The injector's lifetime is bound to the RenderFrame's lifetime.
-  new on_load_script_injector::OnLoadScriptInjector(render_frame);
-
-  // Lifetime is tied to |render_frame| via content::RenderFrameObserver.
-  new media_control::MediaPlaybackOptions(render_frame);
-}
-
-bool ContentRendererClientMixins::DeferMediaLoad(
-    content::RenderFrame* render_frame,
-    base::OnceClosure closure) {
-  auto* playback_options =
-      media_control::MediaPlaybackOptions::Get(render_frame);
-  DCHECK(playback_options);
-  return playback_options->RunWhenInForeground(std::move(closure));
-}
-
-}  // namespace cast_receiver
diff --git a/components/cast_receiver/renderer/content_renderer_client_mixins_impl.cc b/components/cast_receiver/renderer/content_renderer_client_mixins_impl.cc
new file mode 100644
index 0000000..b414db5
--- /dev/null
+++ b/components/cast_receiver/renderer/content_renderer_client_mixins_impl.cc
@@ -0,0 +1,103 @@
+// 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 "components/cast_receiver/renderer/content_renderer_client_mixins_impl.h"
+
+#include "components/cast_receiver/renderer/url_rewrite_rules_provider.h"
+#include "components/media_control/renderer/media_playback_options.h"
+#include "components/on_load_script_injector/renderer/on_load_script_injector.h"
+#include "content/public/renderer/render_frame.h"
+
+namespace cast_receiver {
+
+// static
+std::unique_ptr<ContentRendererClientMixins>
+ContentRendererClientMixins::Create(
+    IsCorsExemptHeadersCallback is_cors_exempt_header_callback) {
+  return std::make_unique<ContentRendererClientMixinsImpl>(
+      std::move(is_cors_exempt_header_callback));
+}
+
+ContentRendererClientMixinsImpl::ContentRendererClientMixinsImpl(
+    IsCorsExemptHeadersCallback is_cors_exempt_header_callback)
+    : is_cors_exempt_header_callback_(
+          std::move(is_cors_exempt_header_callback)) {
+  DCHECK(is_cors_exempt_header_callback_);
+}
+
+ContentRendererClientMixinsImpl::~ContentRendererClientMixinsImpl() = default;
+
+void ContentRendererClientMixinsImpl::RenderFrameCreated(
+    content::RenderFrame& render_frame) {
+  // Add script injection support to the RenderFrame, used for bindings support
+  // APIs. The injector's lifetime is bound to the RenderFrame's lifetime.
+  new on_load_script_injector::OnLoadScriptInjector(&render_frame);
+
+  // Lifetime is tied to |render_frame| via content::RenderFrameObserver.
+  new media_control::MediaPlaybackOptions(&render_frame);
+
+  // Create the new UrlRewriteRulesProvider.
+  url_rewrite_rules_providers_.emplace(
+      render_frame.GetRoutingID(),
+      std::make_unique<UrlRewriteRulesProvider>(
+          &render_frame,
+          base::BindOnce(&ContentRendererClientMixinsImpl::OnRenderFrameRemoved,
+                         base::Unretained(this))));
+}
+
+bool ContentRendererClientMixinsImpl::DeferMediaLoad(
+    content::RenderFrame& render_frame,
+    base::OnceClosure closure) {
+  auto* playback_options =
+      media_control::MediaPlaybackOptions::Get(&render_frame);
+  DCHECK(playback_options);
+  return playback_options->RunWhenInForeground(std::move(closure));
+}
+
+std::unique_ptr<blink::URLLoaderThrottleProvider>
+ContentRendererClientMixinsImpl::CreateURLLoaderThrottleProvider() {
+  // It is safe to use |this| here because the lifetime of this object is
+  // expected to match that of the ContentRendererClient with which it is
+  // associated, so it should outlive any WrappingURLLoaderThrottleProvider
+  // instances it creates.
+  return std::make_unique<WrappingURLLoaderThrottleProvider>(*this);
+}
+
+std::unique_ptr<blink::URLLoaderThrottleProvider>
+ContentRendererClientMixinsImpl::ExtendURLLoaderThrottleProvider(
+    std::unique_ptr<blink::URLLoaderThrottleProvider> delegated_load_provider) {
+  DCHECK(delegated_load_provider);
+  // It is safe to use |this| here because the lifetime of this object is
+  // expected to match that of the ContentRendererClient with which it is
+  // associated, so it should outlive any WrappingURLLoaderThrottleProvider
+  // instances it creates.
+  return std::make_unique<WrappingURLLoaderThrottleProvider>(
+      std::move(delegated_load_provider), *this);
+}
+
+void ContentRendererClientMixinsImpl::OnRenderFrameRemoved(
+    int render_frame_id) {
+  size_t result = url_rewrite_rules_providers_.erase(render_frame_id);
+  if (result != 1U) {
+    LOG(WARNING)
+        << "Can't find the URL rewrite rules provider for render frame: "
+        << render_frame_id;
+  }
+}
+
+UrlRewriteRulesProvider*
+ContentRendererClientMixinsImpl::GetUrlRewriteRulesProvider(
+    int render_frame_id) {
+  auto rules_it = url_rewrite_rules_providers_.find(render_frame_id);
+  return rules_it == url_rewrite_rules_providers_.end()
+             ? nullptr
+             : rules_it->second.get();
+}
+
+bool ContentRendererClientMixinsImpl::IsCorsExemptHeader(
+    base::StringPiece header) {
+  return is_cors_exempt_header_callback_.Run(header);
+}
+
+}  // namespace cast_receiver
diff --git a/components/cast_receiver/renderer/content_renderer_client_mixins_impl.h b/components/cast_receiver/renderer/content_renderer_client_mixins_impl.h
new file mode 100644
index 0000000..a67e1d3
--- /dev/null
+++ b/components/cast_receiver/renderer/content_renderer_client_mixins_impl.h
@@ -0,0 +1,77 @@
+// 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 COMPONENTS_CAST_RECEIVER_RENDERER_CONTENT_RENDERER_CLIENT_MIXINS_IMPL_H_
+#define COMPONENTS_CAST_RECEIVER_RENDERER_CONTENT_RENDERER_CLIENT_MIXINS_IMPL_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/functional/callback_forward.h"
+#include "base/strings/string_piece.h"
+#include "components/cast_receiver/renderer/public/content_renderer_client_mixins.h"
+#include "components/cast_receiver/renderer/wrapping_url_loader_throttle_provider.h"
+
+namespace blink {
+class URLLoaderThrottleProvider;
+}  // namespace blink
+
+namespace content {
+class RenderFrame;
+}  // namespace content
+
+namespace cast_receiver {
+
+class UrlRewriteRulesProvider;
+
+// Functions to provide additional ContentRendererClient functionality as
+// required for a functioning Cast receiver.
+//
+// TODO(crbug.com/1359580): Use this class in the
+// CastRuntimeContentRendererClient.
+class ContentRendererClientMixinsImpl
+    : public ContentRendererClientMixins,
+      public WrappingURLLoaderThrottleProvider::Client {
+ public:
+  using ContentRendererClientMixins::IsCorsExemptHeadersCallback;
+  explicit ContentRendererClientMixinsImpl(
+      IsCorsExemptHeadersCallback is_cors_exempt_header_callback);
+  ~ContentRendererClientMixinsImpl() override;
+
+  ContentRendererClientMixinsImpl(const ContentRendererClientMixinsImpl&) =
+      delete;
+  ContentRendererClientMixinsImpl& operator=(
+      const ContentRendererClientMixinsImpl&) = delete;
+
+  // ContentRendererClientMixins implementation.
+  void RenderFrameCreated(content::RenderFrame& render_frame) override;
+  bool DeferMediaLoad(content::RenderFrame& render_frame,
+                      base::OnceClosure closure) override;
+  std::unique_ptr<blink::URLLoaderThrottleProvider>
+  CreateURLLoaderThrottleProvider() override;
+  std::unique_ptr<blink::URLLoaderThrottleProvider>
+  ExtendURLLoaderThrottleProvider(
+      std::unique_ptr<blink::URLLoaderThrottleProvider> delegated_load_provider)
+      override;
+
+ private:
+  // Called by UrlRewriteRulesProvider instances as part of frame RenderFrame
+  // deletion.
+  void OnRenderFrameRemoved(int render_frame_id);
+
+  // WrappingURLLoaderThrottleProvider::Client implementation.
+  UrlRewriteRulesProvider* GetUrlRewriteRulesProvider(
+      int render_frame_id) override;
+  bool IsCorsExemptHeader(base::StringPiece header) override;
+
+  IsCorsExemptHeadersCallback is_cors_exempt_header_callback_;
+
+  base::flat_map<int /* render_frame_id */,
+                 std::unique_ptr<UrlRewriteRulesProvider>>
+      url_rewrite_rules_providers_;
+};
+
+}  // namespace cast_receiver
+
+#endif  // COMPONENTS_CAST_RECEIVER_RENDERER_CONTENT_RENDERER_CLIENT_MIXINS_IMPL_H_
diff --git a/components/cast_receiver/renderer/public/content_renderer_client_mixins.h b/components/cast_receiver/renderer/public/content_renderer_client_mixins.h
index ef586ad..38d7c3f4 100644
--- a/components/cast_receiver/renderer/public/content_renderer_client_mixins.h
+++ b/components/cast_receiver/renderer/public/content_renderer_client_mixins.h
@@ -5,24 +5,57 @@
 #ifndef COMPONENTS_CAST_RECEIVER_RENDERER_PUBLIC_CONTENT_RENDERER_CLIENT_MIXINS_H_
 #define COMPONENTS_CAST_RECEIVER_RENDERER_PUBLIC_CONTENT_RENDERER_CLIENT_MIXINS_H_
 
+#include <memory>
+
 #include "base/functional/callback_forward.h"
+#include "base/strings/string_piece.h"
+
+namespace blink {
+class URLLoaderThrottleProvider;
+}  // namespace blink
 
 namespace content {
 class RenderFrame;
-}
+}  // namespace content
 
 namespace cast_receiver {
 
 // Functions to provide additional ContentRendererClient functionality as
-// required for a functioning Cast receiver.
+// required for a functioning Cast receiver. The lifetime of this object is
+// expected to match that of the ContentRendererClient with which it is
+// associated.
 //
 // TODO(crbug.com/1359580): Use this class in the
 // CastRuntimeContentRendererClient.
 class ContentRendererClientMixins {
-  // To be called by the ContentRendererClient method of the same name.
-  void RenderFrameCreated(content::RenderFrame* render_frame);
-  bool DeferMediaLoad(content::RenderFrame* render_frame,
-                      base::OnceClosure closure);
+ public:
+  using IsCorsExemptHeadersCallback =
+      base::RepeatingCallback<bool(base::StringPiece)>;
+  static std::unique_ptr<ContentRendererClientMixins> Create(
+      IsCorsExemptHeadersCallback is_cors_exempt_header_callback);
+
+  virtual ~ContentRendererClientMixins() = default;
+
+  // To be called by the ContentRendererClient function of the same name.
+  virtual void RenderFrameCreated(content::RenderFrame& render_frame) = 0;
+  virtual bool DeferMediaLoad(content::RenderFrame& render_frame,
+                              base::OnceClosure closure) = 0;
+  virtual std::unique_ptr<blink::URLLoaderThrottleProvider>
+  CreateURLLoaderThrottleProvider() = 0;
+
+  // Wraps |delegated_load_provider| with a new URLLoaderThrottleProvider to
+  // apply any UrlRewriteRules previously set. |delegated_load_provider| may not
+  // be empty.
+  //
+  // Call this from ContentRendererClient::CreateURLLoaderThrottleProvider() as
+  // an alternative to calling CreateURLLoaderThrottleProvider() above by
+  // passing in a provider which creates any remaining URL Loader Throttles for
+  // this ContentRendererClient. The URLRewriteRules providers will be appended
+  // to those generated by |delegated_load_provider|.
+  virtual std::unique_ptr<blink::URLLoaderThrottleProvider>
+  ExtendURLLoaderThrottleProvider(
+      std::unique_ptr<blink::URLLoaderThrottleProvider>
+          delegated_load_provider = nullptr) = 0;
 };
 
 }  // namespace cast_receiver
diff --git a/components/cast_receiver/renderer/url_rewrite_rules_provider.cc b/components/cast_receiver/renderer/url_rewrite_rules_provider.cc
index 4dee425..834dadc 100644
--- a/components/cast_receiver/renderer/url_rewrite_rules_provider.cc
+++ b/components/cast_receiver/renderer/url_rewrite_rules_provider.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/cast_receiver/renderer/public/url_rewrite_rules_provider.h"
+#include "components/cast_receiver/renderer/url_rewrite_rules_provider.h"
 
 #include "base/bind.h"
 #include "content/public/renderer/render_frame.h"
diff --git a/components/cast_receiver/renderer/public/url_rewrite_rules_provider.h b/components/cast_receiver/renderer/url_rewrite_rules_provider.h
similarity index 86%
rename from components/cast_receiver/renderer/public/url_rewrite_rules_provider.h
rename to components/cast_receiver/renderer/url_rewrite_rules_provider.h
index 18a9496..5f713b7f 100644
--- a/components/cast_receiver/renderer/public/url_rewrite_rules_provider.h
+++ b/components/cast_receiver/renderer/url_rewrite_rules_provider.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CAST_RECEIVER_RENDERER_PUBLIC_URL_REWRITE_RULES_PROVIDER_H_
-#define COMPONENTS_CAST_RECEIVER_RENDERER_PUBLIC_URL_REWRITE_RULES_PROVIDER_H_
+#ifndef COMPONENTS_CAST_RECEIVER_RENDERER_URL_REWRITE_RULES_PROVIDER_H_
+#define COMPONENTS_CAST_RECEIVER_RENDERER_URL_REWRITE_RULES_PROVIDER_H_
 
 #include "base/callback.h"
 #include "components/url_rewrite/renderer/url_request_rules_receiver.h"
@@ -45,4 +45,4 @@
 
 }  // namespace cast_receiver
 
-#endif  // COMPONENTS_CAST_RECEIVER_RENDERER_PUBLIC_URL_REWRITE_RULES_PROVIDER_H_
+#endif  // COMPONENTS_CAST_RECEIVER_RENDERER_URL_REWRITE_RULES_PROVIDER_H_
diff --git a/components/cast_receiver/renderer/wrapping_url_loader_throttle_provider.cc b/components/cast_receiver/renderer/wrapping_url_loader_throttle_provider.cc
new file mode 100644
index 0000000..bf2676f
--- /dev/null
+++ b/components/cast_receiver/renderer/wrapping_url_loader_throttle_provider.cc
@@ -0,0 +1,73 @@
+// 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 "components/cast_receiver/renderer/wrapping_url_loader_throttle_provider.h"
+
+#include "components/cast_receiver/renderer/url_rewrite_rules_provider.h"
+#include "components/media_control/renderer/media_playback_options.h"
+#include "components/on_load_script_injector/renderer/on_load_script_injector.h"
+#include "components/url_rewrite/common/url_loader_throttle.h"
+#include "third_party/blink/public/common/loader/url_loader_throttle.h"
+#include "third_party/blink/public/platform/url_loader_throttle_provider.h"
+#include "third_party/blink/public/platform/web_vector.h"
+
+namespace cast_receiver {
+
+WrappingURLLoaderThrottleProvider::Client::~Client() = default;
+
+WrappingURLLoaderThrottleProvider::WrappingURLLoaderThrottleProvider(
+    std::unique_ptr<blink::URLLoaderThrottleProvider> wrapped_provider,
+    Client& client)
+    : client_(client), wrapped_provider_(std::move(wrapped_provider)) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+WrappingURLLoaderThrottleProvider::WrappingURLLoaderThrottleProvider(
+    Client& client)
+    : WrappingURLLoaderThrottleProvider(nullptr, client) {}
+
+WrappingURLLoaderThrottleProvider::~WrappingURLLoaderThrottleProvider() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+std::unique_ptr<blink::URLLoaderThrottleProvider>
+WrappingURLLoaderThrottleProvider::Clone() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return std::make_unique<WrappingURLLoaderThrottleProvider>(
+      wrapped_provider_ ? wrapped_provider_->Clone() : nullptr, *client_);
+}
+
+blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>>
+WrappingURLLoaderThrottleProvider::CreateThrottles(
+    int render_frame_id,
+    const blink::WebURLRequest& request) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
+  if (wrapped_provider_) {
+    throttles = wrapped_provider_->CreateThrottles(render_frame_id, request);
+  }
+
+  auto* provider = client_->GetUrlRewriteRulesProvider(render_frame_id);
+  if (provider) {
+    auto& rules = provider->GetCachedRules();
+    if (rules) {
+      throttles.emplace_back(std::make_unique<url_rewrite::URLLoaderThrottle>(
+          rules,
+          base::BindRepeating(
+              &WrappingURLLoaderThrottleProvider::Client::IsCorsExemptHeader,
+              base::Unretained(client_))));
+    }
+  }
+
+  return throttles;
+}
+
+void WrappingURLLoaderThrottleProvider::SetOnline(bool is_online) {
+  if (wrapped_provider_) {
+    wrapped_provider_->SetOnline(is_online);
+  }
+}
+
+}  // namespace cast_receiver
diff --git a/components/cast_receiver/renderer/wrapping_url_loader_throttle_provider.h b/components/cast_receiver/renderer/wrapping_url_loader_throttle_provider.h
new file mode 100644
index 0000000..33889c7
--- /dev/null
+++ b/components/cast_receiver/renderer/wrapping_url_loader_throttle_provider.h
@@ -0,0 +1,69 @@
+// 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 COMPONENTS_CAST_RECEIVER_RENDERER_WRAPPING_URL_LOADER_THROTTLE_PROVIDER_H_
+#define COMPONENTS_CAST_RECEIVER_RENDERER_WRAPPING_URL_LOADER_THROTTLE_PROVIDER_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/functional/callback_forward.h"
+#include "base/sequence_checker.h"
+#include "third_party/blink/public/platform/url_loader_throttle_provider.h"
+#include "third_party/blink/public/platform/web_vector.h"
+
+namespace cast_receiver {
+
+class UrlRewriteRulesProvider;
+
+class WrappingURLLoaderThrottleProvider
+    : public blink::URLLoaderThrottleProvider {
+ public:
+  // Provides callbacks needed for an instance of this class to function.
+  class Client {
+   public:
+    virtual ~Client();
+
+    // Returns the UrlRewriteRulesProvider associated with RenderFrame with id
+    // |render_frame_id|, or nullptr if no such provider exists.
+    virtual UrlRewriteRulesProvider* GetUrlRewriteRulesProvider(
+        int render_frame_id) = 0;
+
+    // Returns whether |header| is a cors exempt header.
+    virtual bool IsCorsExemptHeader(base::StringPiece header) = 0;
+  };
+
+  // |client| is expected to outlive this instance.
+  //
+  // Creating an instance of this class without a |wrapped_provider| results in
+  // CreateThrottles() returning url_rewrite::URLLoaderThrottle instances but
+  // no other URLLoaderThrottle instances.
+  WrappingURLLoaderThrottleProvider(
+      std::unique_ptr<blink::URLLoaderThrottleProvider> wrapped_provider,
+      Client& client);
+  explicit WrappingURLLoaderThrottleProvider(Client& client);
+  ~WrappingURLLoaderThrottleProvider() override;
+
+  WrappingURLLoaderThrottleProvider(const WrappingURLLoaderThrottleProvider&) =
+      delete;
+  WrappingURLLoaderThrottleProvider& operator=(
+      const WrappingURLLoaderThrottleProvider&) = delete;
+
+  // blink::URLLoaderThrottleProvider implementation.
+  std::unique_ptr<blink::URLLoaderThrottleProvider> Clone() override;
+  blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>> CreateThrottles(
+      int render_frame_id,
+      const blink::WebURLRequest& request) override;
+  void SetOnline(bool is_online) override;
+
+ private:
+  base::raw_ref<Client> client_;
+  std::unique_ptr<blink::URLLoaderThrottleProvider> wrapped_provider_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+}  // namespace cast_receiver
+
+#endif  // COMPONENTS_CAST_RECEIVER_RENDERER_WRAPPING_URL_LOADER_THROTTLE_PROVIDER_H_
diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc
index 157de97..329e8ef 100644
--- a/components/gcm_driver/gcm_driver_desktop.cc
+++ b/components/gcm_driver/gcm_driver_desktop.cc
@@ -28,6 +28,7 @@
 #include "components/gcm_driver/system_encryptor.h"
 #include "google_apis/gcm/engine/account_mapping.h"
 #include "net/base/ip_endpoint.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace gcm {
diff --git a/components/metrics/debug/BUILD.gn b/components/metrics/debug/BUILD.gn
index 9e704993..0ae13d4 100644
--- a/components/metrics/debug/BUILD.gn
+++ b/components/metrics/debug/BUILD.gn
@@ -9,11 +9,34 @@
 
   static_files = [ "metrics_internals.html" ]
 
-  non_web_component_files = [ "metrics_internals.ts" ]
+  web_component_files = [ "app.ts" ]
+  non_web_component_files = [
+    "browser_proxy.ts",
+    "log_utils.ts",
+    "metrics_internals.ts",
+  ]
 
+  html_to_wrapper_template = "native"
+
+  ts_composite = true
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   ts_deps = [ "//ui/webui/resources:library" ]
   ts_use_local_config = false
 
   grit_output_dir = "$root_gen_dir/components"
 }
+
+static_library("debug") {
+  sources = [
+    "metrics_internals_utils.cc",
+    "metrics_internals_utils.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/metrics",
+    "//components/metrics_services_manager",
+    "//components/variations",
+    "//components/variations/service",
+  ]
+}
diff --git a/components/metrics/debug/DEPS b/components/metrics/debug/DEPS
new file mode 100644
index 0000000..c1e737e
--- /dev/null
+++ b/components/metrics/debug/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/metrics_services_manager",
+]
diff --git a/components/metrics/debug/DIR_METADATA b/components/metrics/debug/DIR_METADATA
new file mode 100644
index 0000000..7e7dcdb
--- /dev/null
+++ b/components/metrics/debug/DIR_METADATA
@@ -0,0 +1,3 @@
+monorail {
+  component: "Internals>Metrics"
+}
\ No newline at end of file
diff --git a/components/metrics/debug/app.html b/components/metrics/debug/app.html
new file mode 100644
index 0000000..c4bc114
--- /dev/null
+++ b/components/metrics/debug/app.html
@@ -0,0 +1,134 @@
+<style>
+  cr-tab-box {
+    --tabs-background-color: white;
+  }
+
+  .uma-callout {
+    font-style: italic;
+  }
+
+  th {
+    text-align: start;
+  }
+
+  .uma-header-type {
+    min-width: 100px;
+  }
+
+  .uma-header-hash {
+    min-width: 300px;
+  }
+
+  .uma-header-timestamp {
+    min-width: 140px;
+  }
+
+  .uma-header-size {
+    min-width: 70px;
+  }
+
+  .uma-header-status {
+    min-width: 500px;
+  }
+
+  .uma-log-events {
+    width: 100%;
+  }
+
+  .uma-log-events-peek {
+    display: flex;
+  }
+
+  .uma-log-events-peek:hover {
+    background-color: #eee;
+    cursor: pointer;
+  }
+
+  .uma-log-events pre {
+    display: none;
+    margin: 0;
+  }
+
+  .uma-log-events-expanded pre {
+    display: block;
+    max-height: 100px;
+    min-height: 20px;
+    overflow-y: scroll;
+  }
+
+  .uma-log-events .expand-or-collapse-icon {
+    content: url(chrome://resources/images/icon_expand_more.svg);
+    margin-inline-start: auto;
+    width: 15px;
+  }
+
+  .uma-log-events-expanded .expand-or-collapse-icon {
+    content: url(chrome://resources/images/icon_expand_less.svg);
+  }
+
+  tbody tr:nth-child(odd) {
+    background: rgb(239, 243, 255);
+  }
+
+  #export-uma-logs {
+    margin-top: 10px;
+  }
+</style>
+<cr-tab-box>
+  <div slot="tab">UMA</div>
+  <div slot="panel">
+    <h2>UMA Summary</h2>
+    <table>
+      <tbody id="uma-summary-body"></tbody>
+    </table>
+    <h2>Logs</h2>
+    <div class="uma-callout">
+      List of UMA logs closed since opening this page.
+    </div>
+    <div class="uma-callout">Proto data is available by exporting.</div>
+    <table>
+      <thead>
+        <tr>
+          <th class="uma-header-type">Type</th>
+          <th class="uma-header-hash">Hash</th>
+          <th class="uma-header-timestamp">Closed Timestamp</th>
+          <th class="uma-header-size">Size</th>
+          <th class="uma-header-status">Status</th>
+        </tr>
+      </thead>
+      <tbody id="uma-logs-body"></tbody>
+    </table>
+    <button id="export-uma-logs">Export logs</button>
+  </div>
+  <div slot="tab">Variations</div>
+  <div slot="panel">
+    <h2>Variations Summary</h2>
+    <table>
+      <tbody id="variations-summary-body"></tbody>
+    </table>
+  </div>
+</cr-tab-box>
+<template id="uma-log-row-template">
+  <tr>
+    <td><!-- Type --></td>
+    <td><!-- Hash --></td>
+    <td><!-- Closed Timestamp --></td>
+    <td><!-- Size -->/td>
+    <td>
+      <!-- Status -->
+      <div class="uma-log-events">
+        <div class="uma-log-events-peek">
+          <span class="uma-log-events-peek-text"></span>
+          <span class="expand-or-collapse-icon"></span>
+        </div>
+        <pre class="uma-log-events-text"></pre>
+      </div>
+    </td>
+  </tr>
+</template>
+<template id="summary-row-template">
+  <tr>
+    <td><!-- Key --></td>
+    <td><!-- Value --></td>
+  </tr>
+</template>
\ No newline at end of file
diff --git a/components/metrics/debug/app.ts b/components/metrics/debug/app.ts
new file mode 100644
index 0000000..fc2ce18
--- /dev/null
+++ b/components/metrics/debug/app.ts
@@ -0,0 +1,247 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_tab_box/cr_tab_box.js';
+
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {addWebUiListener} from 'chrome://resources/js/cr.js';
+import {CustomElement} from 'chrome://resources/js/custom_element.js';
+
+import {getTemplate} from './app.html.js';
+import {KeyValue, Log, LogData, MetricsInternalsBrowserProxy, MetricsInternalsBrowserProxyImpl} from './browser_proxy.js';
+import {getEventsPeekString, logEventToString, sizeToString, timestampToString, umaLogTypeToString} from './log_utils.js';
+
+/**
+ * An empty log. It is appended to a logs table when there are no logs (for
+ * purely aesthetic reasons).
+ */
+const EMPTY_LOG: Log = {
+  type: 'N/A',
+  hash: 'N/A',
+  timestamp: '',
+  size: -1,
+  events: [],
+};
+
+export class MetricsInternalsAppElement extends CustomElement {
+  static get is(): string {
+    return 'metrics-internals-app';
+  }
+
+  static override get template() {
+    return getTemplate();
+  }
+
+  /**
+   * Resolves once the component has finished loading.
+   */
+  initPromise: Promise<void>;
+
+  private browserProxy_: MetricsInternalsBrowserProxy =
+      MetricsInternalsBrowserProxyImpl.getInstance();
+
+  /**
+   * Previous summary tables data. Used to prevent re-renderings of the tables
+   * when the data has not changed.
+   */
+  private previousVariationsSummaryData_: string = '';
+  private previousUmaSummaryData_: string = '';
+
+  constructor() {
+    super();
+    this.initPromise = this.init_();
+  }
+
+  /**
+   * Returns UMA logs data (with their proto) as a JSON string. Used when
+   * exporting UMA logs data. Returns a promise.
+   */
+  getUmaLogsExportContent(): Promise<string> {
+    return this.browserProxy_.getUmaLogData(/*includeLogProtoData*/ true);
+  }
+
+  private async init_(): Promise<void> {
+    // Fetch variations summary data and set up a recurring timer.
+    await this.updateVariationsSummary_();
+    setInterval(() => this.updateVariationsSummary_(), 3000);
+
+    // Fetch UMA summary data and set up a recurring timer.
+    await this.updateUmaSummary_();
+    setInterval(() => this.updateUmaSummary_(), 3000);
+
+    // Set up a listener for UMA logs. Also update UMA log data immediately in
+    // case there are logs that we already have data on.
+    addWebUiListener(
+        'uma-log-created-or-event', () => this.updateUmaLogsData_());
+    await this.updateUmaLogsData_();
+
+    // Set up the UMA "Export logs" button.
+    const exportUmaLogsButton = this.$('#export-uma-logs') as HTMLElement;
+    exportUmaLogsButton.addEventListener('click', () => this.exportUmaLogs_());
+  }
+
+  /**
+   * Callback function to expand/collapse an element on click.
+   * @param e The click event.
+   */
+  private toggleEventsExpand_(e: MouseEvent): void {
+    let umaLogEventsDiv = e.target as HTMLElement;
+
+    // It is possible we have clicked a descendant. Keep checking the parent
+    // until we are the the root div of the events.
+    while (!umaLogEventsDiv.classList.contains('uma-log-events')) {
+      umaLogEventsDiv = umaLogEventsDiv.parentElement as HTMLElement;
+    }
+    umaLogEventsDiv.classList.toggle('uma-log-events-expanded');
+  }
+
+  /**
+   * Fills the passed table element with the given summary.
+   */
+  private updateSummaryTable_(tableBody: HTMLElement, summary: KeyValue[]):
+      void {
+    // Clear the table first.
+    tableBody.replaceChildren();
+
+    const template = this.$('#summary-row-template') as HTMLTemplateElement;
+    for (const info of summary) {
+      const row = template.content.cloneNode(true) as HTMLElement;
+      const [key, value] = row.querySelectorAll('td');
+
+      assert(key);
+      key.textContent = info.key;
+
+      assert(value);
+      value.textContent = info.value;
+
+      tableBody.appendChild(row);
+    }
+  }
+
+  /**
+   * Fetches variations summary data and updates the view.
+   */
+  private async updateVariationsSummary_(): Promise<void> {
+    const summary: KeyValue[] =
+        await this.browserProxy_.fetchVariationsSummary();
+    const variationsSummaryTableBody =
+        this.$('#variations-summary-body') as HTMLElement;
+
+    // Don't re-render the table if the data has not changed.
+    const newDataString = summary.toString();
+    if (newDataString === this.previousVariationsSummaryData_) {
+      return;
+    }
+
+    this.previousVariationsSummaryData_ = newDataString;
+    this.updateSummaryTable_(variationsSummaryTableBody, summary);
+  }
+
+  /**
+   * Fetches UMA summary data and updates the view.
+   */
+  private async updateUmaSummary_(): Promise<void> {
+    const summary: KeyValue[] = await this.browserProxy_.fetchUmaSummary();
+    const umaSummaryTableBody = this.$('#uma-summary-body') as HTMLElement;
+
+    // Don't re-render the table if the data has not changed.
+    const newDataString = summary.toString();
+    if (newDataString === this.previousUmaSummaryData_) {
+      return;
+    }
+
+    this.previousUmaSummaryData_ = newDataString;
+    this.updateSummaryTable_(umaSummaryTableBody, summary);
+  }
+
+  /**
+   * Fills the passed table element with the given logs.
+   */
+  private updateLogsTable_(tableBody: HTMLElement, logs: Log[]): void {
+    // Clear the table first.
+    tableBody.replaceChildren();
+
+    const template = this.$('#uma-log-row-template') as HTMLTemplateElement;
+
+    for (const log of logs) {
+      const row = template.content.cloneNode(true) as HTMLElement;
+      const [type, hash, timestamp, size, events] = row.querySelectorAll('td');
+
+      assert(type);
+      type.textContent = umaLogTypeToString(log.type);
+
+      assert(hash);
+      hash.textContent = log.hash;
+
+      assert(timestamp);
+      timestamp.textContent = timestampToString(log.timestamp);
+
+      assert(size);
+      size.textContent = sizeToString(log.size);
+
+      assert(events);
+      const eventsPeekDiv =
+          events.querySelector<HTMLElement>('.uma-log-events-peek');
+      assert(eventsPeekDiv);
+      eventsPeekDiv.addEventListener('click', this.toggleEventsExpand_, false);
+      const eventsPeekText =
+          events.querySelector<HTMLElement>('.uma-log-events-peek-text');
+      assert(eventsPeekText);
+      eventsPeekText.textContent = getEventsPeekString(log.events);
+      const eventsText =
+          events.querySelector<HTMLElement>('.uma-log-events-text');
+      assert(eventsText);
+      for (const event of log.events) {
+        const div = document.createElement('div');
+        div.textContent = logEventToString(event);
+        eventsText.appendChild(div);
+      }
+
+      tableBody.appendChild(row);
+    }
+  }
+
+  /**
+   * Fetches the latest UMA logs and renders them. This is called when the page
+   * is loaded and whenever there is a log that created or changed.
+   */
+  private async updateUmaLogsData_(): Promise<void> {
+    const logsData: string =
+        await this.browserProxy_.getUmaLogData(/*includeLogProtoData=*/ false);
+    const logs: LogData = JSON.parse(logsData);
+    // If there are no logs, append an empty log. This is purely for aesthetic
+    // reasons. Otherwise, the table may look confusing.
+    if (!logs.logs.length) {
+      logs.logs = [EMPTY_LOG];
+    }
+
+    // We don't compare the new data with the old data to prevent re-renderings
+    // because this should only be called when there is an actual change.
+
+    const umaLogsTableBody = this.$('#uma-logs-body') as HTMLElement;
+    this.updateLogsTable_(umaLogsTableBody, logs.logs);
+  }
+
+  /**
+   * Exports the accumulated UMA logs, including their proto data, as a JSON
+   * file. This will initiate a download.
+   */
+  private async exportUmaLogs_(): Promise<void> {
+    const logsData: string = await this.getUmaLogsExportContent();
+    const file = new Blob([logsData], {type: 'text/plain'});
+    const a = document.createElement('a');
+    a.href = URL.createObjectURL(file);
+    a.download = `uma_logs_${new Date().getTime()}.json`;
+    a.click();
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'metrics-internals-app': MetricsInternalsAppElement;
+  }
+}
+
+customElements.define(
+    MetricsInternalsAppElement.is, MetricsInternalsAppElement);
diff --git a/components/metrics/debug/browser_proxy.ts b/components/metrics/debug/browser_proxy.ts
new file mode 100644
index 0000000..c015aa2e
--- /dev/null
+++ b/components/metrics/debug/browser_proxy.ts
@@ -0,0 +1,107 @@
+// 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 {sendWithPromise} from 'chrome://resources/js/cr.js';
+
+/**
+ * @fileoverview A helper object used by the chrome://metrics-internals page to
+ * interact with the browser.
+ */
+
+/**
+ * A pair of strings representing a row in a summary table. For example, |key|
+ * could be "Platform", with |value| being "Android".
+ */
+export interface KeyValue {
+  key: string;
+  value: string;
+}
+
+/**
+ * An individual event that occurred on a log. Optionally, this may include a
+ * message. For example, for a "Trimmed" event, the message could be "Log size
+ * too large".
+ */
+export interface LogEvent {
+  event: string;
+  timestamp: string;
+  message?: string;
+}
+
+/**
+ * A log and its data, including the events that occurred throughout its
+ * lifetime. The |type| field is only set for UMA logs (i.e., ongoing,
+ * independent, or stability). The |compressed_data| field (i.e., its proto
+ * data) is only set when exporting.
+ * TODO(crbug/1363747): Change name of |type| to something else, since it is
+ * confusing and can be mistaken for |logType| in LogData (UMA or UKM).
+ */
+export interface Log {
+  type?: string;
+  hash: string;
+  timestamp: string;
+  data?: string;
+  size: number;
+  events: LogEvent[];
+}
+
+/**
+ * A list of logs, as well as their type (UMA or UKM).
+ */
+export interface LogData {
+  logType: string;
+  logs: Log[];
+}
+
+export interface MetricsInternalsBrowserProxy {
+  /**
+   * Signals to the browser that the page is ready to receive messages.
+   */
+  ready(): Promise<void>;
+
+  /**
+   * Gets UMA log data. |includeLogProtoData| determines whether or not the
+   * fetched data should also include the protos of the logs.
+   */
+  getUmaLogData(includeLogProtoData: boolean): Promise<string>;
+
+  /**
+   * Fetches a summary of variations info.
+   */
+  fetchVariationsSummary(): Promise<KeyValue[]>;
+
+  /**
+   * Fetches a summary of UMA info.
+   */
+  fetchUmaSummary(): Promise<KeyValue[]>;
+}
+
+export class MetricsInternalsBrowserProxyImpl implements
+    MetricsInternalsBrowserProxy {
+  ready(): Promise<void> {
+    return sendWithPromise('ready');
+  }
+
+  getUmaLogData(includeLogProtoData: boolean): Promise<string> {
+    return sendWithPromise('fetchUmaLogsData', includeLogProtoData);
+  }
+
+  fetchVariationsSummary(): Promise<KeyValue[]> {
+    return sendWithPromise('fetchVariationsSummary');
+  }
+
+  fetchUmaSummary(): Promise<KeyValue[]> {
+    return sendWithPromise('fetchUmaSummary');
+  }
+
+  static getInstance(): MetricsInternalsBrowserProxy {
+    return instance || (instance = new MetricsInternalsBrowserProxyImpl());
+  }
+
+  static setInstance(obj: MetricsInternalsBrowserProxy) {
+    instance = obj;
+  }
+}
+
+let instance: MetricsInternalsBrowserProxy|null = null;
diff --git a/components/metrics/debug/log_utils.ts b/components/metrics/debug/log_utils.ts
new file mode 100644
index 0000000..a5991f0
--- /dev/null
+++ b/components/metrics/debug/log_utils.ts
@@ -0,0 +1,82 @@
+// 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 {assert} from 'chrome://resources/js/assert_ts.js';
+
+import {LogEvent} from './browser_proxy.js';
+
+/**
+ * Helper function to convert undefined UMA log types to "Unknown" string.
+ * @param type The UMA log's type (i.e., ongoing, independent, or stability).
+ * @returns The UMA log's type. "Unknown" if type is undefined.
+ */
+export function umaLogTypeToString(type: string|undefined) {
+  if (!type) {
+    return 'Unknown';
+  }
+  return type;
+}
+
+/**
+ * Converts a given Unix timestamp into a human-readable string.
+ * @param timestampSeconds The timestamp string (seconds since Epoch).
+ * @return A human-readable representation of the timestamp (e.g "01/01/1970,
+ *     12:00:00 AM").
+ */
+export function timestampToString(timestampSeconds: string) {
+  if (!timestampSeconds.length) {
+    // This case should not normally happen, but can happen when the table is
+    // empty (a dummy log |EMPTY_LOG| is added, which has an empty timestamp).
+    return 'N/A';
+  }
+
+  const timestampInt = parseInt(timestampSeconds);
+  assert(!isNaN(timestampInt));
+  // Multiply by 1000 since the constructor expects milliseconds, but the
+  // timestamps are in seconds.
+  return new Date(timestampInt * 1000).toLocaleString();
+}
+
+/**
+ * Converts the size of a log to a human-readable string.
+ * @param size The size of the log in bytes.
+ * @returns The size of the log in KiB as a string.
+ */
+export function sizeToString(size: number) {
+  if (size < 0) {
+    // This case should not normally happen, but can happen when the table is
+    // empty (a dummy log |EMPTY_LOG| is added, which has size -1).
+    return 'N/A';
+  }
+  return `${(size / 1024).toFixed(2)} KiB`;
+}
+
+/**
+ * Converts a log event to a human-readable string.
+ * @param event The log event.
+ * @returns A human-readable string of the log event.
+ */
+export function logEventToString(event: LogEvent) {
+  let result = `[${timestampToString(event.timestamp)}] ${event.event}`;
+  if (event.message) {
+    result += ` (${event.message})`;
+  }
+  return result;
+}
+
+/**
+ * Gets the string to display when the events div of a log are collapsed.
+ * @param events The list of events of the log.
+ * @returns A human-readable string of the last event that occurred.
+ */
+export function getEventsPeekString(events: LogEvent[]) {
+  if (!events.length) {
+    return 'N/A';
+  }
+  // Need to assert that last element exists, otherwise the call to
+  // logEventToString() fails to compile.
+  const lastEvent = events[events.length - 1];
+  assert(lastEvent);
+  return logEventToString(lastEvent);
+}
diff --git a/components/metrics/debug/metrics_internals.html b/components/metrics/debug/metrics_internals.html
index 4703f23..c6336e8 100644
--- a/components/metrics/debug/metrics_internals.html
+++ b/components/metrics/debug/metrics_internals.html
@@ -2,13 +2,12 @@
 <html lang="en">
 <head>
   <meta charset="utf-8">
-  <meta name="color-scheme" content="light dark">
-  <title>UMA Debug Page</title>
+  <title>Metrics Internals</title>
+  <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
   <script type="module" src="metrics_internals.js"></script>
 </head>
 <body>
-  <pre id="content">
-    Loading...
-  </pre>
+  <metrics-internals-app></metrics-internals-app>
+  <script src="app.js" type="module"></script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/components/metrics/debug/metrics_internals.ts b/components/metrics/debug/metrics_internals.ts
index ef1a3d8..3783dd70 100644
--- a/components/metrics/debug/metrics_internals.ts
+++ b/components/metrics/debug/metrics_internals.ts
@@ -2,11 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {sendWithPromise} from 'chrome://resources/js/cr.js';
-import {getRequiredElement} from 'chrome://resources/js/util_ts.js';
-
-document.addEventListener('DOMContentLoaded', function() {
-  sendWithPromise('fetchClientId').then((clientId: string) => {
-    getRequiredElement('content').textContent = clientId;
-  });
+window.addEventListener('beforeunload', function(event) {
+  // Since leaving the page discards the accumulated logs, show a confirmation
+  // dialog when the user attempts to leave.
+  event.returnValue = '';
 });
diff --git a/components/metrics/debug/metrics_internals_utils.cc b/components/metrics/debug/metrics_internals_utils.cc
new file mode 100644
index 0000000..c32f2c792
--- /dev/null
+++ b/components/metrics/debug/metrics_internals_utils.cc
@@ -0,0 +1,149 @@
+// 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 "components/metrics/debug/metrics_internals_utils.h"
+
+#include "base/strings/string_piece.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/variations/client_filterable_state.h"
+#include "components/variations/proto/study.pb.h"
+#include "components/variations/service/variations_service.h"
+
+namespace metrics {
+namespace {
+
+std::string ChannelToString(variations::Study::Channel channel) {
+  switch (channel) {
+    case variations::Study::UNKNOWN:
+      return "Unknown";
+    case variations::Study::CANARY:
+      return "Canary";
+    case variations::Study::DEV:
+      return "Dev";
+    case variations::Study::BETA:
+      return "Beta";
+    case variations::Study::STABLE:
+      return "Stable";
+  }
+  NOTREACHED();
+}
+
+std::string PlatformToString(variations::Study::Platform platform) {
+  switch (platform) {
+    case variations::Study::PLATFORM_WINDOWS:
+      return "Windows";
+    case variations::Study::PLATFORM_MAC:
+      return "Mac";
+    case variations::Study::PLATFORM_LINUX:
+      return "Linux";
+    case variations::Study::PLATFORM_CHROMEOS:
+      return "ChromeOS";
+    case variations::Study::PLATFORM_ANDROID:
+      return "Android";
+    case variations::Study::PLATFORM_IOS:
+      return "iOS";
+    case variations::Study::PLATFORM_ANDROID_WEBVIEW:
+      return "WebView";
+    case variations::Study::PLATFORM_FUCHSIA:
+      return "Fuchsia";
+    case variations::Study::PLATFORM_ANDROID_WEBLAYER:
+      return "WebLayer";
+    case variations::Study::PLATFORM_CHROMEOS_LACROS:
+      return "ChromeOS Lacros";
+  }
+  NOTREACHED();
+}
+
+std::string CpuArchitectureToString(
+    variations::Study::CpuArchitecture cpu_architecture) {
+  switch (cpu_architecture) {
+    case variations::Study::X86_64:
+      return "x86_64";
+    case variations::Study::ARM64:
+      return "arm64";
+    case variations::Study::X86_32:
+      return "x86_32";
+    case variations::Study::ARM32:
+      return "arm32";
+    case variations::Study::TRANSLATED_X86_64:
+      return "translated_x86_64";
+  }
+  NOTREACHED();
+}
+
+std::string FormFactorToString(variations::Study::FormFactor form_factor) {
+  switch (form_factor) {
+    case variations::Study::DESKTOP:
+      return "Desktop";
+    case variations::Study::PHONE:
+      return "Phone";
+    case variations::Study::TABLET:
+      return "Tablet";
+    case variations::Study::KIOSK:
+      return "Kiosk";
+    case variations::Study::MEET_DEVICE:
+      return "Meet Device";
+  }
+  NOTREACHED();
+}
+
+std::string BoolToString(bool val) {
+  return val ? "Yes" : "No";
+}
+
+base::Value::Dict CreateKeyValueDict(base::StringPiece key,
+                                     base::StringPiece value) {
+  base::Value::Dict dict;
+  dict.Set("key", key);
+  dict.Set("value", value);
+  return dict;
+}
+
+}  // namespace
+
+base::Value::List GetUmaSummary(MetricsService* metrics_service) {
+  base::Value::List list;
+  list.Append(CreateKeyValueDict("Client ID", metrics_service->GetClientId()));
+  // TODO(crbug/1363747): Add the server-side client ID.
+  list.Append(CreateKeyValueDict(
+      "Metrics Reporting Enabled",
+      BoolToString(metrics_service->IsMetricsReportingEnabled())));
+  list.Append(
+      CreateKeyValueDict("Currently Recording",
+                         BoolToString(metrics_service->recording_active())));
+  list.Append(
+      CreateKeyValueDict("Currently Reporting",
+                         BoolToString(metrics_service->reporting_active())));
+  return list;
+}
+
+base::Value::List GetVariationsSummary(
+    metrics_services_manager::MetricsServicesManager* metrics_service_manager) {
+  base::Value::List list;
+  std::unique_ptr<variations::ClientFilterableState> state =
+      metrics_service_manager->GetVariationsService()
+          ->GetClientFilterableStateForVersion();
+  list.Append(CreateKeyValueDict("Channel", ChannelToString(state->channel)));
+  list.Append(CreateKeyValueDict("Version", state->version.GetString()));
+  list.Append(
+      CreateKeyValueDict("Platform", PlatformToString(state->platform)));
+  list.Append(CreateKeyValueDict("OS Version", state->os_version.GetString()));
+  list.Append(CreateKeyValueDict(
+      "CPU Architecture", CpuArchitectureToString(state->cpu_architecture)));
+  list.Append(CreateKeyValueDict("Hardware Class", state->hardware_class));
+  list.Append(CreateKeyValueDict("Form Factor",
+                                 FormFactorToString(state->form_factor)));
+  list.Append(CreateKeyValueDict("Low End Device",
+                                 BoolToString(state->is_low_end_device)));
+  list.Append(CreateKeyValueDict("Country (Session Consistency)",
+                                 state->session_consistency_country));
+  list.Append(CreateKeyValueDict("Country (Permanent Consistency)",
+                                 state->permanent_consistency_country));
+  list.Append(CreateKeyValueDict("Locale", state->locale));
+  list.Append(
+      CreateKeyValueDict("Enterprise", BoolToString(state->IsEnterprise())));
+  return list;
+}
+
+}  // namespace metrics
diff --git a/components/metrics/debug/metrics_internals_utils.h b/components/metrics/debug/metrics_internals_utils.h
new file mode 100644
index 0000000..2759f22
--- /dev/null
+++ b/components/metrics/debug/metrics_internals_utils.h
@@ -0,0 +1,21 @@
+// 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 COMPONENTS_METRICS_DEBUG_METRICS_INTERNALS_UTILS_H_
+#define COMPONENTS_METRICS_DEBUG_METRICS_INTERNALS_UTILS_H_
+
+#include "base/values.h"
+#include "components/metrics/metrics_service.h"
+#include "components/metrics_services_manager/metrics_services_manager.h"
+
+namespace metrics {
+
+base::Value::List GetUmaSummary(MetricsService* metrics_service);
+
+base::Value::List GetVariationsSummary(
+    metrics_services_manager::MetricsServicesManager* metrics_service_manager);
+
+}  // namespace metrics
+
+#endif  // COMPONENTS_METRICS_DEBUG_METRICS_INTERNALS_UTILS_H_
diff --git a/components/metrics/metrics_service_observer.cc b/components/metrics/metrics_service_observer.cc
index 2199e44..e99c1a8 100644
--- a/components/metrics/metrics_service_observer.cc
+++ b/components/metrics/metrics_service_observer.cc
@@ -5,6 +5,7 @@
 #include "components/metrics/metrics_service_observer.h"
 
 #include "base/base64.h"
+#include "base/callback_list.h"
 #include "base/json/json_string_value_serializer.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
@@ -75,6 +76,9 @@
 
   indexed_logs_.emplace(log->hash, log.get());
   logs_.push_back(std::move(log));
+
+  // Call all registered callbacks.
+  notified_callbacks_.Notify();
 }
 
 void MetricsServiceObserver::OnLogEvent(MetricsLogsEventManager::LogEvent event,
@@ -94,6 +98,9 @@
   if (!message.empty())
     log_event.message = std::string(message);
   log->events.push_back(std::move(log_event));
+
+  // Call all registered callbacks.
+  notified_callbacks_.Notify();
 }
 
 void MetricsServiceObserver::OnLogType(
@@ -140,14 +147,18 @@
   // Create a last |dict| that contains all the logs and |service_type_|,
   // convert it to a JSON string, and write it to |json_output|.
   base::Value::Dict dict;
-  dict.Set("log_type",
-           service_type_ == MetricsServiceType::UMA ? "UMA" : "UKM");
+  dict.Set("logType", service_type_ == MetricsServiceType::UMA ? "UMA" : "UKM");
   dict.Set("logs", std::move(logs_list));
 
   JSONStringValueSerializer serializer(json_output);
   return serializer.Serialize(dict);
 }
 
+base::CallbackListSubscription MetricsServiceObserver::AddNotifiedCallback(
+    base::RepeatingClosure callback) {
+  return notified_callbacks_.Add(callback);
+}
+
 MetricsServiceObserver::Log* MetricsServiceObserver::GetLogFromHash(
     base::StringPiece log_hash) {
   auto it = indexed_logs_.find(log_hash);
diff --git a/components/metrics/metrics_service_observer.h b/components/metrics/metrics_service_observer.h
index 115eeea..826748a 100644
--- a/components/metrics/metrics_service_observer.h
+++ b/components/metrics/metrics_service_observer.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback_list.h"
 #include "base/containers/flat_map.h"
 #include "base/strings/string_piece.h"
 #include "components/metrics/metrics_logs_event_manager.h"
@@ -101,7 +102,7 @@
   // The format of the JSON object is as follows:
   //
   // {
-  //   log_type: string, // e.g. "UMA" or "UKM"
+  //   logType: string, // e.g. "UMA" or "UKM"
   //   logs: [
   //     {
   //       type?: string, // e.g. "Ongoing" (set only for UMA logs)
@@ -127,6 +128,13 @@
   // serialized protobuf. The "size" field is the size (in bytes) of the log.
   bool ExportLogsAsJson(bool include_log_proto_data, std::string* json_output);
 
+  // Registers a callback. This callback will be run every time this observer is
+  // notified through OnLogCreated() or OnLogEvent(). When the returned
+  // CallbackListSubscription is destroyed, the callback is automatically
+  // de-registered.
+  [[nodiscard]] base::CallbackListSubscription AddNotifiedCallback(
+      base::RepeatingClosure callback);
+
   // Returns |logs_|.
   std::vector<std::unique_ptr<Log>>* logs_for_testing() { return &logs_; }
 
@@ -152,6 +160,10 @@
   // are being created. This should only be set for UMA logs, since the concept
   // of log type only exists in UMA.
   absl::optional<MetricsLog::LogType> uma_log_type_;
+
+  // List of callbacks to run whenever this observer is notified. Note that
+  // OnLogType() will not trigger the callbacks.
+  base::RepeatingClosureList notified_callbacks_;
 };
 
 }  // namespace metrics
diff --git a/components/metrics/metrics_service_observer_unittest.cc b/components/metrics/metrics_service_observer_unittest.cc
index 08f59715..966e601 100644
--- a/components/metrics/metrics_service_observer_unittest.cc
+++ b/components/metrics/metrics_service_observer_unittest.cc
@@ -4,9 +4,13 @@
 
 #include "components/metrics/metrics_service_observer.h"
 
+#include "base/base64.h"
+#include "base/callback_list.h"
 #include "base/json/json_string_value_serializer.h"
+#include "base/test/bind.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/metrics/log_decoder.h"
 #include "components/metrics/metrics_log.h"
 #include "components/metrics/metrics_logs_event_manager.h"
 #include "components/metrics/metrics_pref_names.h"
@@ -17,6 +21,7 @@
 #include "components/metrics/unsent_log_store_metrics_impl.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
 
 namespace metrics {
 namespace {
@@ -495,7 +500,7 @@
   ASSERT_TRUE(logs_value->is_dict());
   base::Value::Dict& logs_dict = logs_value->GetDict();
 
-  base::Value* log_type = logs_dict.Find("log_type");
+  base::Value* log_type = logs_dict.Find("logType");
   ASSERT_TRUE(log_type);
   ASSERT_TRUE(log_type->is_string());
   EXPECT_EQ(log_type->GetString(), "UMA");
@@ -530,6 +535,15 @@
     ASSERT_TRUE(log_data);
     ASSERT_TRUE(log_data->is_string());
     EXPECT_FALSE(log_data->GetString().empty());
+
+    // Verify that the proto data can be parsed.
+    std::string gzipped_log_data;
+    ASSERT_TRUE(base::Base64Decode(log_data->GetString(), &gzipped_log_data));
+    ChromeUserMetricsExtension uma_proto;
+    ASSERT_TRUE(DecodeLogDataToProto(gzipped_log_data, &uma_proto));
+    EXPECT_TRUE(uma_proto.has_client_id());
+    EXPECT_TRUE(uma_proto.has_session_id());
+    EXPECT_TRUE(uma_proto.has_system_profile());
   } else {
     EXPECT_FALSE(log_data);
   }
@@ -573,4 +587,42 @@
   service.RemoveLogsObserver(&logs_observer);
 }
 
+// Verifies that callbacks registered to a MetricsServiceObserver instance are
+// run every time it is notified.
+TEST_F(MetricsServiceObserverTest, NotifiedCallbacks) {
+  // Create a MetricsServiceObserver.
+  MetricsServiceObserver logs_observer(
+      MetricsServiceObserver::MetricsServiceType::UMA);
+
+  int num_callback_executed = 0;
+
+  {
+    // Add a callback to |logs_observer| that increments |num_callback_executed|
+    // every time it is run.
+    base::CallbackListSubscription callback_subscription =
+        logs_observer.AddNotifiedCallback(base::BindLambdaForTesting(
+            [&num_callback_executed]() { num_callback_executed++; }));
+    EXPECT_EQ(num_callback_executed, 0);
+
+    // Verify that OnLogCreated() triggers the callback.
+    const std::string kLogHash = "test";
+    logs_observer.OnLogCreated(kLogHash, /*log_data=*/"", /*log_timestamp=*/"");
+    EXPECT_EQ(num_callback_executed, 1);
+
+    // Verify that OnLogEvent() triggers the callback.
+    logs_observer.OnLogEvent(MetricsLogsEventManager::LogEvent::kLogStaged,
+                             kLogHash, /*message=*/"");
+    EXPECT_EQ(num_callback_executed, 2);
+  }
+
+  // Verify that after the callback list subscription |callback_subscription| is
+  // destroyed, it is automatically de-registered from |logs_observer|.
+  const std::string kLogHash2 = "test2";
+  num_callback_executed = 0;
+  logs_observer.OnLogCreated(kLogHash2, /*log_data=*/"", /*log_timestamp=*/"");
+  logs_observer.OnLogEvent(MetricsLogsEventManager::LogEvent::kLogStaged,
+                           kLogHash2, /*message=*/"");
+  EXPECT_EQ(num_callback_executed, 0);
+}
+
 }  // namespace metrics
diff --git a/components/omnibox/browser/history_quick_provider_unittest.cc b/components/omnibox/browser/history_quick_provider_unittest.cc
index 6b98d95..c2c2060 100644
--- a/components/omnibox/browser/history_quick_provider_unittest.cc
+++ b/components/omnibox/browser/history_quick_provider_unittest.cc
@@ -831,7 +831,7 @@
                                            const std::u16string& input_term) {
   return ScoredHistoryMatch(history::URLRow(GURL(url_text)), VisitInfoVector(),
                             input_term, String16Vector(1, input_term),
-                            WordStarts(1, 0), RowWordStarts(), false, 0,
+                            WordStarts(1, 0), RowWordStarts(), false, 0, false,
                             base::Time());
 }
 
@@ -907,7 +907,7 @@
   word_starts.url_word_starts_ = {0};
   ScoredHistoryMatch sh_match(history::URLRow(GURL("http://cr/")),
                               VisitInfoVector(), u"cr/", {u"cr"}, {0},
-                              word_starts, false, 0, base::Time());
+                              word_starts, false, 0, false, base::Time());
   AutocompleteMatch ac_match(QuickMatchToACMatch(sh_match, 0));
   EXPECT_EQ(u"cr/", ac_match.fill_into_edit);
   EXPECT_EQ(u"", ac_match.inline_autocompletion);
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 68a779d..33a2f3a 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -968,6 +968,11 @@
     "DomainSuggestionsMaxMatchesPerDomain",
     2);
 
+const base::FeatureParam<double> kDomainSuggestionsScoreFactor(
+    &omnibox::kDomainSuggestions,
+    "DomainSuggestionsScoreFactor",
+    1);
+
 }  // namespace OmniboxFieldTrial
 
 std::string OmniboxFieldTrial::internal::GetValueForRuleInContext(
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 7e29f33..479576b 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -582,6 +582,10 @@
 extern const base::FeatureParam<int> kDomainSuggestionsMinInputLength;
 // The maximum number of matches per domain to suggest.
 extern const base::FeatureParam<int> kDomainSuggestionsMaxMatchesPerDomain;
+// The scoring factor used to boost HQP suggestions from highly visited domains.
+// A value of 1 is the control behavior. A value of 2 will boost scores, but not
+// necessarily double them due to how HQP maps the factors to actual scores.
+extern const base::FeatureParam<double> kDomainSuggestionsScoreFactor;
 
 // New params should be inserted above this comment. They should be ordered
 // consistently with `omnibox_features.h`. They should be formatted as:
diff --git a/components/omnibox/browser/scored_history_match.cc b/components/omnibox/browser/scored_history_match.cc
index 34fb5e2..673b9531 100644
--- a/components/omnibox/browser/scored_history_match.cc
+++ b/components/omnibox/browser/scored_history_match.cc
@@ -131,6 +131,7 @@
                          RowWordStarts(),
                          false,
                          1,
+                         false,
                          base::Time::Max()) {}
 
 ScoredHistoryMatch::ScoredHistoryMatch(
@@ -142,6 +143,7 @@
     const RowWordStarts& word_starts,
     bool is_url_bookmarked,
     size_t num_matching_pages,
+    bool is_highly_visited_host,
     base::Time now)
     : raw_score(0) {
   // Initialize HistoryMatch fields. TODO(tommycli): Merge these two classes.
@@ -278,8 +280,15 @@
   const float frequency_score = GetFrequency(now, is_url_bookmarked, visits);
   const float specificity_score =
       GetDocumentSpecificityScore(num_matching_pages);
+
+  static float domain_suggestions_score_factor =
+      OmniboxFieldTrial::kDomainSuggestionsScoreFactor.Get();
+  DCHECK_GE(domain_suggestions_score_factor, 1);
+  const float domain_score =
+      is_highly_visited_host ? domain_suggestions_score_factor : 1;
+
   raw_score = base::saturated_cast<int>(GetFinalRelevancyScore(
-      topicality_score, frequency_score, specificity_score));
+      topicality_score, frequency_score, specificity_score, domain_score));
 
   if (also_do_hup_like_scoring_ && likely_can_inline) {
     // HistoryURL-provider-like scoring gives any match that is
@@ -723,7 +732,8 @@
 // static
 float ScoredHistoryMatch::GetFinalRelevancyScore(float topicality_score,
                                                  float frequency_score,
-                                                 float specificity_score) {
+                                                 float specificity_score,
+                                                 float domain_score) {
   // |relevance_buckets| gives a mapping from intermediate score to the final
   // relevance score.
   static base::NoDestructor<ScoreMaxRelevances> default_relevance_buckets(
@@ -760,7 +770,7 @@
   // The score maxes out at 1399 (i.e., cannot beat a good inlineable result
   // from HistoryURL provider).
   const float intermediate_score =
-      topicality_score * frequency_score * specificity_score;
+      topicality_score * frequency_score * specificity_score * domain_score;
 
   // Find the threshold where intermediate score is greater than bucket.
   size_t i = 1;
diff --git a/components/omnibox/browser/scored_history_match.h b/components/omnibox/browser/scored_history_match.h
index 3f9bfd1e..4859ecd 100644
--- a/components/omnibox/browser/scored_history_match.h
+++ b/components/omnibox/browser/scored_history_match.h
@@ -67,6 +67,7 @@
                      const RowWordStarts& word_starts,
                      bool is_url_bookmarked,
                      size_t num_matching_pages,
+                     bool is_highly_visited_host,
                      base::Time now);
 
   ~ScoredHistoryMatch();
@@ -159,11 +160,12 @@
   // user's input.
   float GetDocumentSpecificityScore(size_t num_matching_pages) const;
 
-  // Combines the three component scores into a final score that's
-  // an appropriate value to use as a relevancy score.
+  // Combines the four component scores into a final score that's an appropriate
+  // value to use as a relevancy score.
   static float GetFinalRelevancyScore(float topicality_score,
                                       float frequency_score,
-                                      float specificity_score);
+                                      float specificity_score,
+                                      float domain_score);
 
   // Helper function that returns the string containing the scoring buckets
   // (either the default ones or ones specified in an experiment).
diff --git a/components/omnibox/browser/scored_history_match_unittest.cc b/components/omnibox/browser/scored_history_match_unittest.cc
index 18fc0ae7..da6f6d29 100644
--- a/components/omnibox/browser/scored_history_match_unittest.cc
+++ b/components/omnibox/browser/scored_history_match_unittest.cc
@@ -135,7 +135,7 @@
   row.set_title(title);
   ScoredHistoryMatch scored_match(
       row, VisitInfoVector(), base::UTF8ToUTF16(terms_joint), term_vector,
-      term_word_starts, row_word_starts, false, 1, base::Time::Max());
+      term_word_starts, row_word_starts, false, 1, false, base::Time::Max());
   scored_match.topicality_threshold_ = -1;
   return scored_match.GetTopicalityScore(term_vector.size(), url,
                                          base::OffsetAdjuster::Adjustments(),
@@ -155,7 +155,8 @@
   // Mark one visit as typed.
   visits_a[0].second = ui::PAGE_TRANSITION_TYPED;
   ScoredHistoryMatch scored_a(row_a, visits_a, u"abc", Make1Term("abc"),
-                              one_word_no_offset, word_starts_a, false, 1, now);
+                              one_word_no_offset, word_starts_a, false, 1,
+                              false, now);
 
   // Test scores based on visit_count.
   history::URLRow row_b(MakeURLRow("http://abcdef", "abcd bcd", 10, 30, 1));
@@ -164,7 +165,8 @@
   VisitInfoVector visits_b = CreateVisitInfoVector(10, 30, now);
   visits_b[0].second = ui::PAGE_TRANSITION_TYPED;
   ScoredHistoryMatch scored_b(row_b, visits_b, u"abc", Make1Term("abc"),
-                              one_word_no_offset, word_starts_b, false, 1, now);
+                              one_word_no_offset, word_starts_b, false, 1,
+                              false, now);
   EXPECT_GT(scored_b.raw_score, scored_a.raw_score);
 
   // Test scores based on last_visit.
@@ -174,7 +176,8 @@
   VisitInfoVector visits_c = CreateVisitInfoVector(3, 10, now);
   visits_c[0].second = ui::PAGE_TRANSITION_TYPED;
   ScoredHistoryMatch scored_c(row_c, visits_c, u"abc", Make1Term("abc"),
-                              one_word_no_offset, word_starts_c, false, 1, now);
+                              one_word_no_offset, word_starts_c, false, 1,
+                              false, now);
   EXPECT_GT(scored_c.raw_score, scored_a.raw_score);
 
   // Test scores based on typed_count.
@@ -186,7 +189,8 @@
   visits_d[1].second = ui::PAGE_TRANSITION_TYPED;
   visits_d[2].second = ui::PAGE_TRANSITION_TYPED;
   ScoredHistoryMatch scored_d(row_d, visits_d, u"abc", Make1Term("abc"),
-                              one_word_no_offset, word_starts_d, false, 1, now);
+                              one_word_no_offset, word_starts_d, false, 1,
+                              false, now);
   EXPECT_GT(scored_d.raw_score, scored_a.raw_score);
 
   // Test scores based on a terms appearing multiple times.
@@ -197,13 +201,15 @@
   PopulateWordStarts(row_e, &word_starts_e);
   const VisitInfoVector visits_e = visits_d;
   ScoredHistoryMatch scored_e(row_e, visits_e, u"csi", Make1Term("csi"),
-                              one_word_no_offset, word_starts_e, false, 1, now);
+                              one_word_no_offset, word_starts_e, false, 1,
+                              false, now);
   EXPECT_LT(scored_e.raw_score, 1400);
 
   // Test that a result with only a mid-term match (i.e., not at a word
   // boundary) scores 0.
   ScoredHistoryMatch scored_f(row_a, visits_a, u"cd", Make1Term("cd"),
-                              one_word_no_offset, word_starts_a, false, 1, now);
+                              one_word_no_offset, word_starts_a, false, 1,
+                              false, now);
   EXPECT_EQ(scored_f.raw_score, 0);
 }
 
@@ -220,12 +226,13 @@
   WordStarts one_word_no_offset(1, 0u);
   VisitInfoVector visits = CreateVisitInfoVector(8, 3, now);
   ScoredHistoryMatch scored(row, visits, u"abc", Make1Term("abc"),
-                            one_word_no_offset, word_starts, false, 1, now);
+                            one_word_no_offset, word_starts, false, 1, false,
+                            now);
   // Now check that if URL is bookmarked then its score increases.
   base::AutoReset<float> reset(&ScoredHistoryMatch::bookmark_value_, 5);
   ScoredHistoryMatch scored_with_bookmark(row, visits, u"abc", Make1Term("abc"),
                                           one_word_no_offset, word_starts, true,
-                                          1, now);
+                                          1, false, now);
   EXPECT_GT(scored_with_bookmark.raw_score, scored.raw_score);
 }
 
@@ -243,14 +250,15 @@
   WordStarts two_words_no_offsets(2, 0u);
   VisitInfoVector visits = CreateVisitInfoVector(8, 3, now);
   ScoredHistoryMatch scored(row, visits, u"fed com", Make2Terms("fed", "com"),
-                            two_words_no_offsets, word_starts, false, 1, now);
+                            two_words_no_offsets, word_starts, false, 1, false,
+                            now);
   EXPECT_GT(scored.raw_score, 0);
 
   // Now allow credit for the match in the TLD.
   base::AutoReset<bool> reset(&ScoredHistoryMatch::allow_tld_matches_, true);
   ScoredHistoryMatch scored_with_tld(
       row, visits, u"fed com", Make2Terms("fed", "com"), two_words_no_offsets,
-      word_starts, false, 1, now);
+      word_starts, false, 1, false, now);
   EXPECT_GT(scored_with_tld.raw_score, 0);
 
   EXPECT_GT(scored_with_tld.raw_score, scored.raw_score);
@@ -270,14 +278,15 @@
   WordStarts two_words_no_offsets(2, 0u);
   VisitInfoVector visits = CreateVisitInfoVector(8, 3, now);
   ScoredHistoryMatch scored(row, visits, u"fed http", Make2Terms("fed", "http"),
-                            two_words_no_offsets, word_starts, false, 1, now);
+                            two_words_no_offsets, word_starts, false, 1, false,
+                            now);
   EXPECT_GT(scored.raw_score, 0);
 
   // Now allow credit for the match in the scheme.
   base::AutoReset<bool> reset(&ScoredHistoryMatch::allow_scheme_matches_, true);
   ScoredHistoryMatch scored_with_scheme(
       row, visits, u"fed http", Make2Terms("fed", "http"), two_words_no_offsets,
-      word_starts, false, 1, now);
+      word_starts, false, 1, false, now);
   EXPECT_GT(scored_with_scheme.raw_score, 0);
 
   EXPECT_GT(scored_with_scheme.raw_score, scored.raw_score);
@@ -296,19 +305,23 @@
         MakeURLRow("http://www.google.com", "abcdef", 3, 30, 1));
     PopulateWordStarts(row, &word_starts);
     ScoredHistoryMatch scored_a(row, visits, u"g", Make1Term("g"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_a.match_in_scheme);
     EXPECT_FALSE(scored_a.match_in_subdomain);
     ScoredHistoryMatch scored_b(row, visits, u"w", Make1Term("w"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_b.match_in_scheme);
     EXPECT_TRUE(scored_b.match_in_subdomain);
     ScoredHistoryMatch scored_c(row, visits, u"h", Make1Term("h"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_TRUE(scored_c.match_in_scheme);
     EXPECT_FALSE(scored_c.match_in_subdomain);
     ScoredHistoryMatch scored_d(row, visits, u"o", Make1Term("o"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_d.match_in_scheme);
     EXPECT_FALSE(scored_d.match_in_subdomain);
   }
@@ -317,15 +330,18 @@
     history::URLRow row(MakeURLRow("http://teams.foo.com", "abcdef", 3, 30, 1));
     PopulateWordStarts(row, &word_starts);
     ScoredHistoryMatch scored_a(row, visits, u"t", Make1Term("t"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_a.match_in_scheme);
     EXPECT_TRUE(scored_a.match_in_subdomain);
     ScoredHistoryMatch scored_b(row, visits, u"f", Make1Term("f"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_b.match_in_scheme);
     EXPECT_FALSE(scored_b.match_in_subdomain);
     ScoredHistoryMatch scored_c(row, visits, u"o", Make1Term("o"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_c.match_in_scheme);
     EXPECT_FALSE(scored_c.match_in_subdomain);
   }
@@ -334,15 +350,18 @@
     history::URLRow row(MakeURLRow("http://en.m.foo.com", "abcdef", 3, 30, 1));
     PopulateWordStarts(row, &word_starts);
     ScoredHistoryMatch scored_a(row, visits, u"e", Make1Term("e"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_a.match_in_scheme);
     EXPECT_TRUE(scored_a.match_in_subdomain);
     ScoredHistoryMatch scored_b(row, visits, u"m", Make1Term("m"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_b.match_in_scheme);
     EXPECT_TRUE(scored_b.match_in_subdomain);
     ScoredHistoryMatch scored_c(row, visits, u"f", Make1Term("f"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_c.match_in_scheme);
     EXPECT_FALSE(scored_c.match_in_subdomain);
   }
@@ -352,42 +371,49 @@
         MakeURLRow("https://www.testing.com/xxx?yyy#zzz", "abcdef", 3, 30, 1));
     PopulateWordStarts(row, &word_starts);
     ScoredHistoryMatch scored_a(row, visits, u"t", Make1Term("t"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_a.match_in_scheme);
     EXPECT_FALSE(scored_a.match_in_subdomain);
     ScoredHistoryMatch scored_b(row, visits, u"h", Make1Term("h"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_TRUE(scored_b.match_in_scheme);
     EXPECT_FALSE(scored_b.match_in_subdomain);
     ScoredHistoryMatch scored_c(row, visits, u"w", Make1Term("w"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_c.match_in_scheme);
     EXPECT_TRUE(scored_c.match_in_subdomain);
     ScoredHistoryMatch scored_d(row, visits, u"x", Make1Term("x"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_d.match_in_scheme);
     EXPECT_FALSE(scored_d.match_in_subdomain);
     ScoredHistoryMatch scored_e(row, visits, u"y", Make1Term("y"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_e.match_in_scheme);
     EXPECT_FALSE(scored_e.match_in_subdomain);
     ScoredHistoryMatch scored_f(row, visits, u"z", Make1Term("z"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_f.match_in_scheme);
     EXPECT_FALSE(scored_f.match_in_subdomain);
     ScoredHistoryMatch scored_g(row, visits, u"https://www",
                                 Make1Term("https://www"), one_word_no_offset,
-                                word_starts, false, 1, now);
+                                word_starts, false, 1, false, now);
     EXPECT_TRUE(scored_g.match_in_scheme);
     EXPECT_TRUE(scored_g.match_in_subdomain);
     ScoredHistoryMatch scored_h(row, visits, u"testing.com/x",
                                 Make1Term("testing.com/x"), one_word_no_offset,
-                                word_starts, false, 1, now);
+                                word_starts, false, 1, false, now);
     EXPECT_FALSE(scored_h.match_in_scheme);
     EXPECT_FALSE(scored_h.match_in_subdomain);
     ScoredHistoryMatch scored_i(row, visits, u"https://www.testing.com/x",
                                 Make1Term("https://www.testing.com/x"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_TRUE(scored_i.match_in_scheme);
     EXPECT_TRUE(scored_i.match_in_subdomain);
   }
@@ -397,15 +423,18 @@
         MakeURLRow("http://www.xn--1lq90ic7f1rc.cn/xnblah", "abcd", 3, 30, 1));
     PopulateWordStarts(row, &word_starts);
     ScoredHistoryMatch scored_a(row, visits, u"x", Make1Term("x"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_a.match_in_scheme);
     EXPECT_FALSE(scored_a.match_in_subdomain);
     ScoredHistoryMatch scored_b(row, visits, u"xn", Make1Term("xn"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_b.match_in_scheme);
     EXPECT_FALSE(scored_b.match_in_subdomain);
     ScoredHistoryMatch scored_c(row, visits, u"w", Make1Term("w"),
-                                one_word_no_offset, word_starts, false, 1, now);
+                                one_word_no_offset, word_starts, false, 1,
+                                false, now);
     EXPECT_FALSE(scored_c.match_in_scheme);
     EXPECT_TRUE(scored_c.match_in_subdomain);
   }
@@ -565,7 +594,7 @@
   base::Time now(base::Time::Max());
   VisitInfoVector visits;
   ScoredHistoryMatch match(row, visits, u"foo", Make1Term("foo"), WordStarts{0},
-                           row_word_starts, false, 1, now);
+                           row_word_starts, false, 1, false, now);
 
   // Record the score for one untyped visit.
   visits = {{now, ui::PAGE_TRANSITION_LINK}};
@@ -624,7 +653,7 @@
   base::Time now(base::Time::Max());
   VisitInfoVector visits;
   ScoredHistoryMatch match(row, visits, u"foo", Make1Term("foo"), WordStarts{0},
-                           row_word_starts, false, 1, now);
+                           row_word_starts, false, 1, false, now);
 
   EXPECT_EQ(3.0, match.GetDocumentSpecificityScore(1));
   EXPECT_EQ(1.0, match.GetDocumentSpecificityScore(5));
@@ -757,31 +786,36 @@
   float topicality_score = 0.0;
   float frequency_score = 10.0;
   float specificity_score = 1.0;
-  // intermediate_score = 0.0 * 10.0 * 1.0 = 0.0.
+  float domain_score = 1.0;
+  // intermediate_score = 0.0 * 10.0 * 1.0 * 1.0 = 0.0.
   EXPECT_EQ(0, ScoredHistoryMatch::GetFinalRelevancyScore(
-                   topicality_score, frequency_score, specificity_score));
+                   topicality_score, frequency_score, specificity_score,
+                   domain_score));
 
   // Check when intermediate score falls at the border range.
   topicality_score = 0.4f;
   frequency_score = 10.0f;
-  // intermediate_score = 0.4 * 10.0 * 1.0 = 4.0.
+  // intermediate_score = 0.4 * 10.0 * 1.0 * 1.0 = 4.0.
   EXPECT_EQ(500, ScoredHistoryMatch::GetFinalRelevancyScore(
-                     topicality_score, frequency_score, specificity_score));
+                     topicality_score, frequency_score, specificity_score,
+                     domain_score));
 
   // Checking the score that falls into one of the buckets.
   topicality_score = 0.5f;
   frequency_score = 10.0f;
-  // intermediate_score = 0.5 * 10.0 * 1.0 = 5.0.
-  EXPECT_EQ(600,  // 500 + (((900 - 500)/(8 -4)) * 1) = 600.
-            ScoredHistoryMatch::GetFinalRelevancyScore(
-                topicality_score, frequency_score, specificity_score));
+  // intermediate_score = 0.5 * 10.0 * 1.0 * 1.0 = 5.0.
+  EXPECT_EQ(
+      600,  // 500 + (((900 - 500)/(8 -4)) * 1) = 600.
+      ScoredHistoryMatch::GetFinalRelevancyScore(
+          topicality_score, frequency_score, specificity_score, domain_score));
 
   // Never give the score greater than maximum specified.
   topicality_score = 0.5f;
   frequency_score = 22.0f;
-  // intermediate_score = 0.5 * 22.0 * 1.0 = 11.0
+  // intermediate_score = 0.5 * 22.0 * 1.0 * 1.0 = 11.0
   EXPECT_EQ(1000, ScoredHistoryMatch::GetFinalRelevancyScore(
-                      topicality_score, frequency_score, specificity_score));
+                      topicality_score, frequency_score, specificity_score,
+                      domain_score));
 }
 
 // Test the function GetHQPBucketsFromString().
diff --git a/components/omnibox/browser/url_index_private_data.cc b/components/omnibox/browser/url_index_private_data.cc
index 429a9fe..5fcd611e 100644
--- a/components/omnibox/browser/url_index_private_data.cc
+++ b/components/omnibox/browser/url_index_private_data.cc
@@ -685,11 +685,16 @@
     const history::URLRow& hist_item = hist_pos->second.url_row;
     auto starts_pos = word_starts_map_.find(history_id);
     DCHECK(starts_pos != word_starts_map_.end());
+
+    bool is_highly_visited_host =
+        !host_filter.empty() ||
+        base::ranges::find(HighlyVisitedHosts(), hist_item.url().host()) !=
+            HighlyVisitedHosts().end();
     ScoredHistoryMatch new_scored_match(
         hist_item, hist_pos->second.visits, lower_raw_string, lower_raw_terms,
         lower_terms_to_word_starts_offsets, starts_pos->second,
         bookmark_model && bookmark_model->IsBookmarked(hist_item.url()),
-        num_unique_hosts, now);
+        num_unique_hosts, is_highly_visited_host, now);
     // Filter new matches that ended up scoring 0. (These are usually matches
     // which didn't match the user's raw terms.)
     if (new_scored_match.raw_score > 0)
diff --git a/components/password_manager/core/browser/site_affiliation/affiliation_service_impl.cc b/components/password_manager/core/browser/site_affiliation/affiliation_service_impl.cc
index a085a1f..e13a2931 100644
--- a/components/password_manager/core/browser/site_affiliation/affiliation_service_impl.cc
+++ b/components/password_manager/core/browser/site_affiliation/affiliation_service_impl.cc
@@ -20,6 +20,7 @@
 #include "components/password_manager/core/browser/android_affiliation/affiliation_backend.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher_interface.h"
 #include "components/password_manager/core/browser/password_store_factory_util.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "url/gurl.h"
 #include "url/scheme_host_port.h"
diff --git a/components/policy/tools/syntax_check_policy_template_json.py b/components/policy/tools/syntax_check_policy_template_json.py
index 648d718..0f3de37 100755
--- a/components/policy/tools/syntax_check_policy_template_json.py
+++ b/components/policy/tools/syntax_check_policy_template_json.py
@@ -2013,7 +2013,10 @@
       Returns warnings and errors found in the policies.
     '''
     self.features = known_features
-    modified_policies = [pc['new_policy'] for pc in policy_change_list]
+    modified_policies = [
+        pc['new_policy'] for pc in policy_change_list
+        if pc['new_policy'] is not None
+    ]
     for policy in modified_policies:
       self._CheckPolicyDefinition(policy, current_version)
     self.non_compatibility_error_count = 0
diff --git a/components/safe_browsing/content/browser/browser_url_loader_throttle.cc b/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
index 50f6fe3c4..ed7d670 100644
--- a/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
+++ b/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
@@ -74,13 +74,20 @@
         can_check_high_confidence_allowlist_(
             can_check_high_confidence_allowlist),
         url_lookup_service_metric_suffix_(url_lookup_service_metric_suffix),
-        url_lookup_service_(url_lookup_service) {
+        url_lookup_service_(url_lookup_service),
+        creation_time_(base::TimeTicks::Now()) {
     content::WebContents* contents = web_contents_getter_.Run();
     if (!!contents) {
       last_committed_url_ = contents->GetLastCommittedURL();
     }
   }
 
+  ~CheckerOnIO() {
+    base::UmaHistogramMediumTimes(
+        "SafeBrowsing.BrowserThrottle.CheckerOnIOLifetime",
+        base::TimeTicks::Now() - creation_time_);
+  }
+
   // Starts the initial safe browsing check. This check and future checks may be
   // skipped after checking with the UrlCheckerDelegate.
   void Start(const net::HttpRequestHeaders& headers,
@@ -194,6 +201,7 @@
   std::string url_lookup_service_metric_suffix_;
   GURL last_committed_url_;
   base::WeakPtr<RealTimeUrlLookupServiceBase> url_lookup_service_;
+  base::TimeTicks creation_time_;
 };
 
 // static
@@ -307,6 +315,11 @@
     network::mojom::URLResponseHead* response_head,
     bool* defer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  will_process_response_count_++;
+  base::UmaHistogramCounts100(
+      "SafeBrowsing.BrowserThrottle.WillProcessResponseCount",
+      will_process_response_count_);
+
   if (blocked_) {
     // OnCheckUrlResult() has set |blocked_| to true and called
     // |delegate_->CancelWithError|, but this method is called before the
diff --git a/components/safe_browsing/content/browser/browser_url_loader_throttle.h b/components/safe_browsing/content/browser/browser_url_loader_throttle.h
index a8a08d7..aa36047 100644
--- a/components/safe_browsing/content/browser/browser_url_loader_throttle.h
+++ b/components/safe_browsing/content/browser/browser_url_loader_throttle.h
@@ -141,6 +141,9 @@
   // Whether real time lookup is enabled for the user.
   bool real_time_lookup_enabled_;
 
+  // Tracks how many times |WillProcessResponse| is called.
+  int will_process_response_count_ = 0;
+
   base::WeakPtrFactory<BrowserURLLoaderThrottle> weak_factory_{this};
 };
 
diff --git a/components/safe_browsing/core/common/safe_browsing_prefs.cc b/components/safe_browsing/core/common/safe_browsing_prefs.cc
index 12d10cc3..2c44c3d9 100644
--- a/components/safe_browsing/core/common/safe_browsing_prefs.cc
+++ b/components/safe_browsing/core/common/safe_browsing_prefs.cc
@@ -152,8 +152,7 @@
 bool IsEnhancedProtectionEnabled(const PrefService& prefs) {
   // SafeBrowsingEnabled is checked too due to devices being out
   // of sync or not on a version that includes SafeBrowsingEnhanced pref.
-  return base::FeatureList::IsEnabled(kEnhancedProtection) &&
-         prefs.GetBoolean(prefs::kSafeBrowsingEnhanced) &&
+  return prefs.GetBoolean(prefs::kSafeBrowsingEnhanced) &&
          IsSafeBrowsingEnabled(prefs);
 }
 
diff --git a/components/segmentation_platform/embedder/model_provider_factory_impl.cc b/components/segmentation_platform/embedder/model_provider_factory_impl.cc
index e40a024a..d9c2b91 100644
--- a/components/segmentation_platform/embedder/model_provider_factory_impl.cc
+++ b/components/segmentation_platform/embedder/model_provider_factory_impl.cc
@@ -19,7 +19,7 @@
   void InitAndFetchModel(
       const ModelUpdatedCallback& model_updated_callback) override {}
 
-  void ExecuteModelWithInput(const std::vector<float>& inputs,
+  void ExecuteModelWithInput(const ModelProvider::Request& inputs,
                              ExecutionCallback callback) override {
     std::move(callback).Run(absl::nullopt);
   }
diff --git a/components/segmentation_platform/internal/data_collection/training_data_cache.cc b/components/segmentation_platform/internal/data_collection/training_data_cache.cc
index ed52b37..00918ca 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_cache.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_cache.cc
@@ -17,7 +17,7 @@
 
 void TrainingDataCache::StoreInputs(SegmentId segment_id,
                                     RequestId request_id,
-                                    const std::vector<float>& inputs) {
+                                    const ModelProvider::Request& inputs) {
   TrainingData training_data;
   for (const auto& input : inputs) {
     training_data.add_inputs(input);
diff --git a/components/segmentation_platform/internal/data_collection/training_data_cache.h b/components/segmentation_platform/internal/data_collection/training_data_cache.h
index d271c1c1..2617e48a 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_cache.h
+++ b/components/segmentation_platform/internal/data_collection/training_data_cache.h
@@ -8,6 +8,7 @@
 #include "base/containers/flat_map.h"
 #include "base/types/id_type.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -27,7 +28,7 @@
   // Stores the inputs for a segment given a request ID.
   void StoreInputs(proto::SegmentId segment_id,
                    RequestId request_id,
-                   const std::vector<float>& inputs);
+                   const ModelProvider::Request& inputs);
 
   // Retrieves and deletes the inputs for a segment given a request ID from the
   // cache. Returns nullopt when the associated request ID is not found.
diff --git a/components/segmentation_platform/internal/data_collection/training_data_cache_unittest.cc b/components/segmentation_platform/internal/data_collection/training_data_cache_unittest.cc
index 46fc8412..844572f0 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_cache_unittest.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_cache_unittest.cc
@@ -44,7 +44,7 @@
 }
 
 TEST_F(TrainingDataCacheTest, GetTrainingDataFromCache) {
-  std::vector<float> data = {1, 2, 3};
+  ModelProvider::Request data = {1, 2, 3};
   training_data_cache_->StoreInputs(kSegmentId, kRequestId, data);
   auto training_data =
       training_data_cache_->GetInputsAndDelete(kSegmentId, kRequestId);
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
index ab02830..84afe11 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
@@ -316,8 +316,8 @@
     const absl::optional<ImmediaCollectionParam>& param,
     const proto::SegmentInfo& segment_info,
     bool has_error,
-    const std::vector<float>& input_tensors,
-    const std::vector<float>& output_tensors) {
+    const ModelProvider::Request& input_tensors,
+    const ModelProvider::Response& output_tensors) {
   if (has_error) {
     RecordTrainingDataCollectionEvent(
         segment_info.segment_id(),
@@ -342,7 +342,7 @@
 
   auto ukm_source_id = SegmentationUkmHelper::GetInstance()->RecordTrainingData(
       segment_info.segment_id(), segment_info.model_version(), input_tensors,
-      param.has_value() ? std::vector<float>{param->output_value}
+      param.has_value() ? ModelProvider::Response{param->output_value}
                         : output_tensors,
       output_indexes, segment_info.prediction_result(),
       result_prefs_->ReadSegmentationResultFromPref(segmentation_key));
@@ -430,8 +430,8 @@
     TrainingDataCache::RequestId request_id,
     const proto::SegmentInfo& segment_info,
     bool has_error,
-    const std::vector<float>& input_tensors,
-    const std::vector<float>& output_tensors) {
+    const ModelProvider::Request& input_tensors,
+    const ModelProvider::Response& output_tensors) {
   // Store inputs to cache.
   training_cache_->StoreInputs(segment_info.segment_id(), request_id,
                                input_tensors);
@@ -478,17 +478,17 @@
       base::BindOnce(
           &TrainingDataCollectorImpl::onGetOutputsOnObservationTrigger,
           weak_ptr_factory_.GetWeakPtr(), request_id, segment_info,
-          std::vector<float>(input.value().inputs().begin(),
-                             input.value().inputs().end())));
+          ModelProvider::Response(input.value().inputs().begin(),
+                                  input.value().inputs().end())));
 }
 
 void TrainingDataCollectorImpl::onGetOutputsOnObservationTrigger(
     TrainingDataCache::RequestId request_id,
     const proto::SegmentInfo& segment_info,
-    const std::vector<float>& cached_input_tensors,
+    const ModelProvider::Request& cached_input_tensors,
     bool has_error,
-    const std::vector<float>& input_tensors,
-    const std::vector<float>& output_tensors) {
+    const ModelProvider::Request& input_tensors,
+    const ModelProvider::Response& output_tensors) {
   // Upload input and output tensors.
   // TODO(haileywang): Add state in cache for each request; never seen,
   // fulfilled, unfullfilled. (Or make triggers cancellable callbacks)
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
index 642d23e..2a0321d6 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
@@ -18,6 +18,7 @@
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -81,22 +82,22 @@
       TrainingDataCache::RequestId request_id,
       const proto::SegmentInfo& segment_info,
       bool has_error,
-      const std::vector<float>& input_tensors,
-      const std::vector<float>& output_tensors);
+      const ModelProvider::Request& input_tensors,
+      const ModelProvider::Response& output_tensors);
 
   void onGetOutputsOnObservationTrigger(
       TrainingDataCache::RequestId request_id,
       const proto::SegmentInfo& segment_info,
-      const std::vector<float>& cached_input_tensors,
+      const ModelProvider::Request& cached_input_tensors,
       bool has_error,
-      const std::vector<float>& input_tensors,
-      const std::vector<float>& output_tensors);
+      const ModelProvider::Request& input_tensors,
+      const ModelProvider::Response& output_tensors);
 
   void OnGetTrainingTensors(const absl::optional<ImmediaCollectionParam>& param,
                             const proto::SegmentInfo& segment_info,
                             bool has_error,
-                            const std::vector<float>& input_tensors,
-                            const std::vector<float>& output_tensors);
+                            const ModelProvider::Request& input_tensors,
+                            const ModelProvider::Response& output_tensors);
 
   // Returns whether training data can be reported through UKM. If
   // |include_output| is false, only input data will be checked to see if they
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
index b3e13fd..38b5879 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
@@ -77,9 +77,10 @@
         features::kSegmentationStructuredMetricsFeature, params);
 
     // Setup behavior for |feature_list_processor_|.
-    std::vector<float> inputs({1.f});
+    ModelProvider::Request inputs({1.f});
     ON_CALL(feature_list_processor_, ProcessFeatureList(_, _, _, _, _, _))
-        .WillByDefault(RunOnceCallback<5>(false, inputs, std::vector<float>()));
+        .WillByDefault(
+            RunOnceCallback<5>(false, inputs, ModelProvider::Response()));
     ON_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
         .WillByDefault(Return(true));
 
@@ -336,8 +337,8 @@
 // Tests that continuous collection happens on startup.
 TEST_F(TrainingDataCollectorImplTest, ContinousCollectionOnStartup) {
   ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
-      .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
-                                        std::vector<float>{2.f, 3.f}));
+      .WillByDefault(RunOnceCallback<5>(false, ModelProvider::Request{1.f},
+                                        ModelProvider::Response{2.f, 3.f}));
   CreateSegmentInfo();
   clock()->Advance(base::Hours(24));
   Init();
@@ -357,8 +358,8 @@
 // no data is reported on start up.
 TEST_F(TrainingDataCollectorImplTest, ReportCollectedContinuousTrainingData) {
   ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
-      .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
-                                        std::vector<float>{2.f, 3.f}));
+      .WillByDefault(RunOnceCallback<5>(false, ModelProvider::Request{1.f},
+                                        ModelProvider::Response{2.f, 3.f}));
   CreateSegmentInfo();
   Init();
   clock()->Advance(base::Hours(24));
@@ -385,8 +386,8 @@
 TEST_F(TrainingDataCollectorImplTest,
        NoImmediateDataCollectionAfterLastCollection) {
   ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
-      .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
-                                        std::vector<float>{2.f, 3.f}));
+      .WillByDefault(RunOnceCallback<5>(false, ModelProvider::Request{1.f},
+                                        ModelProvider::Response{2.f, 3.f}));
   CreateSegmentInfo();
   Init();
   clock()->Advance(base::Hours(24));
@@ -409,8 +410,8 @@
 // collection won't happen.
 TEST_F(TrainingDataCollectorImplTest, NoDataCollectionIfUkmAllowedPrefNotSet) {
   ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
-      .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
-                                        std::vector<float>{2.f, 3.f}));
+      .WillByDefault(RunOnceCallback<5>(false, ModelProvider::Request{1.f},
+                                        ModelProvider::Response{2.f, 3.f}));
   LocalStateHelper::GetInstance().SetPrefTime(
       kSegmentationUkmMostRecentAllowedTimeKey, base::Time());
   CreateSegmentInfo();
@@ -424,8 +425,8 @@
 // trigger histogram is observed.
 TEST_F(TrainingDataCollectorImplTest, DataCollectionWithUMATrigger) {
   ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
-      .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
-                                        std::vector<float>{2.f, 3.f}));
+      .WillByDefault(RunOnceCallback<5>(false, ModelProvider::Request{1.f},
+                                        ModelProvider::Response{2.f, 3.f}));
 
   // Create a segment that contain a time delay trigger and a uma trigger.
   CreateSegmentInfoWithTriggers(10);
@@ -461,8 +462,8 @@
 // the time delay passes.
 TEST_F(TrainingDataCollectorImplTest, DataCollectionWithTimeTrigger) {
   ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
-      .WillByDefault(RunOnceCallback<5>(false, std::vector<float>{1.f},
-                                        std::vector<float>{2.f, 3.f}));
+      .WillByDefault(RunOnceCallback<5>(false, ModelProvider::Request{1.f},
+                                        ModelProvider::Response{2.f, 3.f}));
 
   // Create a segment that contain a time delay trigger and a uma trigger.
   CreateSegmentInfoWithTriggers(10);
diff --git a/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc b/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
index 750c3b09..609c2ee7 100644
--- a/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
@@ -161,8 +161,9 @@
               ProcessFeatureList(
                   _, _, segment_id, clock_.Now(),
                   FeatureListQueryProcessor::ProcessOption::kInputsOnly, _))
-      .WillOnce(RunOnceCallback<5>(/*error=*/true, std::vector<float>{1, 2, 3},
-                                   std::vector<float>()));
+      .WillOnce(RunOnceCallback<5>(/*error=*/true,
+                                   ModelProvider::Request{1, 2, 3},
+                                   ModelProvider::Response()));
 
   // The input tensor should contain all values flattened to a single vector.
   EXPECT_CALL(mock_model_, ModelAvailable()).WillRepeatedly(Return(true));
@@ -176,8 +177,8 @@
               ProcessFeatureList(
                   _, _, segment_id, clock_.Now(),
                   FeatureListQueryProcessor::ProcessOption::kInputsOnly, _))
-      .WillOnce(RunOnceCallback<5>(/*error=*/true, std::vector<float>(),
-                                   std::vector<float>()));
+      .WillOnce(RunOnceCallback<5>(/*error=*/true, ModelProvider::Request(),
+                                   ModelProvider::Response()));
   ExecuteModel(*metadata_writer.FindOrCreateSegment(segment_id), &mock_model_,
                std::make_unique<ModelExecutionResult>(
                    ModelExecutionStatus::kSkippedInvalidMetadata));
@@ -192,14 +193,14 @@
   std::string user_action_name = "some_user_action";
   metadata_writer.AddUserActionFeature(kSegmentId, user_action_name, 3, 3,
                                        proto::Aggregation::BUCKETED_COUNT);
-  const std::vector<float> inputs{1, 2, 3, 4, 5, 6, 7};
+  const ModelProvider::Request inputs{1, 2, 3, 4, 5, 6, 7};
 
   EXPECT_CALL(*feature_list_query_processor_,
               ProcessFeatureList(
                   _, _, kSegmentId, clock_.Now(),
                   FeatureListQueryProcessor::ProcessOption::kInputsOnly, _))
-      .WillOnce(
-          RunOnceCallback<5>(/*error=*/false, inputs, std::vector<float>()));
+      .WillOnce(RunOnceCallback<5>(/*error=*/false, inputs,
+                                   ModelProvider::Response()));
 
   // The input tensor should contain all values flattened to a single vector.
   EXPECT_CALL(mock_model_, ModelAvailable()).WillRepeatedly(Return(true));
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc
index 07266f07..bad35cb 100644
--- a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc
@@ -68,7 +68,7 @@
 }
 
 void OptimizationGuideSegmentationModelProvider::ExecuteModelWithInput(
-    const std::vector<float>& inputs,
+    const ModelProvider::Request& inputs,
     ExecutionCallback callback) {
   if (!model_handler_) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h
index d45ae1fa..69667e98 100644
--- a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h
@@ -40,7 +40,7 @@
   // ModelProvider impl:
   void InitAndFetchModel(
       const ModelUpdatedCallback& model_updated_callback) override;
-  void ExecuteModelWithInput(const std::vector<float>& inputs,
+  void ExecuteModelWithInput(const ModelProvider::Request& inputs,
                              ExecutionCallback callback) override;
   bool ModelAvailable() override;
 
diff --git a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h
index 1a64a4e..e63fec5 100644
--- a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h
+++ b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h
@@ -15,6 +15,7 @@
 #include "components/segmentation_platform/internal/execution/processing/query_processor.h"
 #include "components/segmentation_platform/internal/execution/processing/uma_feature_processor.h"
 #include "components/segmentation_platform/public/input_context.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "components/segmentation_platform/public/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
@@ -65,8 +66,8 @@
 
   using FeatureProcessorCallback =
       base::OnceCallback<void(bool,
-                              const std::vector<float>& /*inputs*/,
-                              const std::vector<float>& /*outputs*/)>;
+                              const ModelProvider::Request& /*inputs*/,
+                              const ModelProvider::Response& /*outputs*/)>;
 
   // Options for determining what should be included in the result.
   enum class ProcessOption { kInputsOnly, kOutputsOnly, kInputsAndOutputs };
diff --git a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc
index 22a0074..480e44c 100644
--- a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc
+++ b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc
@@ -169,8 +169,8 @@
 
   void ExpectProcessedFeatureList(
       bool expected_error,
-      const std::vector<float>& expected_input_tensor,
-      const std::vector<float>& expected_output_tensor,
+      const ModelProvider::Request& expected_input_tensor,
+      const ModelProvider::Response& expected_output_tensor,
       base::Time prediction_time,
       FeatureListQueryProcessor::ProcessOption process_option =
           FeatureListQueryProcessor::ProcessOption::kInputsOnly) {
@@ -187,19 +187,19 @@
 
   void ExpectProcessedFeatureList(
       bool expected_error,
-      const std::vector<float>& expected_input_tensor) {
+      const ModelProvider::Request& expected_input_tensor) {
     ExpectProcessedFeatureList(expected_error, expected_input_tensor,
-                               std::vector<float>(), clock_.Now());
+                               ModelProvider::Response(), clock_.Now());
   }
 
   void OnProcessingFinishedCallback(
       base::RepeatingClosure closure,
       bool expected_error,
-      const std::vector<float>& expected_input_tensor,
-      const std::vector<float>& expected_output_tensor,
+      const ModelProvider::Request& expected_input_tensor,
+      const ModelProvider::Response& expected_output_tensor,
       bool error,
-      const std::vector<float>& input_tensor,
-      const std::vector<float>& output_tensor) {
+      const ModelProvider::Request& input_tensor,
+      const ModelProvider::Response& output_tensor) {
     EXPECT_EQ(expected_error, error);
     EXPECT_EQ(expected_input_tensor, input_tensor);
     EXPECT_EQ(expected_output_tensor, output_tensor);
@@ -230,7 +230,7 @@
                 1, proto::Aggregation::COUNT, {});
 
   // The next step should be to run the feature processor.
-  ExpectProcessedFeatureList(true, std::vector<float>{});
+  ExpectProcessedFeatureList(true, ModelProvider::Request{});
 }
 
 TEST_F(FeatureListQueryProcessorTest, PredictionTimeCustomInput) {
@@ -244,7 +244,7 @@
 
   // The next step should be to run the feature processor, the input tensor
   // should not allow non float type value such as TIME values.
-  ExpectProcessedFeatureList(true, std::vector<float>{});
+  ExpectProcessedFeatureList(true, ModelProvider::Request{});
 }
 
 TEST_F(FeatureListQueryProcessorTest, DefaultValueCustomInput) {
@@ -258,7 +258,7 @@
 
   // The next step should be to run the feature processor, the input tensor
   // should contain the default values 1 and 2.
-  ExpectProcessedFeatureList(false, std::vector<float>{1, 2});
+  ExpectProcessedFeatureList(false, ModelProvider::Request{1, 2});
 }
 
 TEST_F(FeatureListQueryProcessorTest, SingleUserAction) {
@@ -293,7 +293,7 @@
       .WillOnce(Return(std::vector<float>{3}));
 
   // The next step should be to run the feature processor.
-  ExpectProcessedFeatureList(false, std::vector<float>{3});
+  ExpectProcessedFeatureList(false, ModelProvider::Request{3});
 }
 
 TEST_F(FeatureListQueryProcessorTest, LatestOrDefaultUmaFeature) {
@@ -348,7 +348,7 @@
       .WillOnce(Return(absl::nullopt));
 
   // The next step should be to run the feature processor.
-  ExpectProcessedFeatureList(false, std::vector<float>{3, 6});
+  ExpectProcessedFeatureList(false, ModelProvider::Request{3, 6});
 }
 
 TEST_F(FeatureListQueryProcessorTest, UmaFeaturesAndCustomInputs) {
@@ -366,7 +366,7 @@
 
   // The next step should be to run the feature processor, the input tensor
   // should contain {3, 1, 2}.
-  ExpectProcessedFeatureList(false, std::vector<float>{3, 1, 2});
+  ExpectProcessedFeatureList(false, ModelProvider::Request{3, 1, 2});
 }
 
 TEST_F(FeatureListQueryProcessorTest, UmaFeaturesAndCustomInputsInvalid) {
@@ -383,7 +383,7 @@
   AddCustomInput(1, proto::CustomInput::UNKNOWN_FILL_POLICY, {});
 
   // The next step should be to run the feature processor.
-  ExpectProcessedFeatureList(true, std::vector<float>{});
+  ExpectProcessedFeatureList(true, ModelProvider::Request{});
 }
 
 TEST_F(FeatureListQueryProcessorTest, MultipleUmaFeaturesWithOutputs) {
@@ -468,7 +468,7 @@
       .WillRepeatedly(Return(std::vector<float>{4}));
 
   // The input tensor should contain all three values: 3, 6, and 4.
-  ExpectProcessedFeatureList(false, std::vector<float>{3, 6, 4});
+  ExpectProcessedFeatureList(false, ModelProvider::Request{3, 6, 4});
 
   // Output is also enum histogram
   std::vector<SignalDatabaseSample> output_histogram_enum_samples{
@@ -490,12 +490,13 @@
   // The input tensor should contain all three values: {3, 6, 4}, output
   // contains {5}
   ExpectProcessedFeatureList(
-      false, std::vector<float>{3, 6, 4}, std::vector<float>{5}, clock_.Now(),
+      false, ModelProvider::Request{3, 6, 4}, ModelProvider::Response{5},
+      clock_.Now(),
       FeatureListQueryProcessor::ProcessOption::kInputsAndOutputs);
 
   // Only return tensors for output features.
   ExpectProcessedFeatureList(
-      false, std::vector<float>(), std::vector<float>{5}, clock_.Now(),
+      false, ModelProvider::Request(), ModelProvider::Response{5}, clock_.Now(),
       FeatureListQueryProcessor::ProcessOption::kOutputsOnly);
 }
 
@@ -559,7 +560,7 @@
       .WillOnce(Return(std::vector<float>{6}));
 
   // The input tensor should contain only the first and last uma feature.
-  ExpectProcessedFeatureList(false, std::vector<float>{3, 6});
+  ExpectProcessedFeatureList(false, ModelProvider::Request{3, 6});
 }
 
 TEST_F(FeatureListQueryProcessorTest, SkipNoColumnWeightCustomInput) {
@@ -580,7 +581,7 @@
 
   // The next step should be to run the feature processor, the input tensor
   // should contain the first and last custom input of 1 and 4.
-  ExpectProcessedFeatureList(false, std::vector<float>{1, 4});
+  ExpectProcessedFeatureList(false, ModelProvider::Request{1, 4});
 }
 
 TEST_F(FeatureListQueryProcessorTest, FilteredEnumSamples) {
@@ -623,7 +624,7 @@
       .WillOnce(Return(std::vector<float>{2}));
 
   // The input tensor should contain a single value.
-  ExpectProcessedFeatureList(false, std::vector<float>{2});
+  ExpectProcessedFeatureList(false, ModelProvider::Request{2});
 }
 
 TEST_F(FeatureListQueryProcessorTest, MultipleUmaFeaturesWithMultipleBuckets) {
@@ -693,7 +694,8 @@
       .WillOnce(Return(std::vector<float>{4, 5, 6, 7}));
 
   // The input tensor should contain all values flattened to a single vector.
-  ExpectProcessedFeatureList(false, std::vector<float>{1, 2, 3, 4, 5, 6, 7});
+  ExpectProcessedFeatureList(false,
+                             ModelProvider::Request{1, 2, 3, 4, 5, 6, 7});
 }
 
 }  // namespace segmentation_platform::processing
diff --git a/components/segmentation_platform/internal/segmentation_ukm_helper.cc b/components/segmentation_platform/internal/segmentation_ukm_helper.cc
index 62cf6a7f..8dd6a94 100644
--- a/components/segmentation_platform/internal/segmentation_ukm_helper.cc
+++ b/components/segmentation_platform/internal/segmentation_ukm_helper.cc
@@ -108,7 +108,7 @@
 ukm::SourceId SegmentationUkmHelper::RecordModelExecutionResult(
     SegmentId segment_id,
     int64_t model_version,
-    const std::vector<float>& input_tensor,
+    const ModelProvider::Request& input_tensor,
     float result) {
   ukm::SourceId source_id = ukm::NoURLSourceId();
   ukm::builders::Segmentation_ModelExecution execution_result(source_id);
@@ -128,8 +128,8 @@
 ukm::SourceId SegmentationUkmHelper::RecordTrainingData(
     SegmentId segment_id,
     int64_t model_version,
-    const std::vector<float>& input_tensor,
-    const std::vector<float>& outputs,
+    const ModelProvider::Request& input_tensor,
+    const ModelProvider::Response& outputs,
     const std::vector<int>& output_indexes,
     absl::optional<proto::PredictionResult> prediction_result,
     absl::optional<SelectedSegment> selected_segment) {
@@ -163,7 +163,7 @@
     ukm::builders::Segmentation_ModelExecution* ukm_builder,
     SegmentId segment_id,
     int64_t model_version,
-    const std::vector<float>& input_tensor) {
+    const ModelProvider::Request& input_tensor) {
   if (input_tensor.size() > ARRAY_SIZE(kSegmentationUkmInputMethods)) {
     // Don't record UKM if there are too many tensors.
     stats::RecordTooManyInputTensors(input_tensor.size());
@@ -180,7 +180,7 @@
 
 bool SegmentationUkmHelper::AddOutputsToUkm(
     ukm::builders::Segmentation_ModelExecution* ukm_builder,
-    const std::vector<float>& outputs,
+    const ModelProvider::Response& outputs,
     const std::vector<int>& output_indexes) {
   DCHECK(!outputs.empty());
   if (outputs.size() != output_indexes.size())
diff --git a/components/segmentation_platform/internal/segmentation_ukm_helper.h b/components/segmentation_platform/internal/segmentation_ukm_helper.h
index 61d1d050..ccbe4a0 100644
--- a/components/segmentation_platform/internal/segmentation_ukm_helper.h
+++ b/components/segmentation_platform/internal/segmentation_ukm_helper.h
@@ -9,6 +9,7 @@
 #include "base/no_destructor.h"
 #include "base/time/time.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -38,7 +39,7 @@
   ukm::SourceId RecordModelExecutionResult(
       SegmentId segment_id,
       int64_t model_version,
-      const std::vector<float>& input_tensor,
+      const ModelProvider::Request& input_tensor,
       float result);
 
   // Record segmentation model training data as UKM message.
@@ -53,8 +54,8 @@
   ukm::SourceId RecordTrainingData(
       SegmentId segment_id,
       int64_t model_version,
-      const std::vector<float>& input_tensors,
-      const std::vector<float>& outputs,
+      const ModelProvider::Request& input_tensors,
+      const ModelProvider::Response& outputs,
       const std::vector<int>& output_indexes,
       absl::optional<proto::PredictionResult> prediction_result,
       absl::optional<SelectedSegment> selected_segment);
@@ -79,10 +80,10 @@
   bool AddInputsToUkm(ukm::builders::Segmentation_ModelExecution* ukm_builder,
                       SegmentId segment_id,
                       int64_t model_version,
-                      const std::vector<float>& input_tensor);
+                      const ModelProvider::Request& input_tensor);
 
   bool AddOutputsToUkm(ukm::builders::Segmentation_ModelExecution* ukm_builder,
-                       const std::vector<float>& outputs,
+                       const ModelProvider::Response& outputs,
                        const std::vector<int>& output_indexes);
 
   friend class base::NoDestructor<SegmentationUkmHelper>;
diff --git a/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc b/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
index a65be99..74f3dee 100644
--- a/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
+++ b/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
@@ -115,7 +115,7 @@
 TEST_F(SegmentationUkmHelperTest, TestExecutionResultReporting) {
   // Allow results for OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB to be recorded.
   InitializeAllowedSegmentIds("4");
-  std::vector<float> input_tensors = {0.1, 0.7, 0.8, 0.5};
+  ModelProvider::Request input_tensors = {0.1, 0.7, 0.8, 0.5};
   SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
       proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors, 0.6);
   ExpectUkmMetrics(Segmentation_ModelExecution::kEntryName,
@@ -141,8 +141,8 @@
 TEST_F(SegmentationUkmHelperTest, TestTrainingDataCollectionReporting) {
   // Allow results for OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB to be recorded.
   InitializeAllowedSegmentIds("4");
-  std::vector<float> input_tensors = {0.1};
-  std::vector<float> outputs = {1.0, 0.0};
+  ModelProvider::Request input_tensors = {0.1};
+  ModelProvider::Response outputs = {1.0, 0.0};
   std::vector<int> output_indexes = {2, 3};
 
   SelectedSegment selected_segment(
@@ -238,7 +238,7 @@
   std::string histogram_name(
       "SegmentationPlatform.StructuredMetrics.TooManyTensors.Count");
   InitializeAllowedSegmentIds("4");
-  std::vector<float> input_tensors(100, 0.1);
+  ModelProvider::Request input_tensors(100, 0.1);
   ukm::SourceId source_id =
       SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
           proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
@@ -251,10 +251,10 @@
 // Tests output validation for |RecordTrainingData|.
 TEST_F(SegmentationUkmHelperTest, OutputsValidation) {
   InitializeAllowedSegmentIds("4");
-  std::vector<float> input_tensors{0.1};
+  ModelProvider::Request input_tensors{0.1};
 
   // outputs, output_indexes size doesn't match.
-  std::vector<float> outputs{1.0, 0.0};
+  ModelProvider::Response outputs{1.0, 0.0};
   std::vector<int> output_indexes{0};
 
   ukm::SourceId source_id =
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
index 994f230f..f790814b 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
@@ -238,8 +238,9 @@
   EXPECT_CALL(*mock_execution_manager_, GetProvider(kTestSegment))
       .WillOnce(Return(&provider));
   EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(/*error=*/true, std::vector<float>{{1, 2}},
-                                   std::vector<float>()));
+      .WillOnce(RunOnceCallback<5>(/*error=*/true,
+                                   ModelProvider::Request{{1, 2}},
+                                   ModelProvider::Response()));
   ExpectSegmentResultOnGet(
       kTestSegment, /*ignore_db_scores=*/true,
       SegmentResultProvider::ResultState::kTfliteModelExecutionFailed,
@@ -257,8 +258,9 @@
   EXPECT_CALL(*mock_execution_manager_, GetProvider(kTestSegment))
       .WillOnce(Return(&provider));
   EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(/*error=*/false, std::vector<float>{{1, 2}},
-                                   std::vector<float>()));
+      .WillOnce(RunOnceCallback<5>(/*error=*/false,
+                                   ModelProvider::Request{{1, 2}},
+                                   ModelProvider::Response()));
 
   // Gets the rank from test model instead of database.
   ExpectSegmentResultOnGet(
@@ -295,8 +297,9 @@
 
   // Set error while computing features.
   EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(/*error=*/true, std::vector<float>{{1, 2}},
-                                   std::vector<float>()));
+      .WillOnce(RunOnceCallback<5>(/*error=*/true,
+                                   ModelProvider::Request{{1, 2}},
+                                   ModelProvider::Response()));
   ExpectSegmentResultOnGet(
       kTestSegment,
       /*ignore_db_scores=*/false,
@@ -313,8 +316,9 @@
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(/*error=*/false, std::vector<float>{{1, 2}},
-                                   std::vector<float>()));
+      .WillOnce(RunOnceCallback<5>(/*error=*/false,
+                                   ModelProvider::Request{{1, 2}},
+                                   ModelProvider::Response()));
   ExpectSegmentResultOnGet(
       kTestSegment, /*ignore_db_scores=*/false,
       SegmentResultProvider::ResultState::kDefaultModelScoreUsed, kTestRank);
@@ -330,8 +334,9 @@
       .WillOnce(Return(true))
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(/*error=*/false, std::vector<float>{{1, 2}},
-                                   std::vector<float>()));
+      .WillOnce(RunOnceCallback<5>(/*error=*/false,
+                                   ModelProvider::Request{{1, 2}},
+                                   ModelProvider::Response()));
   ExpectSegmentResultOnGet(
       kTestSegment, /*ignore_db_scores=*/true,
       SegmentResultProvider::ResultState::kDefaultModelScoreUsed, kTestRank);
@@ -353,8 +358,9 @@
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(/*error=*/false, std::vector<float>{{1, 2}},
-                                   std::vector<float>()));
+      .WillOnce(RunOnceCallback<5>(/*error=*/false,
+                                   ModelProvider::Request{{1, 2}},
+                                   ModelProvider::Response()));
   ExpectSegmentResultOnGet(
       kTestSegment, /*ignore_db_scores=*/false,
       SegmentResultProvider::ResultState::kDefaultModelScoreUsed, kTestRank);
diff --git a/components/services/storage/BUILD.gn b/components/services/storage/BUILD.gn
index 51fc6634a..83a522b 100644
--- a/components/services/storage/BUILD.gn
+++ b/components/services/storage/BUILD.gn
@@ -74,6 +74,8 @@
     "shared_storage/async_shared_storage_database_impl.h",
     "shared_storage/shared_storage_database.cc",
     "shared_storage/shared_storage_database.h",
+    "shared_storage/shared_storage_database_migrations.cc",
+    "shared_storage/shared_storage_database_migrations.h",
     "shared_storage/shared_storage_manager.cc",
     "shared_storage/shared_storage_manager.h",
     "shared_storage/shared_storage_options.cc",
@@ -160,16 +162,17 @@
   visibility = [ ":tests" ]
   testonly = true
   sources = [
-    "//components/test/data/storage/shared_storage.v0.init_too_old.sql",
-    "//components/test/data/storage/shared_storage.v1.empty_values_mapping.5origins.sql",
-    "//components/test/data/storage/shared_storage.v1.empty_values_mapping.6origins.sql",
-    "//components/test/data/storage/shared_storage.v1.empty_values_mapping.7origins.sql",
-    "//components/test/data/storage/shared_storage.v1.empty_values_mapping.8origins.sql",
-    "//components/test/data/storage/shared_storage.v1.init_too_new.sql",
-    "//components/test/data/storage/shared_storage.v1.iterator.sql",
+    "//components/test/data/storage/shared_storage.init_too_new.sql",
+    "//components/test/data/storage/shared_storage.v0.sql",
     "//components/test/data/storage/shared_storage.v1.no_budget_table.sql",
-    "//components/test/data/storage/shared_storage.v1.single_origin.sql",
     "//components/test/data/storage/shared_storage.v1.sql",
+    "//components/test/data/storage/shared_storage.v2.empty_values_mapping.5origins.sql",
+    "//components/test/data/storage/shared_storage.v2.empty_values_mapping.6origins.sql",
+    "//components/test/data/storage/shared_storage.v2.empty_values_mapping.7origins.sql",
+    "//components/test/data/storage/shared_storage.v2.empty_values_mapping.8origins.sql",
+    "//components/test/data/storage/shared_storage.v2.iterator.sql",
+    "//components/test/data/storage/shared_storage.v2.single_origin.sql",
+    "//components/test/data/storage/shared_storage.v2.sql",
   ]
   outputs = [ "{{bundle_resources_dir}}/" +
               "{{source_root_relative_dir}}/{{source_file_part}}" ]
@@ -203,6 +206,7 @@
     "service_worker/service_worker_storage_test_utils.h",
     "service_worker/service_worker_storage_unittest.cc",
     "shared_storage/async_shared_storage_database_impl_unittest.cc",
+    "shared_storage/shared_storage_database_migrations_unittest.cc",
     "shared_storage/shared_storage_database_unittest.cc",
     "shared_storage/shared_storage_manager_unittest.cc",
     "storage_service_impl_unittest.cc",
diff --git a/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc b/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc
index ac450ff..70635e7 100644
--- a/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc
+++ b/components/services/storage/shared_storage/async_shared_storage_database_impl_unittest.cc
@@ -106,7 +106,7 @@
   // Return the relative file path in the "storage/" subdirectory of test data
   // for the SQL file from which to initialize an async shared storage database
   // instance.
-  virtual const char* GetRelativeFilePath() { return nullptr; }
+  virtual std::string GetRelativeFilePath() { return nullptr; }
 
   std::unique_ptr<AsyncSharedStorageDatabase> Create() {
     if (GetType() != DBType::kInMemory)
@@ -663,16 +663,19 @@
   base::SimpleTestClock clock_;
 };
 
-class AsyncSharedStorageDatabaseImplFromFileV1Test
+class AsyncSharedStorageDatabaseImplFromFileTest
     : public AsyncSharedStorageDatabaseImplTest {
  public:
   DBType GetType() override { return DBType::kFileBackedFromExisting; }
 
-  const char* GetRelativeFilePath() override { return "shared_storage.v1.sql"; }
+  std::string GetRelativeFilePath() override {
+    return GetTestFileNameForCurrentVersion();
+  }
 };
 
-// Test loading version 1 database.
-TEST_F(AsyncSharedStorageDatabaseImplFromFileV1Test, Version1_LoadFromFile) {
+// Test loading current version database.
+TEST_F(AsyncSharedStorageDatabaseImplFromFileTest,
+       CurrentVersion_LoadFromFile) {
   ASSERT_TRUE(async_database_);
 
   // Override the clock and set to the last time in the file that is used to
@@ -709,9 +712,9 @@
 }
 
 class AsyncSharedStorageDatabaseImplFromFileV1NoBudgetTableTest
-    : public AsyncSharedStorageDatabaseImplFromFileV1Test {
+    : public AsyncSharedStorageDatabaseImplFromFileTest {
  public:
-  const char* GetRelativeFilePath() override {
+  std::string GetRelativeFilePath() override {
     return "shared_storage.v1.no_budget_table.sql";
   }
 };
@@ -749,14 +752,14 @@
 }
 
 struct InitFailureTestCase {
-  const char* relative_file_path;
+  std::string relative_file_path;
   InitStatus expected_status;
 };
 
 std::vector<InitFailureTestCase> GetInitFailureTestCases() {
   return std::vector<InitFailureTestCase>(
-      {{"shared_storage.v1.init_too_new.sql", InitStatus::kTooNew},
-       {"shared_storage.v0.init_too_old.sql", InitStatus::kTooOld}});
+      {{"shared_storage.init_too_new.sql", InitStatus::kTooNew},
+       {GetTestFileNameForLatestDeprecatedVersion(), InitStatus::kTooOld}});
 }
 
 // Used by `testing::PrintToStringParamName()`.
@@ -775,7 +778,7 @@
  public:
   DBType GetType() override { return DBType::kFileBackedFromExisting; }
 
-  const char* GetRelativeFilePath() override {
+  std::string GetRelativeFilePath() override {
     return GetParam().relative_file_path;
   }
 };
@@ -785,8 +788,7 @@
                          testing::ValuesIn(GetInitFailureTestCases()),
                          testing::PrintToStringParamName());
 
-TEST_P(AsyncSharedStorageDatabaseImplFromFileWithFailureTest,
-       Version1_Destroy) {
+TEST_P(AsyncSharedStorageDatabaseImplFromFileWithFailureTest, Destroy) {
   ASSERT_TRUE(async_database_);
 
   // Call an operation so that the database will attempt to be lazy-initialized.
diff --git a/components/services/storage/shared_storage/shared_storage_database.cc b/components/services/storage/shared_storage/shared_storage_database.cc
index 8941e3bc..77030c7c 100644
--- a/components/services/storage/shared_storage/shared_storage_database.cc
+++ b/components/services/storage/shared_storage/shared_storage_database.cc
@@ -18,6 +18,7 @@
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
+#include "components/services/storage/shared_storage/shared_storage_database_migrations.h"
 #include "components/services/storage/shared_storage/shared_storage_options.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "sql/error_delegate_util.h"
@@ -36,10 +37,27 @@
 // entry is at most 2 * 2 * `max_string_length_`.
 const int kSharedStorageEntryTotalBytesMultiplier = 4;
 
-namespace {
-
 // Version number of the database.
-const int kCurrentVersionNumber = 1;
+//
+// Version 1 - https://crrev.com/c/3112567
+//              * initial commit
+//             https://crrev.com/c/3491742
+//              * add `budget_mapping` table
+// Version 2 - https://crrev.com/c/4029459
+//              * add `last_used_time` to `values_mapping`
+//              * rename `last_used_time` in `per_origin_mapping` to
+//                `creation_time`
+const int SharedStorageDatabase::kCurrentVersionNumber = 2;
+
+// Earliest version which can use a `kCurrentVersionNumber` database
+// without failing.
+const int SharedStorageDatabase::kCompatibleVersionNumber = 2;
+
+// Latest version of the database that cannot be upgraded to
+// `kCurrentVersionNumber` without razing the database.
+const int SharedStorageDatabase::kDeprecatedVersionNumber = 0;
+
+namespace {
 
 [[nodiscard]] std::string SerializeOrigin(const url::Origin& origin) {
   DCHECK(!origin.opaque());
@@ -47,27 +65,21 @@
   return origin.Serialize();
 }
 
-[[nodiscard]] bool InitSchema(sql::Database& db) {
+[[nodiscard]] bool InitSchema(sql::Database& db, sql::MetaTable& meta_table) {
   static constexpr char kValuesMappingSql[] =
       "CREATE TABLE IF NOT EXISTS values_mapping("
       "context_origin TEXT NOT NULL,"
       "key TEXT NOT NULL,"
       "value TEXT,"
+      "last_used_time INTEGER NOT NULL,"
       "PRIMARY KEY(context_origin,key)) WITHOUT ROWID";
   if (!db.Execute(kValuesMappingSql))
     return false;
 
-  // Note: In `per_origin_mapping`, `last_used_time` is now the origin's most
-  // recent creation time. We are keeping the outdated name while changing the
-  // de facto usage of the data field in order to avoid an expensive database
-  // migration.
-  //
-  // TODO(cammie): If we ever alter this schema in the future, change the name
-  // of `last_used_time` to `creation_time`.
   static constexpr char kPerOriginMappingSql[] =
       "CREATE TABLE IF NOT EXISTS per_origin_mapping("
       "context_origin TEXT NOT NULL PRIMARY KEY,"
-      "last_used_time INTEGER NOT NULL,"
+      "creation_time INTEGER NOT NULL,"
       "length INTEGER NOT NULL) WITHOUT ROWID";
   if (!db.Execute(kPerOriginMappingSql))
     return false;
@@ -81,18 +93,27 @@
   if (!db.Execute(kBudgetMappingSql))
     return false;
 
-  static constexpr char kLastUsedTimeIndexSql[] =
-      "CREATE INDEX IF NOT EXISTS per_origin_mapping_last_used_time_idx "
-      "ON per_origin_mapping(last_used_time)";
-  if (!db.Execute(kLastUsedTimeIndexSql))
-    return false;
-
   static constexpr char kOriginTimeIndexSql[] =
       "CREATE INDEX IF NOT EXISTS budget_mapping_origin_time_stamp_idx "
       "ON budget_mapping(context_origin,time_stamp)";
   if (!db.Execute(kOriginTimeIndexSql))
     return false;
 
+  if (meta_table.GetVersionNumber() ==
+      SharedStorageDatabase::kCurrentVersionNumber) {
+    static constexpr char kValuesLastUsedTimeIndexSql[] =
+        "CREATE INDEX IF NOT EXISTS values_mapping_last_used_time_idx "
+        "ON values_mapping(last_used_time)";
+    if (!db.Execute(kValuesLastUsedTimeIndexSql))
+      return false;
+
+    static constexpr char kCreationTimeIndexSql[] =
+        "CREATE INDEX IF NOT EXISTS per_origin_mapping_creation_time_idx "
+        "ON per_origin_mapping(creation_time)";
+    if (!db.Execute(kCreationTimeIndexSql))
+      return false;
+  }
+
   return true;
 }
 
@@ -106,8 +127,9 @@
     : result(result) {}
 
 SharedStorageDatabase::GetResult::GetResult(std::u16string data,
+                                            base::Time last_used_time,
                                             OperationResult result)
-    : data(data), result(result) {}
+    : data(data), last_used_time(last_used_time), result(result) {}
 
 SharedStorageDatabase::GetResult::~GetResult() = default;
 
@@ -225,7 +247,7 @@
   // assumption about the state of the disk. In the rare case that multiple
   // entries are found, we return only the value from the first entry found.
   static constexpr char kSelectSql[] =
-      "SELECT value FROM values_mapping "
+      "SELECT value,last_used_time FROM values_mapping "
       "WHERE context_origin=? AND key=? "
       "LIMIT 1";
 
@@ -234,8 +256,10 @@
   statement.BindString(0, origin_str);
   statement.BindString16(1, key);
 
-  if (statement.Step())
-    return GetResult(statement.ColumnString16(0), OperationResult::kSuccess);
+  if (statement.Step()) {
+    return GetResult(statement.ColumnString16(0), statement.ColumnTime(1),
+                     OperationResult::kSuccess);
+  }
 
   if (!statement.Succeeded())
     return GetResult();
@@ -260,21 +284,26 @@
   if (!transaction.Begin())
     return OperationResult::kSqlError;
 
-  std::string origin_str(SerializeOrigin(context_origin));
-  if (HasEntryFor(origin_str, key)) {
-    if (behavior == SharedStorageDatabase::SetBehavior::kIgnoreIfPresent) {
-      // If we are in a nested transaction, we need to commit, even though we
-      // haven't made any changes, so that the failure to set in this case
-      // isn't seen as an error (as then the entire stack of transactions
-      // will be rolled back and the next transaction within the parent
-      // transaction will fail to begin).
-      if (db_.transaction_nesting())
-        transaction.Commit();
-      return OperationResult::kIgnored;
-    }
+  GetResult get_result = Get(context_origin, key);
+  if (get_result.result != OperationResult::kSuccess &&
+      get_result.result != OperationResult::kNotFound) {
+    return OperationResult::kSqlError;
+  }
 
+  std::string origin_str(SerializeOrigin(context_origin));
+  if (get_result.result == OperationResult::kSuccess) {
     if (Delete(context_origin, key) != OperationResult::kSuccess)
       return OperationResult::kSqlError;
+
+    if (behavior == SharedStorageDatabase::SetBehavior::kIgnoreIfPresent) {
+      // We re-insert the old key-value pair with an updated `last_used_time`.
+      if (!InsertIntoValuesMapping(origin_str, key, get_result.data))
+        return OperationResult::kSqlError;
+
+      if (!transaction.Commit())
+        return OperationResult::kSqlError;
+      return OperationResult::kIgnored;
+    }
   } else if (!HasCapacity(origin_str)) {
     return OperationResult::kNoCapacity;
   }
@@ -282,9 +311,6 @@
   if (!InsertIntoValuesMapping(origin_str, key, value))
     return OperationResult::kSqlError;
 
-  if (!UpdateLength(origin_str, /*delta=*/1))
-    return OperationResult::kSqlError;
-
   if (!transaction.Commit())
     return OperationResult::kSqlError;
 
@@ -335,9 +361,6 @@
   if (!InsertIntoValuesMapping(origin_str, key, new_value))
     return OperationResult::kSqlError;
 
-  if (!UpdateLength(origin_str, /*delta=*/1))
-    return OperationResult::kSqlError;
-
   if (!transaction.Commit())
     return OperationResult::kSqlError;
 
@@ -686,8 +709,8 @@
 
   static constexpr char kSelectSql[] =
       "SELECT context_origin FROM per_origin_mapping "
-      "WHERE last_used_time BETWEEN ? AND ? "
-      "ORDER BY last_used_time";
+      "WHERE creation_time BETWEEN ? AND ? "
+      "ORDER BY creation_time";
   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
   statement.BindTime(0, begin);
   statement.BindTime(1, end);
@@ -744,8 +767,8 @@
 
   static constexpr char kSelectSql[] =
       "SELECT context_origin FROM per_origin_mapping "
-      "WHERE last_used_time<? "
-      "ORDER BY last_used_time";
+      "WHERE creation_time<? "
+      "ORDER BY creation_time";
   sql::Statement select_statement(
       db_.GetCachedStatement(SQL_FROM_HERE, kSelectSql));
   select_statement.BindTime(0, clock_->Now() - origin_staleness_threshold_);
@@ -790,10 +813,10 @@
     return {};
 
   const char* kSelectSql = (exclude_empty_origins)
-                               ? "SELECT context_origin,last_used_time,length "
+                               ? "SELECT context_origin,creation_time,length "
                                  "FROM per_origin_mapping "
                                  "WHERE length>0 ORDER BY context_origin"
-                               : "SELECT context_origin,last_used_time,length "
+                               : "SELECT context_origin,creation_time,length "
                                  "FROM per_origin_mapping "
                                  "ORDER BY context_origin";
 
@@ -1144,6 +1167,10 @@
   if (!db_.is_open())
     db_.set_histogram_tag("SharedStorage");
 
+  // 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 here because this SharedStorageDatabase owns
   // the sql::Database instance that stores and uses the callback. So,
   // `this` is guaranteed to outlive the callback.
@@ -1199,24 +1226,33 @@
   }
 
   // Create the tables.
-  if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCurrentVersionNumber) ||
-      !InitSchema(db_)) {
+  if (!meta_table_.Init(&db_, kCurrentVersionNumber,
+                        kCompatibleVersionNumber) ||
+      !InitSchema(db_, meta_table_)) {
     return InitStatus::kError;
   }
 
   if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
     LOG(WARNING) << "Shared storage database is too new.";
+    db_.RazeAndClose();
     return InitStatus::kTooNew;
   }
 
   int cur_version = meta_table_.GetVersionNumber();
 
-  if (cur_version < kCurrentVersionNumber) {
+  if (cur_version <= kDeprecatedVersionNumber) {
     LOG(WARNING) << "Shared storage database is too old to be compatible.";
     db_.RazeAndClose();
     return InitStatus::kTooOld;
   }
 
+  if (cur_version < kCurrentVersionNumber &&
+      !UpgradeSharedStorageDatabaseSchema(db_, meta_table_, clock_)) {
+    LOG(WARNING) << "Shared storage database upgrade failed.";
+    db_.RazeAndClose();
+    return InitStatus::kUpgradeFailed;
+  }
+
   // The initialization is complete.
   if (!transaction.Commit()) {
     LOG(WARNING) << "Shared storage database initialization commit failed.";
@@ -1315,10 +1351,10 @@
 
   // In theory, there ought to be at most one entry found. But we make no
   // assumption about the state of the disk. In the rare case that multiple
-  // entries are found, we retrieve only the `length` and `last_used_time`
+  // entries are found, we retrieve only the `length` and `creation_time`
   // from the first entry found.
   static constexpr char kSelectSql[] =
-      "SELECT length,last_used_time FROM per_origin_mapping "
+      "SELECT length,creation_time FROM per_origin_mapping "
       "WHERE context_origin=? "
       "LIMIT 1";
 
@@ -1369,16 +1405,27 @@
     const std::string& context_origin,
     const std::u16string& key,
     const std::u16string& value) {
+  sql::Transaction transaction(&db_);
+  if (!transaction.Begin())
+    return false;
+
   static constexpr char kInsertSql[] =
-      "INSERT INTO values_mapping(context_origin,key,value)"
-      "VALUES(?,?,?)";
+      "INSERT INTO values_mapping(context_origin,key,value,last_used_time)"
+      "VALUES(?,?,?,?)";
 
   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kInsertSql));
   statement.BindString(0, context_origin);
   statement.BindString16(1, key);
   statement.BindString16(2, value);
+  statement.BindTime(3, clock_->Now());
 
-  return statement.Run();
+  if (!statement.Run())
+    return false;
+
+  if (!UpdateLength(context_origin, /*delta=*/1))
+    return false;
+
+  return transaction.Commit();
 }
 
 bool SharedStorageDatabase::DeleteFromPerOriginMapping(
@@ -1398,7 +1445,7 @@
     base::Time creation_time,
     uint64_t length) {
   static constexpr char kInsertSql[] =
-      "INSERT INTO per_origin_mapping(context_origin,last_used_time,length)"
+      "INSERT INTO per_origin_mapping(context_origin,creation_time,length)"
       "VALUES(?,?,?)";
 
   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kInsertSql));
diff --git a/components/services/storage/shared_storage/shared_storage_database.h b/components/services/storage/shared_storage/shared_storage_database.h
index 53e9458..71c8f1e 100644
--- a/components/services/storage/shared_storage/shared_storage_database.h
+++ b/components/services/storage/shared_storage/shared_storage_database.h
@@ -46,7 +46,7 @@
 class SpecialStoragePolicy;
 
 // Multiplier for determining the padded total size in bytes that an origin
-// is using.
+// is using. Exposed to content/ for testing.
 extern const int kSharedStorageEntryTotalBytesMultiplier;
 
 // Wraps its own `sql::Database` instance on behalf of the Shared Storage
@@ -72,6 +72,8 @@
                    // number being too high.
     kTooOld = 4,  // Status if `LazyInit()` failed due to a version number being
                   // too low.
+    kUpgradeFailed =
+        5,  // Status if migration to current database version failed.
   };
 
   enum class DBFileStatus {
@@ -113,16 +115,20 @@
                         // `Keys()`/`Entries()` exceeds INT_MAX.
   };
 
-  // Bundles a retrieved string from the database along with a field indicating
-  // whether the transaction was free of SQL errors.
+  // Bundles a retrieved string `data` and its last write time `last_used_time`
+  // from the database along with a field `result` indicating whether the
+  // transaction was free of SQL errors.
   struct GetResult {
     std::u16string data;
+    base::Time last_used_time = base::Time::Min();
     OperationResult result = OperationResult::kSqlError;
     GetResult();
     GetResult(const GetResult&) = delete;
     GetResult(GetResult&&);
     explicit GetResult(OperationResult result);
-    GetResult(std::u16string data, OperationResult result);
+    GetResult(std::u16string data,
+              base::Time last_used_time,
+              OperationResult result);
     ~GetResult();
     GetResult& operator=(const GetResult&) = delete;
     GetResult& operator=(GetResult&&);
@@ -184,6 +190,11 @@
     EntriesResult& operator=(EntriesResult&&);
   };
 
+  // Exposed for testing.
+  static const int kCurrentVersionNumber;
+  static const int kCompatibleVersionNumber;
+  static const int kDeprecatedVersionNumber;
+
   // When `db_path` is empty, the database will be opened in memory only.
   SharedStorageDatabase(
       base::FilePath db_path,
@@ -277,7 +288,7 @@
   [[nodiscard]] OperationResult Clear(url::Origin context_origin);
 
   // Returns the number of entries for `context_origin` in the database, or -1
-  // on error. Note that this call will update the origin's `last_used_time`.
+  // on error. Note that this call will update the origin's `creation_time`.
   // TODO(crbug.com/1277662): Consider renaming to something more descriptive.
   [[nodiscard]] int64_t Length(url::Origin context_origin);
 
@@ -302,7 +313,7 @@
           pending_listener);
 
   // Clears all origins that match `storage_key_matcher` run on the owning
-  // StoragePartition's `SpecialStoragePolicy` and have `last_used_time` between
+  // StoragePartition's `SpecialStoragePolicy` and have `creation_time` between
   // the times `begin` and `end`. If `perform_storage_cleanup` is true, vacuums
   // the database afterwards. Returns whether the transaction was successful.
   [[nodiscard]] OperationResult PurgeMatchingOrigins(
@@ -311,10 +322,10 @@
       base::Time end,
       bool perform_storage_cleanup = false);
 
-  // Clear all entries for all origins whose `last_read_time` (i.e. creation
-  // time) falls before `clock_->Now() - origin_staleness_threshold_`. Also
-  // purges, for all origins, all privacy budget withdrawals that have
-  // `time_stamps` older than `clock_->Now() - budget_interval_`.
+  // Clear all entries for all origins whose `creation_time` falls before
+  // `clock_->Now() - origin_staleness_threshold_`. Also purges, for all
+  // origins, all privacy budget withdrawals that have `time_stamps` older than
+  // `clock_->Now() - budget_interval_`.
   [[nodiscard]] OperationResult PurgeStaleOrigins();
 
   // Fetches a vector of `mojom::StorageUsageInfoPtr`, with one
@@ -336,8 +347,7 @@
   // retrieval was successful.
   [[nodiscard]] BudgetResult GetRemainingBudget(url::Origin context_origin);
 
-  // Retrieves the most recent creation time (currently in the schema as
-  // `last_used_time`) for `context_origin`.
+  // Retrieves the most recent `creation_time` for `context_origin`.
   [[nodiscard]] TimeResult GetCreationTime(url::Origin context_origin);
 
   // Calls `Length()`, `GetRemainingBudget()`, and `GetCreationTime()`, then
@@ -355,7 +365,7 @@
   // Returns the `db_status_` for tests.
   [[nodiscard]] InitStatus DBStatusForTesting() const;
 
-  // Changes `last_used_time` to `new_creation_time` for `context_origin`.
+  // Changes `creation_time` to `new_creation_time` for `context_origin`.
   [[nodiscard]] bool OverrideCreationTimeForTesting(
       url::Origin context_origin,
       base::Time new_creation_time);
@@ -384,8 +394,8 @@
   //
   // Sets two example key-value pairs for `origin1`, one example pair for
   // `origin2`, and two example pairs for `origin3`, while also overriding the
-  // `last_used_time` for `origin2` so that it is 1 day earlier and the
-  // `last_used_time` for `origin3` so that it is 60 days earlier.
+  // `creation_time` for `origin2` so that it is 1 day earlier and the
+  // `creation_time` for `origin3` so that it is 60 days earlier.
   [[nodiscard]] bool PopulateDatabaseForTesting(url::Origin origin1,
                                                 url::Origin origin2,
                                                 url::Origin origin3);
@@ -448,11 +458,11 @@
                                  const std::u16string& key)
       VALID_CONTEXT_REQUIRED(sequence_checker_);
 
-  // Retrieves the `length` in `out_length`, and `last_used_time` (i.e. creation
-  // time) in `out_creation_time`, of `context_origin`. Leaves the `out_*`
-  // parameters unchanged if `context_origin` is not found in the database.
-  // Returns an `OperationResult` indicating success, error, or that the origin
-  // was not found.
+  // Retrieves the `length` in `out_length`, and `creation_time` in
+  // `out_creation_time`, of `context_origin`. Leaves the `out_*` parameters
+  // unchanged if `context_origin` is not found in the database. Returns an
+  // `OperationResult` indicating success, error, or that the origin was not
+  // found.
   [[nodiscard]] OperationResult GetOriginInfo(const std::string& context_origin,
                                               int64_t* out_length,
                                               base::Time* out_creation_time)
@@ -466,8 +476,9 @@
                                   bool delete_origin_if_empty = false)
       VALID_CONTEXT_REQUIRED(sequence_checker_);
 
-  // Inserts a triple for `(context_origin,key,value)` into
-  // `values_mapping`.
+  // Inserts a tuple for `(context_origin,key,value,clock_->Now())` into
+  // `values_mapping` (i.e. uses the current time as `last_used_time`).
+  // Also calls `UpdateLength()` with `delta=1`.
   [[nodiscard]] bool InsertIntoValuesMapping(const std::string& context_origin,
                                              const std::u16string& key,
                                              const std::u16string& value)
diff --git a/components/services/storage/shared_storage/shared_storage_database_migrations.cc b/components/services/storage/shared_storage/shared_storage_database_migrations.cc
new file mode 100644
index 0000000..a304996
--- /dev/null
+++ b/components/services/storage/shared_storage/shared_storage_database_migrations.cc
@@ -0,0 +1,93 @@
+// 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 "components/services/storage/shared_storage/shared_storage_database_migrations.h"
+
+#include "sql/database.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace storage {
+
+namespace {
+
+bool MigrateToVersion2(sql::Database& db,
+                       sql::MetaTable& meta_table,
+                       base::Clock* clock) {
+  sql::Transaction transaction(&db);
+  if (!transaction.Begin())
+    return false;
+
+  static constexpr char kNewValuesTableSql[] =
+      "CREATE TABLE new_values_mapping("
+      "context_origin TEXT NOT NULL,"
+      "key TEXT NOT NULL,"
+      "value TEXT,"
+      "last_used_time INTEGER NOT NULL,"
+      "PRIMARY KEY(context_origin,key)) WITHOUT ROWID";
+  if (!db.Execute(kNewValuesTableSql))
+    return false;
+
+  static constexpr char kInsertSql[] =
+      "INSERT INTO new_values_mapping(context_origin, key, value, "
+      "last_used_time) "
+      "SELECT context_origin, key, value, ? "
+      "FROM values_mapping";
+
+  sql::Statement statement(db.GetCachedStatement(SQL_FROM_HERE, kInsertSql));
+  statement.BindTime(0, clock->Now());
+  if (!statement.Run())
+    return false;
+
+  static constexpr char kDropOldValuesSql[] = "DROP TABLE values_mapping";
+  if (!db.Execute(kDropOldValuesSql))
+    return false;
+
+  static constexpr char kRenameValuesMapSql[] =
+      "ALTER TABLE new_values_mapping RENAME TO values_mapping";
+  if (!db.Execute(kRenameValuesMapSql))
+    return false;
+
+  static constexpr char kRenameCreationColumnSql[] =
+      "ALTER TABLE per_origin_mapping RENAME COLUMN last_used_time TO "
+      "creation_time";
+  if (!db.Execute(kRenameCreationColumnSql))
+    return false;
+
+  static constexpr char kAddValuesLastUsedTimeIndexSql[] =
+      "CREATE INDEX IF NOT EXISTS values_mapping_last_used_time_idx "
+      "ON values_mapping(last_used_time)";
+  if (!db.Execute(kAddValuesLastUsedTimeIndexSql))
+    return false;
+
+  static constexpr char kAddCreationTimeIndexSql[] =
+      "CREATE INDEX IF NOT EXISTS per_origin_mapping_creation_time_idx "
+      "ON per_origin_mapping(creation_time)";
+  if (!db.Execute(kAddCreationTimeIndexSql))
+    return false;
+
+  static constexpr char kDropLastUsedTimeIndexSql[] =
+      "DROP INDEX IF EXISTS per_origin_mapping_last_used_time_idx";
+  if (!db.Execute(kDropLastUsedTimeIndexSql))
+    return false;
+
+  meta_table.SetVersionNumber(2);
+  return transaction.Commit();
+}
+
+}  // namespace
+
+bool UpgradeSharedStorageDatabaseSchema(sql::Database& db,
+                                        sql::MetaTable& meta_table,
+                                        base::Clock* clock) {
+  if (meta_table.GetVersionNumber() == 1 &&
+      !MigrateToVersion2(db, meta_table, clock)) {
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace storage
diff --git a/components/services/storage/shared_storage/shared_storage_database_migrations.h b/components/services/storage/shared_storage/shared_storage_database_migrations.h
new file mode 100644
index 0000000..ecf33631e
--- /dev/null
+++ b/components/services/storage/shared_storage/shared_storage_database_migrations.h
@@ -0,0 +1,83 @@
+// 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 COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_DATABASE_MIGRATIONS_H_
+#define COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_DATABASE_MIGRATIONS_H_
+
+#include "base/time/clock.h"
+
+namespace sql {
+class Database;
+class MetaTable;
+}  // namespace sql
+
+namespace storage {
+
+// Changes to the SQL database schema or data format must be accompanied by
+// a database migration. This includes new columns, new tables, or changes to
+// existing stored data. Data loss must be avoided during migrations, because
+// the impact could extend to weeks of data.
+//
+// To do a migration, add a new function `MigrateToVersionN()` which performs
+// the modifications to the old database, and increment `kCurrentVersionNumber`
+// in shared_storage_database.cc.
+//
+// Generate a new sql file which will hold the new database schema (with `N`
+// replaced by the new version number):
+//  * Build and open the Chromium executable
+//  * Go to a site which uses shared storage to init the database
+//   (e.g. https://shared-storage-demo.web.app/).
+//  * Go to chrome://version if needed to confirm/copy the Profile Path
+//    ($USERDATADIR below).
+//  * Build the sqlite_shell executable:
+//    > autoninja -C out/Default sqlite_shell
+//  * Using the sqlite_shell executable do the following:
+//    > out/Default/sqlite_shell
+//    >> .open $USER_DATA_DIR/SharedStorage
+//    >> .output version_N.sql
+//    >> .dump
+//    >> .quit
+//  * Find file version_N.sql in your current directory.
+//  * Run bash command:
+//    > mv version_N.sql components/test/data/storage/shared_storage.vN.sql
+//  * Replace any rows with relevant test data needed for below.
+//  * Add newlines between statements as necessary.
+//
+// Add a new test to `shared_storage_database_migration_unittest.cc` named
+// "MigrateVersionNToCurrent" where N is the previous database version.
+//
+// Update other tests in `shared_storage_database_migration_unittest.cc` as
+// necessary.
+//
+// Update the expectations in `VerifySharedStorageTablesAndColumns()` of
+// `components/services/storage/shared_storage/shared_storage_test_utils.cc`
+// for the new schema and indices.
+//
+// You will need to increment the `last_compatible_version` in
+// `components/test/data/storage/shared_storage.init_too_new.sql`
+//
+// Other non-migration test files in `components/test/data/storage/` that must
+// be using at least a compatible version are the following (files not listed
+// below should not be updated). Update them if you have time. If you are
+// deprecating the version that they're using, however, then you MUST update
+// them (and ensure that any test data file name changes are also reflected in
+// the code of the unit tests that load them):
+//  * shared_storage.v*.single_origin.sql
+//  * shared_storage.v*.iterator.sql
+//  * shared_storage.v*.empty_values_mapping.5origins.sql
+//  * shared_storage.v*.empty_values_mapping.6origins.sql
+//  * shared_storage.v*.empty_values_mapping.7origins.sql
+//  * shared_storage.v*.empty_values_mapping.8origins.sql
+
+// Upgrades `db` to the latest schema, and updates the version stored in
+// `meta_table` accordingly. Must be called with an open `db`. Returns false on
+// failure.
+[[nodiscard]] bool UpgradeSharedStorageDatabaseSchema(
+    sql::Database& db,
+    sql::MetaTable& meta_table,
+    base::Clock* clock);
+
+}  // namespace storage
+
+#endif  // COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_DATABASE_MIGRATIONS_H_
diff --git a/components/services/storage/shared_storage/shared_storage_database_migrations_unittest.cc b/components/services/storage/shared_storage/shared_storage_database_migrations_unittest.cc
new file mode 100644
index 0000000..b10208a
--- /dev/null
+++ b/components/services/storage/shared_storage/shared_storage_database_migrations_unittest.cc
@@ -0,0 +1,356 @@
+// 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 "components/services/storage/shared_storage/shared_storage_database_migrations.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "base/time/time.h"
+#include "components/services/storage/shared_storage/shared_storage_database.h"
+#include "components/services/storage/shared_storage/shared_storage_options.h"
+#include "components/services/storage/shared_storage/shared_storage_test_utils.h"
+#include "sql/database.h"
+#include "sql/statement.h"
+#include "sql/test/test_helpers.h"
+#include "storage/browser/quota/special_storage_policy.h"
+#include "storage/browser/test/mock_special_storage_policy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+
+namespace storage {
+
+namespace {
+
+std::string RemoveQuotes(std::string input) {
+  std::string output;
+  base::RemoveChars(input, "\"", &output);
+  return output;
+}
+
+}  // namespace
+
+class SharedStorageDatabaseMigrationsTest : public testing::Test {
+ public:
+  SharedStorageDatabaseMigrationsTest() {
+    special_storage_policy_ = base::MakeRefCounted<MockSpecialStoragePolicy>();
+  }
+
+  ~SharedStorageDatabaseMigrationsTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        {blink::features::kSharedStorageAPI},
+        {{"MaxSharedStorageInitTries", "2"}});
+
+    // Get a temporary directory for the test DB files.
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+    file_name_ = temp_dir_.GetPath().AppendASCII("TestSharedStorage.db");
+  }
+
+  void TearDown() override { EXPECT_TRUE(temp_dir_.Delete()); }
+
+  void MigrateDatabase() {
+    auto shared_storage_db = std::make_unique<SharedStorageDatabase>(
+        file_name_, special_storage_policy_,
+        SharedStorageOptions::Create()->GetDatabaseOptions());
+
+    // We need to run an operation on storage to force the lazy initialization.
+    std::ignore = shared_storage_db->Get(
+        url::Origin::Create(GURL("http://google.com/")), u"key1");
+  }
+
+  std::string GetCurrentSchema() {
+    base::FilePath current_version_path =
+        temp_dir_.GetPath().Append(FILE_PATH_LITERAL("TestCurrentVersion.db"));
+    EXPECT_TRUE(CreateDatabaseFromSQL(current_version_path,
+                                      GetTestFileNameForCurrentVersion()));
+    sql::Database db;
+    EXPECT_TRUE(db.Open(current_version_path));
+    return db.GetSchema();
+  }
+
+  int VersionFromDatabase(sql::Database& db) {
+    // Get version.
+    sql::Statement statement(
+        db.GetUniqueStatement("SELECT value FROM meta WHERE key='version'"));
+    if (!statement.Step())
+      return 0;
+    return statement.ColumnInt(0);
+  }
+
+  int CompatibleVersionFromDatabase(sql::Database& db) {
+    // Get compatible version.
+    sql::Statement statement(db.GetUniqueStatement(
+        "SELECT value FROM meta WHERE key='last_compatible_version'"));
+    if (!statement.Step())
+      return 0;
+    return statement.ColumnInt(0);
+  }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  base::ScopedTempDir temp_dir_;
+  base::FilePath file_name_;
+  scoped_refptr<storage::MockSpecialStoragePolicy> special_storage_policy_;
+};
+
+TEST_F(SharedStorageDatabaseMigrationsTest, MigrateEmptyToCurrent) {
+  {
+    auto shared_storage_db = std::make_unique<SharedStorageDatabase>(
+        file_name_, special_storage_policy_,
+        SharedStorageOptions::Create()->GetDatabaseOptions());
+
+    // We need to run a non-trivial operation on storage to force the lazy
+    // initialization.
+    std::ignore = shared_storage_db->Set(
+        url::Origin::Create(GURL("http://google.com/")), u"key0", u"value0");
+  }
+
+  // Verify schema is current.
+  {
+    sql::Database db;
+    ASSERT_TRUE(db.Open(file_name_));
+
+    // Check version.
+    EXPECT_EQ(SharedStorageDatabase::kCurrentVersionNumber,
+              VersionFromDatabase(db));
+
+    // Check that expected tables are present.
+    EXPECT_TRUE(db.DoesTableExist("meta"));
+    EXPECT_TRUE(db.DoesTableExist("values_mapping"));
+    EXPECT_TRUE(db.DoesTableExist("per_origin_mapping"));
+    EXPECT_TRUE(db.DoesTableExist("budget_mapping"));
+
+    // Compare without quotes as sometimes migrations cause table names to be
+    // string literals.
+    EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
+  }
+}
+
+TEST_F(SharedStorageDatabaseMigrationsTest,
+       MigrateLastDeprecatedVersionToCurrent) {
+  ASSERT_TRUE(CreateDatabaseFromSQL(
+      file_name_, GetTestFileNameForLatestDeprecatedVersion()));
+
+  // Verify pre-conditions.
+  {
+    sql::Database db;
+    ASSERT_TRUE(db.Open(file_name_));
+
+    // Check version.
+    EXPECT_EQ(SharedStorageDatabase::kDeprecatedVersionNumber,
+              VersionFromDatabase(db));
+
+    sql::Statement statement(
+        db.GetUniqueStatement("SELECT COUNT(*) FROM values_mapping"));
+
+    ASSERT_TRUE(statement.Step());
+    ASSERT_LT(0, statement.ColumnInt(0));
+  }
+
+  MigrateDatabase();
+
+  // Verify schema is current.
+  {
+    sql::Database db;
+    ASSERT_TRUE(db.Open(file_name_));
+
+    // Check version.
+    EXPECT_EQ(SharedStorageDatabase::kCurrentVersionNumber,
+              VersionFromDatabase(db));
+
+    // Compare without quotes as sometimes migrations cause table names to be
+    // string literals.
+    EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
+
+    // Verify that data is not preserved across the migration.
+    sql::Statement statement(
+        db.GetUniqueStatement("SELECT COUNT(*) FROM values_mapping"));
+
+    ASSERT_TRUE(statement.Step());
+    ASSERT_EQ(0, statement.ColumnInt(0));
+  }
+}
+
+TEST_F(SharedStorageDatabaseMigrationsTest, MigrateTooNewVersionToCurrent) {
+  ASSERT_TRUE(
+      CreateDatabaseFromSQL(file_name_, "shared_storage.init_too_new.sql"));
+
+  // Verify pre-conditions.
+  {
+    sql::Database db;
+    ASSERT_TRUE(db.Open(file_name_));
+
+    // Check compatible version.
+    EXPECT_LT(SharedStorageDatabase::kCurrentVersionNumber,
+              CompatibleVersionFromDatabase(db));
+
+    sql::Statement statement(
+        db.GetUniqueStatement("SELECT COUNT(*) FROM values_mapping"));
+
+    ASSERT_TRUE(statement.Step());
+    ASSERT_LT(0, statement.ColumnInt(0));
+  }
+
+  MigrateDatabase();
+
+  // Verify schema is current.
+  {
+    sql::Database db;
+    ASSERT_TRUE(db.Open(file_name_));
+
+    // Check version.
+    EXPECT_EQ(SharedStorageDatabase::kCurrentVersionNumber,
+              VersionFromDatabase(db));
+
+    // Check compatible version.
+    EXPECT_GE(SharedStorageDatabase::kCurrentVersionNumber,
+              CompatibleVersionFromDatabase(db));
+
+    // Compare without quotes as sometimes migrations cause table names to be
+    // string literals.
+    EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
+
+    // Verify that data is not preserved across the migration.
+    sql::Statement statement(
+        db.GetUniqueStatement("SELECT COUNT(*) FROM values_mapping"));
+
+    ASSERT_TRUE(statement.Step());
+    ASSERT_EQ(0, statement.ColumnInt(0));
+  }
+}
+
+TEST_F(SharedStorageDatabaseMigrationsTest, MigrateVersion1ToCurrent) {
+  ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, GetTestFileNameForVersion(1)));
+
+  // Verify pre-conditions.
+  {
+    sql::Database db;
+    ASSERT_TRUE(db.Open(file_name_));
+
+    // `context_origin`, `key`, and `value`.
+    EXPECT_EQ(3u, sql::test::CountTableColumns(&db, "values_mapping"));
+
+    // Implicit index on `meta`, `per_origin_mapping_last_used_time_idx`,
+    // and budget_mapping_origin_time_stamp_idx.
+    EXPECT_EQ(3u, sql::test::CountSQLIndices(&db));
+
+    ASSERT_FALSE(db.DoesColumnExist("values_mapping", "last_used_time"));
+    ASSERT_TRUE(db.DoesColumnExist("per_origin_mapping", "last_used_time"));
+    ASSERT_FALSE(db.DoesColumnExist("per_origin_mapping", "creation_time"));
+    ASSERT_FALSE(db.DoesIndexExist("values_mapping_last_used_time_idx"));
+    ASSERT_TRUE(db.DoesIndexExist("per_origin_mapping_last_used_time_idx"));
+    ASSERT_FALSE(db.DoesIndexExist("per_origin_mapping_creation_time_idx"));
+  }
+
+  MigrateDatabase();
+  base::Time now = base::Time::Now();
+
+  // Verify schema is current.
+  {
+    sql::Database db;
+    ASSERT_TRUE(db.Open(file_name_));
+
+    // Check version.
+    EXPECT_EQ(SharedStorageDatabase::kCurrentVersionNumber,
+              VersionFromDatabase(db));
+
+    // Compare without quotes as sometimes migrations cause table names to be
+    // string literals.
+    EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
+
+    // Verify that data is preserved across the migration.
+    sql::Statement count_statement(
+        db.GetUniqueStatement("SELECT COUNT(*) FROM values_mapping"));
+
+    ASSERT_TRUE(count_statement.Step());
+    ASSERT_LT(0, count_statement.ColumnInt(0));
+
+    // Verify that the `last_used_time` in `values_mapping` is the time recorded
+    // as the current time just after migration (within a tolerance).
+    sql::Statement select_statement(
+        db.GetUniqueStatement("SELECT * FROM values_mapping"));
+
+    ASSERT_TRUE(select_statement.Step());
+    base::Time last_used_time = select_statement.ColumnTime(3);
+    ASSERT_LE(last_used_time, now);
+    ASSERT_GE(last_used_time, now - TestTimeouts::action_max_timeout());
+  }
+}
+
+// Test loading version 1 database with no budget tables.
+TEST_F(SharedStorageDatabaseMigrationsTest,
+       MigrateVersion1NoBudgetTablesToCurrent) {
+  ASSERT_TRUE(CreateDatabaseFromSQL(file_name_,
+                                    "shared_storage.v1.no_budget_table.sql"));
+
+  // Verify pre-conditions.
+  {
+    sql::Database db;
+    ASSERT_TRUE(db.Open(file_name_));
+
+    // `meta`, `values_mapping`, and `per_origin_mapping`.
+    EXPECT_EQ(3u, sql::test::CountSQLTables(&db));
+
+    // `context_origin`, `key`, and `value`.
+    EXPECT_EQ(3u, sql::test::CountTableColumns(&db, "values_mapping"));
+
+    // Implicit index on `meta`, and `per_origin_mapping_last_used_time_idx`.
+    EXPECT_EQ(2u, sql::test::CountSQLIndices(&db));
+
+    ASSERT_FALSE(db.DoesTableExist("budget_mapping"));
+    ASSERT_FALSE(db.DoesColumnExist("values_mapping", "last_used_time"));
+    ASSERT_TRUE(db.DoesColumnExist("per_origin_mapping", "last_used_time"));
+    ASSERT_FALSE(db.DoesColumnExist("per_origin_mapping", "creation_time"));
+    ASSERT_FALSE(db.DoesIndexExist("values_mapping_last_used_time_idx"));
+    ASSERT_TRUE(db.DoesIndexExist("per_origin_mapping_last_used_time_idx"));
+    ASSERT_FALSE(db.DoesIndexExist("per_origin_mapping_creation_time_idx"));
+    ASSERT_FALSE(db.DoesIndexExist("budget_mapping_origin_time_stamp_idx"));
+  }
+
+  MigrateDatabase();
+  base::Time now = base::Time::Now();
+
+  // Verify schema is current.
+  {
+    sql::Database db;
+    ASSERT_TRUE(db.Open(file_name_));
+
+    // Check version.
+    EXPECT_EQ(SharedStorageDatabase::kCurrentVersionNumber,
+              VersionFromDatabase(db));
+
+    // Compare without quotes as sometimes migrations cause table names to be
+    // string literals.
+    EXPECT_EQ(RemoveQuotes(GetCurrentSchema()), RemoveQuotes(db.GetSchema()));
+
+    // Verify that data is preserved across the migration.
+    sql::Statement count_statement(
+        db.GetUniqueStatement("SELECT COUNT(*) FROM values_mapping"));
+
+    ASSERT_TRUE(count_statement.Step());
+    ASSERT_LT(0, count_statement.ColumnInt(0));
+
+    // Verify that the `last_used_time` in `values_mapping` is the time recorded
+    // as the current time just after migration (within a tolerance).
+    sql::Statement select_statement(
+        db.GetUniqueStatement("SELECT * FROM values_mapping"));
+
+    ASSERT_TRUE(select_statement.Step());
+    base::Time last_used_time = select_statement.ColumnTime(3);
+    ASSERT_LE(last_used_time, now);
+    ASSERT_GE(last_used_time, now - TestTimeouts::action_max_timeout());
+  }
+}
+
+}  // namespace storage
diff --git a/components/services/storage/shared_storage/shared_storage_database_unittest.cc b/components/services/storage/shared_storage/shared_storage_database_unittest.cc
index 69064f9..90bdc0e 100644
--- a/components/services/storage/shared_storage/shared_storage_database_unittest.cc
+++ b/components/services/storage/shared_storage/shared_storage_database_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
 #include "base/time/time.h"
 #include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
 #include "components/services/storage/shared_storage/shared_storage_options.h"
@@ -100,7 +101,7 @@
   // Initialize a shared storage database instance from the SQL file at
   // `relative_file_path` in the "storage/" subdirectory of test data.
   std::unique_ptr<SharedStorageDatabase> LoadFromFile(
-      const char* relative_file_path) {
+      std::string relative_file_path) {
     if (!CreateDatabaseFromSQL(file_name_, relative_file_path)) {
       ADD_FAILURE() << "Failed loading " << relative_file_path;
       return nullptr;
@@ -133,9 +134,9 @@
   base::HistogramTester histogram_tester_;
 };
 
-// Test loading version 1 database.
-TEST_F(SharedStorageDatabaseTest, Version1_LoadFromFile) {
-  db_ = LoadFromFile("shared_storage.v1.sql");
+// Test loading current version database.
+TEST_F(SharedStorageDatabaseTest, CurrentVersion_LoadFromFile) {
+  db_ = LoadFromFile(GetTestFileNameForCurrentVersion());
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
 
@@ -147,7 +148,15 @@
 
   url::Origin google_com = url::Origin::Create(GURL("http://google.com/"));
   EXPECT_EQ(db_->Get(google_com, u"key1").data, u"value1");
+  EXPECT_EQ(db_->Get(google_com, u"key1")
+                .last_used_time.ToDeltaSinceWindowsEpoch()
+                .InMicroseconds(),
+            13312097333991364);
   EXPECT_EQ(db_->Get(google_com, u"key2").data, u"value2");
+  EXPECT_EQ(db_->Get(google_com, u"key2")
+                .last_used_time.ToDeltaSinceWindowsEpoch()
+                .InMicroseconds(),
+            13313037427966159);
 
   // Because the SQL database is lazy-initialized, wait to verify tables and
   // columns until after the first call to `Get()`.
@@ -159,6 +168,10 @@
 
   url::Origin chromium_org = url::Origin::Create(GURL("http://chromium.org/"));
   EXPECT_EQ(db_->Get(chromium_org, u"a").data, u"");
+  EXPECT_EQ(db_->Get(chromium_org, u"a")
+                .last_used_time.ToDeltaSinceWindowsEpoch()
+                .InMicroseconds(),
+            13313037416916308);
 
   TestSharedStorageEntriesListener listener(
       task_environment_.GetMainThreadTaskRunner());
@@ -252,7 +265,8 @@
   EXPECT_EQ(base::Time(), result.time);
 
   histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram, true, 1);
-  histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 40, 1);
+  histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+  EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
   histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 9, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 18, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesMinHistogram, 1, 1);
@@ -362,7 +376,8 @@
   EXPECT_DOUBLE_EQ(kBitBudget, db_->GetRemainingBudget(youtube_com).bits);
 
   histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram, true, 1);
-  histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 40, 1);
+  histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+  EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
   histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 9, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 18, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesMinHistogram, 1, 1);
@@ -374,10 +389,10 @@
   EXPECT_TRUE(db_->Destroy());
 }
 
-TEST_F(SharedStorageDatabaseTest, Version1_DestroyTooNew) {
+TEST_F(SharedStorageDatabaseTest, DestroyTooNew) {
   // Initialization should fail, since the last compatible version number
   // is too high.
-  db_ = LoadFromFile("shared_storage.v1.init_too_new.sql");
+  db_ = LoadFromFile("shared_storage.init_too_new.sql");
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
   ASSERT_TRUE(SqlDB());
@@ -412,10 +427,10 @@
   EXPECT_TRUE(db_->Destroy());
 }
 
-TEST_F(SharedStorageDatabaseTest, Version0_DestroyTooOld) {
+TEST_F(SharedStorageDatabaseTest, DestroyTooOld) {
   // Initialization should fail, since the current version number
   // is too low and we're forcing there not to be a retry attempt.
-  db_ = LoadFromFile("shared_storage.v0.init_too_old.sql");
+  db_ = LoadFromFile(GetTestFileNameForLatestDeprecatedVersion());
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
   ASSERT_TRUE(SqlDB());
@@ -471,7 +486,8 @@
     histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram,
                                          db_->is_filebacked(), 1);
     if (db_->is_filebacked()) {
-      histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 29, 1);
+      histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+      EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
       histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 0, 1);
       histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 0, 1);
     }
@@ -484,14 +500,32 @@
                          testing::PrintToStringParamName());
 
 TEST_P(SharedStorageDatabaseParamTest, BasicOperations) {
+  clock_.SetNow(base::Time::Now());
+
   const url::Origin kOrigin1 =
       url::Origin::Create(GURL("http://www.example1.test"));
   EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+  base::Time now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
 
+  // Verify that `last_used_time1` is set to `now` (within a tolerance).
+  base::Time last_used_time1 = db_->Get(kOrigin1, u"key1").last_used_time;
+  ASSERT_LE(last_used_time1, now);
+  ASSERT_GE(last_used_time1, now - TestTimeouts::action_max_timeout());
+
+  // Advance the clock to put distance between the last used times.
+  clock_.Advance(base::Hours(12));
+
   EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value2"));
+  now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value2");
 
+  // Verify that `last_used_time2` is set to `now` (within a tolerance).
+  base::Time last_used_time2 = db_->Get(kOrigin1, u"key1").last_used_time;
+  ASSERT_GT(last_used_time2, last_used_time1);
+  ASSERT_LE(last_used_time2, now);
+  ASSERT_GE(last_used_time2, now - TestTimeouts::action_max_timeout());
+
   EXPECT_EQ(OperationResult::kSuccess, db_->Delete(kOrigin1, u"key1"));
   EXPECT_EQ(OperationResult::kNotFound, db_->Get(kOrigin1, u"key1").result);
 
@@ -508,28 +542,62 @@
 }
 
 TEST_P(SharedStorageDatabaseParamTest, IgnoreIfPresent) {
+  clock_.SetNow(base::Time::Now());
+
   const url::Origin kOrigin1 =
       url::Origin::Create(GURL("http://www.example1.test"));
   EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key1", u"value1"));
+  base::Time now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
 
+  // Verify that `last_used_time1` is set to `now` (within a tolerance).
+  base::Time last_used_time1 = db_->Get(kOrigin1, u"key1").last_used_time;
+  ASSERT_LE(last_used_time1, now);
+  ASSERT_GE(last_used_time1, now - TestTimeouts::action_max_timeout());
+
+  // Advance the clock to put distance between the last used times.
+  clock_.Advance(base::Hours(12));
+
   // The database does not set a new value for "key1", but retains the
   // previously set value "value1" because `behavior` is `kIgnoreIfPresent`.
   EXPECT_EQ(OperationResult::kIgnored,
             db_->Set(kOrigin1, u"key1", u"value2",
                      /*behavior=*/SetBehavior::kIgnoreIfPresent));
+  now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
 
+  // Verify that `last_used_time2` is set to `now` (within a tolerance).
+  base::Time last_used_time2 = db_->Get(kOrigin1, u"key1").last_used_time;
+  ASSERT_GT(last_used_time2, last_used_time1);
+  ASSERT_LE(last_used_time2, now);
+  ASSERT_GE(last_used_time2, now - TestTimeouts::action_max_timeout());
+
   EXPECT_EQ(OperationResult::kSet, db_->Set(kOrigin1, u"key2", u"value1"));
+  now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin1, u"key2").data, u"value1");
 
+  // Verify that `last_used_time1` is set to `now` (within a tolerance).
+  last_used_time1 = db_->Get(kOrigin1, u"key2").last_used_time;
+  ASSERT_LE(last_used_time1, now);
+  ASSERT_GE(last_used_time1, now - TestTimeouts::action_max_timeout());
+
+  // Advance the clock to put distance between the last used times.
+  clock_.Advance(base::Hours(12));
+
   // Having `behavior` set to `kDefault` makes `Set()` override any previous
   // value.
   EXPECT_EQ(OperationResult::kSet,
             db_->Set(kOrigin1, u"key2", u"value2",
                      /*behavior=*/SetBehavior::kDefault));
+  now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin1, u"key2").data, u"value2");
 
+  // Verify that `last_used_time2` is set to `now` (within a tolerance).
+  last_used_time2 = db_->Get(kOrigin1, u"key2").last_used_time;
+  ASSERT_GT(last_used_time2, last_used_time1);
+  ASSERT_LE(last_used_time2, now);
+  ASSERT_GE(last_used_time2, now - TestTimeouts::action_max_timeout());
+
   const url::Origin kOrigin2 =
       url::Origin::Create(GURL("http://www.example2.test"));
 
@@ -538,25 +606,65 @@
   EXPECT_EQ(OperationResult::kSet,
             db_->Set(kOrigin2, u"key1", u"value1",
                      /*behavior=*/SetBehavior::kIgnoreIfPresent));
+  now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin2, u"key1").data, u"value1");
 
+  // Verify that `last_used_time1` is set to `now` (within a tolerance).
+  last_used_time1 = db_->Get(kOrigin2, u"key1").last_used_time;
+  ASSERT_LE(last_used_time1, now);
+  ASSERT_GE(last_used_time1, now - TestTimeouts::action_max_timeout());
+
   EXPECT_EQ(OperationResult::kSet,
             db_->Set(kOrigin2, u"key2", u"value2",
                      /*behavior=*/SetBehavior::kDefault));
+  now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin2, u"key2").data, u"value2");
+
+  // Verify that `last_used_time1` is set to `now` (within a tolerance).
+  last_used_time1 = db_->Get(kOrigin2, u"key2").last_used_time;
+  ASSERT_LE(last_used_time1, now);
+  ASSERT_GE(last_used_time1, now - TestTimeouts::action_max_timeout());
 }
 
 TEST_P(SharedStorageDatabaseParamTest, Append) {
+  clock_.SetNow(base::Time::Now());
+
   const url::Origin kOrigin1 =
       url::Origin::Create(GURL("http://www.example1.test"));
   EXPECT_EQ(OperationResult::kSet, db_->Append(kOrigin1, u"key1", u"value1"));
+  base::Time now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1");
 
-  EXPECT_EQ(OperationResult::kSet, db_->Append(kOrigin1, u"key1", u"value1"));
-  EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1value1");
+  // Verify that `last_used_time1` is set to `now` (within a tolerance).
+  base::Time last_used_time1 = db_->Get(kOrigin1, u"key1").last_used_time;
+  ASSERT_LE(last_used_time1, now);
+  ASSERT_GE(last_used_time1, now - TestTimeouts::action_max_timeout());
+
+  // Advance the clock to put distance between the last used times.
+  clock_.Advance(base::Hours(12));
 
   EXPECT_EQ(OperationResult::kSet, db_->Append(kOrigin1, u"key1", u"value1"));
+  now = clock_.Now();
+  EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1value1");
+
+  // Verify that `last_used_time2` is set to `now` (within a tolerance).
+  base::Time last_used_time2 = db_->Get(kOrigin1, u"key1").last_used_time;
+  ASSERT_GT(last_used_time2, last_used_time1);
+  ASSERT_LE(last_used_time2, now);
+  ASSERT_GE(last_used_time2, now - TestTimeouts::action_max_timeout());
+
+  // Advance the clock to put distance between the last used times.
+  clock_.Advance(base::Hours(12));
+
+  EXPECT_EQ(OperationResult::kSet, db_->Append(kOrigin1, u"key1", u"value1"));
+  now = clock_.Now();
   EXPECT_EQ(db_->Get(kOrigin1, u"key1").data, u"value1value1value1");
+
+  // Verify that `last_used_time3` is set to `now` (within a tolerance).
+  base::Time last_used_time3 = db_->Get(kOrigin1, u"key1").last_used_time;
+  ASSERT_GT(last_used_time3, last_used_time2);
+  ASSERT_LE(last_used_time3, now);
+  ASSERT_GE(last_used_time3, now - TestTimeouts::action_max_timeout());
 }
 
 TEST_P(SharedStorageDatabaseParamTest, Length) {
@@ -1395,7 +1503,7 @@
 };
 
 TEST_F(SharedStorageDatabaseIteratorTest, Keys) {
-  db_ = LoadFromFile("shared_storage.v1.iterator.sql");
+  db_ = LoadFromFile("shared_storage.v2.iterator.sql");
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
 
@@ -1425,7 +1533,8 @@
   utility.VerifyNoErrorForId(id2);
 
   histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram, true, 1);
-  histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 40, 1);
+  histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+  EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
   histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 2, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 227, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesMinHistogram, 26, 1);
@@ -1436,7 +1545,7 @@
 }
 
 TEST_F(SharedStorageDatabaseIteratorTest, Entries) {
-  db_ = LoadFromFile("shared_storage.v1.iterator.sql");
+  db_ = LoadFromFile("shared_storage.v2.iterator.sql");
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
 
@@ -1467,7 +1576,8 @@
   utility.VerifyNoErrorForId(id2);
 
   histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram, true, 1);
-  histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 40, 1);
+  histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+  EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
   histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 2, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 227, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesMinHistogram, 26, 1);
@@ -1480,7 +1590,7 @@
 // Tests correct calculation of five-number summary when there is only one
 // origin.
 TEST_F(SharedStorageDatabaseTest, SingleOrigin) {
-  db_ = LoadFromFile("shared_storage.v1.single_origin.sql");
+  db_ = LoadFromFile("shared_storage.v2.single_origin.sql");
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
 
@@ -1492,7 +1602,8 @@
   EXPECT_THAT(origins, ElementsAre(google_com));
 
   histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram, true, 1);
-  histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 29, 1);
+  histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+  EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
   histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 1, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 10, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesMinHistogram, 10, 1);
@@ -1505,7 +1616,7 @@
 // Tests correct calculation of five-number summary when number of origins is
 // greater than one and has remainder 1 modulo 4.
 TEST_F(SharedStorageDatabaseTest, FiveOrigins) {
-  db_ = LoadFromFile("shared_storage.v1.empty_values_mapping.5origins.sql");
+  db_ = LoadFromFile("shared_storage.v2.empty_values_mapping.5origins.sql");
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
 
@@ -1522,7 +1633,8 @@
                                    google_org, gv_com));
 
   histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram, true, 1);
-  histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 29, 1);
+  histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+  EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
   histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 5, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 0, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesMinHistogram, 10, 1);
@@ -1535,7 +1647,7 @@
 // Tests correct calculation of five-number summary when number of origins has
 // remainder 2 modulo 4.
 TEST_F(SharedStorageDatabaseTest, SixOrigins) {
-  db_ = LoadFromFile("shared_storage.v1.empty_values_mapping.6origins.sql");
+  db_ = LoadFromFile("shared_storage.v2.empty_values_mapping.6origins.sql");
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
 
@@ -1553,7 +1665,8 @@
                                    google_org, gv_com, waymo_com));
 
   histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram, true, 1);
-  histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 29, 1);
+  histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+  EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
   histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 6, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 0, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesMinHistogram, 10, 1);
@@ -1566,7 +1679,7 @@
 // Tests correct calculation of five-number summary when number of origins has
 // remainder 3 modulo 4.
 TEST_F(SharedStorageDatabaseTest, SevenOrigins) {
-  db_ = LoadFromFile("shared_storage.v1.empty_values_mapping.7origins.sql");
+  db_ = LoadFromFile("shared_storage.v2.empty_values_mapping.7origins.sql");
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
 
@@ -1587,7 +1700,8 @@
                           waymo_com, with_google_com));
 
   histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram, true, 1);
-  histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 29, 1);
+  histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+  EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
   histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 7, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 0, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesMinHistogram, 10, 1);
@@ -1600,7 +1714,7 @@
 // Tests correct calculation of five-number summary when number of origins has
 // remainder 0 modulo 4.
 TEST_F(SharedStorageDatabaseTest, EightOrigins) {
-  db_ = LoadFromFile("shared_storage.v1.empty_values_mapping.8origins.sql");
+  db_ = LoadFromFile("shared_storage.v2.empty_values_mapping.8origins.sql");
   ASSERT_TRUE(db_);
   ASSERT_TRUE(db_->is_filebacked());
 
@@ -1622,7 +1736,8 @@
                           waymo_com, with_google_com, youtube_com));
 
   histogram_tester_.ExpectUniqueSample(kIsFileBackedHistogram, true, 1);
-  histogram_tester_.ExpectUniqueSample(kFileSizeKBHistogram, 29, 1);
+  histogram_tester_.ExpectTotalCount(kFileSizeKBHistogram, 1);
+  EXPECT_GT(histogram_tester_.GetTotalSum(kFileSizeKBHistogram), 0);
   histogram_tester_.ExpectUniqueSample(kNumOriginsHistogram, 8, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesTotalHistogram, 0, 1);
   histogram_tester_.ExpectUniqueSample(kNumEntriesMinHistogram, 10, 1);
diff --git a/components/services/storage/shared_storage/shared_storage_manager_unittest.cc b/components/services/storage/shared_storage/shared_storage_manager_unittest.cc
index 585c9b79..9020120c 100644
--- a/components/services/storage/shared_storage/shared_storage_manager_unittest.cc
+++ b/components/services/storage/shared_storage/shared_storage_manager_unittest.cc
@@ -414,7 +414,7 @@
   // Return the relative file path in the "storage/" subdirectory of test data
   // for the SQL file from which to initialize an async shared storage database
   // instance.
-  virtual const char* GetRelativeFilePath() { return nullptr; }
+  virtual std::string GetRelativeFilePath() { return nullptr; }
 
   virtual DBType GetType() { return DBType::kInMemory; }
 
@@ -838,15 +838,17 @@
   bool memory_trimmed_ = false;
 };
 
-class SharedStorageManagerFromFileV1Test : public SharedStorageManagerTest {
+class SharedStorageManagerFromFileTest : public SharedStorageManagerTest {
  public:
   DBType GetType() override { return DBType::kFileBackedFromExisting; }
 
-  const char* GetRelativeFilePath() override { return "shared_storage.v1.sql"; }
+  std::string GetRelativeFilePath() override {
+    return GetTestFileNameForCurrentVersion();
+  }
 };
 
-// Test loading version 1 database.
-TEST_F(SharedStorageManagerFromFileV1Test, Version1_LoadFromFile) {
+// Test loading current version database.
+TEST_F(SharedStorageManagerFromFileTest, CurrentVersion_LoadFromFile) {
   url::Origin google_com = url::Origin::Create(GURL("http://google.com/"));
   EXPECT_EQ(GetSync(google_com, u"key1").data, u"value1");
   EXPECT_EQ(GetSync(google_com, u"key2").data, u"value2");
@@ -974,9 +976,9 @@
 }
 
 class SharedStorageManagerFromFileV1NoBudgetTableTest
-    : public SharedStorageManagerFromFileV1Test {
+    : public SharedStorageManagerFromFileTest {
  public:
-  const char* GetRelativeFilePath() override {
+  std::string GetRelativeFilePath() override {
     return "shared_storage.v1.no_budget_table.sql";
   }
 };
diff --git a/components/services/storage/shared_storage/shared_storage_test_utils.cc b/components/services/storage/shared_storage/shared_storage_test_utils.cc
index 769b767..0faee352 100644
--- a/components/services/storage/shared_storage/shared_storage_test_utils.cc
+++ b/components/services/storage/shared_storage/shared_storage_test_utils.cc
@@ -16,6 +16,7 @@
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
 #include "sql/database.h"
@@ -530,18 +531,22 @@
   // `meta`, `values_mapping`, `per_origin_mapping`, and budget_mapping.
   EXPECT_EQ(4u, sql::test::CountSQLTables(&db));
 
-  // Implicit index on `meta`, `per_origin_mapping_last_used_time_idx`,
-  // and budget_mapping_origin_time_stamp_idx.
-  EXPECT_EQ(3u, sql::test::CountSQLIndices(&db));
+  // Implicit index on `meta`, `values_mapping_last_used_time_idx`,
+  // `per_origin_mapping_creation_time_idx`, and
+  // budget_mapping_origin_time_stamp_idx.
+  EXPECT_EQ(4u, sql::test::CountSQLIndices(&db));
 
   // `key` and `value`.
   EXPECT_EQ(2u, sql::test::CountTableColumns(&db, "meta"));
 
-  // `context_origin`, `script_key`, and `script_value`.
-  EXPECT_EQ(3u, sql::test::CountTableColumns(&db, "values_mapping"));
+  // `context_origin`, `key`, `value`, and `last_used_time`.
+  EXPECT_EQ(4u, sql::test::CountTableColumns(&db, "values_mapping"));
 
-  // `context_origin`, `last_used_time`, and `length`.
+  // `context_origin`, `creation_time`, and `length`.
   EXPECT_EQ(3u, sql::test::CountTableColumns(&db, "per_origin_mapping"));
+
+  // `id`, `context_origin`, `time_stamp`, and `bits_debit`.
+  EXPECT_EQ(4u, sql::test::CountTableColumns(&db, "budget_mapping"));
 }
 
 bool GetTestDataSharedStorageDir(base::FilePath* dir) {
@@ -555,7 +560,7 @@
 }
 
 bool CreateDatabaseFromSQL(const base::FilePath& db_path,
-                           const char* ascii_path) {
+                           std::string ascii_path) {
   base::FilePath dir;
   if (!GetTestDataSharedStorageDir(&dir))
     return false;
@@ -570,4 +575,20 @@
   return BudgetResult(0.0, OperationResult::kSqlError);
 }
 
+std::string GetTestFileNameForVersion(int version_number) {
+  // Should be safe cross platform because StringPrintf has overloads for wide
+  // strings.
+  return base::StringPrintf("shared_storage.v%d.sql", version_number);
+}
+
+std::string GetTestFileNameForCurrentVersion() {
+  return GetTestFileNameForVersion(
+      SharedStorageDatabase::kCurrentVersionNumber);
+}
+
+std::string GetTestFileNameForLatestDeprecatedVersion() {
+  return GetTestFileNameForVersion(
+      SharedStorageDatabase::kDeprecatedVersionNumber);
+}
+
 }  // namespace storage
diff --git a/components/services/storage/shared_storage/shared_storage_test_utils.h b/components/services/storage/shared_storage/shared_storage_test_utils.h
index 42088b3..4410a28 100644
--- a/components/services/storage/shared_storage/shared_storage_test_utils.h
+++ b/components/services/storage/shared_storage/shared_storage_test_utils.h
@@ -327,12 +327,18 @@
 [[nodiscard]] bool GetTestDataSharedStorageDir(base::FilePath* dir);
 
 [[nodiscard]] bool CreateDatabaseFromSQL(const base::FilePath& db_path,
-                                         const char* ascii_path);
+                                         std::string ascii_path);
 
 [[nodiscard]] std::string TimeDeltaToString(base::TimeDelta delta);
 
 [[nodiscard]] BudgetResult MakeBudgetResultForSqlError();
 
+[[nodiscard]] std::string GetTestFileNameForVersion(int version_number);
+
+[[nodiscard]] std::string GetTestFileNameForCurrentVersion();
+
+[[nodiscard]] std::string GetTestFileNameForLatestDeprecatedVersion();
+
 }  // namespace storage
 
 #endif  // COMPONENTS_SERVICES_STORAGE_SHARED_STORAGE_SHARED_STORAGE_TEST_UTILS_H_
diff --git a/components/test/data/storage/shared_storage.v1.init_too_new.sql b/components/test/data/storage/shared_storage.init_too_new.sql
similarity index 93%
rename from components/test/data/storage/shared_storage.v1.init_too_new.sql
rename to components/test/data/storage/shared_storage.init_too_new.sql
index b714f500..bace8100 100644
--- a/components/test/data/storage/shared_storage.v1.init_too_new.sql
+++ b/components/test/data/storage/shared_storage.init_too_new.sql
@@ -1,11 +1,11 @@
--- components_unittests --gtest_filter=SharedStorageDatabaseTest.Version1_DestroyTooNew
+-- components_unittests --gtest_filter=SharedStorageDatabaseTest.DestroyTooNew
 --
 -- .dump of a version 1 Shared Storage database.
 -- intentionally set up to fail initialization with error SharedStorageDatabase::InitStatus::kTooNew
 BEGIN TRANSACTION;
 CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
 INSERT INTO "meta" VALUES('version','1');
-INSERT INTO "meta" VALUES('last_compatible_version','2');
+INSERT INTO "meta" VALUES('last_compatible_version','3');
 CREATE TABLE values_mapping(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
 INSERT INTO "values_mapping" VALUES ('http://google.com','key1','value1');
 INSERT INTO "values_mapping" VALUES ('http://google.com','key2','value2');
diff --git a/components/test/data/storage/shared_storage.v0.init_too_old.sql b/components/test/data/storage/shared_storage.v0.sql
similarity index 97%
rename from components/test/data/storage/shared_storage.v0.init_too_old.sql
rename to components/test/data/storage/shared_storage.v0.sql
index 2f62200..703cc41 100644
--- a/components/test/data/storage/shared_storage.v0.init_too_old.sql
+++ b/components/test/data/storage/shared_storage.v0.sql
@@ -1,4 +1,4 @@
--- components_unittests --gtest_filter=SharedStorageDatabaseTest.Version1_DestroyTooOld
+-- components_unittests --gtest_filter=SharedStorageDatabaseTest.DestroyTooOld
 --
 -- .dump of a version 1 Shared Storage database (although erroneously marked as version 0).
 -- intentionally set up to fail initialization with error SharedStorageDatabase::InitStatus::kTooOld
diff --git a/components/test/data/storage/shared_storage.v1.empty_values_mapping.5origins.sql b/components/test/data/storage/shared_storage.v1.empty_values_mapping.5origins.sql
deleted file mode 100644
index a4d49e977..0000000
--- a/components/test/data/storage/shared_storage.v1.empty_values_mapping.5origins.sql
+++ /dev/null
@@ -1,18 +0,0 @@
--- components_unittests --gtest_filter=SharedStorageDatabaseTest.FiveOrigins
---
--- .dump of a version 1 Shared Storage database.
-BEGIN TRANSACTION;
-CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-INSERT INTO "meta" VALUES('version','1');
-INSERT INTO "meta" VALUES('last_compatible_version','1');
-CREATE TABLE values_mapping(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
-CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,last_used_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
-INSERT INTO "per_origin_mapping" VALUES ('http://google.com',13266954476192362,20);
-INSERT INTO "per_origin_mapping" VALUES ('http://chromium.org',13268941676192362,40);
-INSERT INTO "per_origin_mapping" VALUES ('http://gv.com',13268941793856733,15);
-INSERT INTO "per_origin_mapping" VALUES ('http://abc.xyz',13269481776356965,250);
-INSERT INTO "per_origin_mapping" VALUES ('http://google.org',13269546476192362,10);
-CREATE TABLE budget_mapping(id INTEGER NOT NULL PRIMARY KEY,context_origin TEXT NOT NULL,time_stamp INTEGER NOT NULL,bits_debit REAL NOT NULL);
-CREATE INDEX IF NOT EXISTS per_origin_mapping_last_used_time_idx ON per_origin_mapping(last_used_time);
-CREATE INDEX IF NOT EXISTS budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
-COMMIT;
diff --git a/components/test/data/storage/shared_storage.v1.single_origin.sql b/components/test/data/storage/shared_storage.v1.single_origin.sql
deleted file mode 100644
index e13284ba..0000000
--- a/components/test/data/storage/shared_storage.v1.single_origin.sql
+++ /dev/null
@@ -1,24 +0,0 @@
--- components_unittests --gtest_filter=SharedStorageDatabaseTest.SingleOrigin
---
--- .dump of a version 1 Shared Storage database.
-BEGIN TRANSACTION;
-CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-INSERT INTO "meta" VALUES('version','1');
-INSERT INTO "meta" VALUES('last_compatible_version','1');
-CREATE TABLE values_mapping(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
-INSERT INTO "values_mapping" VALUES ('http://google.com','key1','value1');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key2','value2');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key3','value3');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key4','value4');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key5','value5');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key6','value6');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key7','value7');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key8','value8');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key9','value9');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key10','value10');
-CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,last_used_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
-INSERT INTO "per_origin_mapping" VALUES ('http://google.com',13266954476192362,10);
-CREATE TABLE budget_mapping(id INTEGER NOT NULL PRIMARY KEY,context_origin TEXT NOT NULL,time_stamp INTEGER NOT NULL,bits_debit REAL NOT NULL);
-CREATE INDEX IF NOT EXISTS per_origin_mapping_last_used_time_idx ON per_origin_mapping(last_used_time);
-CREATE INDEX IF NOT EXISTS budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
-COMMIT;
diff --git a/components/test/data/storage/shared_storage.v2.empty_values_mapping.5origins.sql b/components/test/data/storage/shared_storage.v2.empty_values_mapping.5origins.sql
new file mode 100644
index 0000000..cd422012
--- /dev/null
+++ b/components/test/data/storage/shared_storage.v2.empty_values_mapping.5origins.sql
@@ -0,0 +1,19 @@
+-- components_unittests --gtest_filter=SharedStorageDatabaseTest.FiveOrigins
+--
+-- .dump of a version 2 Shared Storage database.
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO "meta" VALUES('version','2');
+INSERT INTO "meta" VALUES('last_compatible_version','1');
+CREATE TABLE IF NOT EXISTS "values_mapping"(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,last_used_time INTEGER NOT NULL,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
+CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,creation_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
+INSERT INTO "per_origin_mapping" VALUES ('http://google.com',13266954476192362,20);
+INSERT INTO "per_origin_mapping" VALUES ('http://chromium.org',13268941676192362,40);
+INSERT INTO "per_origin_mapping" VALUES ('http://gv.com',13268941793856733,15);
+INSERT INTO "per_origin_mapping" VALUES ('http://abc.xyz',13269481776356965,250);
+INSERT INTO "per_origin_mapping" VALUES ('http://google.org',13269546476192362,10);
+CREATE TABLE budget_mapping(id INTEGER NOT NULL PRIMARY KEY,context_origin TEXT NOT NULL,time_stamp INTEGER NOT NULL,bits_debit REAL NOT NULL);
+CREATE INDEX budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX values_mapping_last_used_time_idx ON values_mapping(last_used_time);
+CREATE INDEX per_origin_mapping_creation_time_idx ON per_origin_mapping(creation_time);
+COMMIT;
diff --git a/components/test/data/storage/shared_storage.v1.empty_values_mapping.6origins.sql b/components/test/data/storage/shared_storage.v2.empty_values_mapping.6origins.sql
similarity index 60%
rename from components/test/data/storage/shared_storage.v1.empty_values_mapping.6origins.sql
rename to components/test/data/storage/shared_storage.v2.empty_values_mapping.6origins.sql
index f1f9bd0..ff7cf063 100644
--- a/components/test/data/storage/shared_storage.v1.empty_values_mapping.6origins.sql
+++ b/components/test/data/storage/shared_storage.v2.empty_values_mapping.6origins.sql
@@ -1,12 +1,12 @@
 -- components_unittests --gtest_filter=SharedStorageDatabaseTest.SixOrigins
 --
--- .dump of a version 1 Shared Storage database.
+-- .dump of a version 2 Shared Storage database.
 BEGIN TRANSACTION;
 CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-INSERT INTO "meta" VALUES('version','1');
+INSERT INTO "meta" VALUES('version','2');
 INSERT INTO "meta" VALUES('last_compatible_version','1');
-CREATE TABLE values_mapping(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
-CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,last_used_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
+CREATE TABLE IF NOT EXISTS "values_mapping"(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,last_used_time INTEGER NOT NULL,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
+CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,creation_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
 INSERT INTO "per_origin_mapping" VALUES ('http://google.com',13266954476192362,20);
 INSERT INTO "per_origin_mapping" VALUES ('http://chromium.org',13268941676192362,40);
 INSERT INTO "per_origin_mapping" VALUES ('http://gv.com',13268941793856733,15);
@@ -14,6 +14,7 @@
 INSERT INTO "per_origin_mapping" VALUES ('http://waymo.com',13269546064355176,1599);
 INSERT INTO "per_origin_mapping" VALUES ('http://google.org',13269546476192362,10);
 CREATE TABLE budget_mapping(id INTEGER NOT NULL PRIMARY KEY,context_origin TEXT NOT NULL,time_stamp INTEGER NOT NULL,bits_debit REAL NOT NULL);
-CREATE INDEX IF NOT EXISTS per_origin_mapping_last_used_time_idx ON per_origin_mapping(last_used_time);
-CREATE INDEX IF NOT EXISTS budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX values_mapping_last_used_time_idx ON values_mapping(last_used_time);
+CREATE INDEX per_origin_mapping_creation_time_idx ON per_origin_mapping(creation_time);
 COMMIT;
diff --git a/components/test/data/storage/shared_storage.v1.empty_values_mapping.7origins.sql b/components/test/data/storage/shared_storage.v2.empty_values_mapping.7origins.sql
similarity index 63%
rename from components/test/data/storage/shared_storage.v1.empty_values_mapping.7origins.sql
rename to components/test/data/storage/shared_storage.v2.empty_values_mapping.7origins.sql
index 4aa48d1..fc01cae4 100644
--- a/components/test/data/storage/shared_storage.v1.empty_values_mapping.7origins.sql
+++ b/components/test/data/storage/shared_storage.v2.empty_values_mapping.7origins.sql
@@ -1,12 +1,12 @@
 -- components_unittests --gtest_filter=SharedStorageDatabaseTest.SevenOrigins
 --
--- .dump of a version 1 Shared Storage database.
+-- .dump of a version 2 Shared Storage database.
 BEGIN TRANSACTION;
 CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-INSERT INTO "meta" VALUES('version','1');
+INSERT INTO "meta" VALUES('version','2');
 INSERT INTO "meta" VALUES('last_compatible_version','1');
-CREATE TABLE values_mapping(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
-CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,last_used_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
+CREATE TABLE IF NOT EXISTS "values_mapping"(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,last_used_time INTEGER NOT NULL,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
+CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,creation_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
 INSERT INTO "per_origin_mapping" VALUES ('http://google.com',13266954476192362,20);
 INSERT INTO "per_origin_mapping" VALUES ('http://chromium.org',13268941676192362,40);
 INSERT INTO "per_origin_mapping" VALUES ('http://gv.com',13268941793856733,15);
@@ -15,6 +15,7 @@
 INSERT INTO "per_origin_mapping" VALUES ('http://waymo.com',13269546064355176,1599);
 INSERT INTO "per_origin_mapping" VALUES ('http://google.org',13269546476192362,10);
 CREATE TABLE budget_mapping(id INTEGER NOT NULL PRIMARY KEY,context_origin TEXT NOT NULL,time_stamp INTEGER NOT NULL,bits_debit REAL NOT NULL);
-CREATE INDEX IF NOT EXISTS per_origin_mapping_last_used_time_idx ON per_origin_mapping(last_used_time);
-CREATE INDEX IF NOT EXISTS budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX values_mapping_last_used_time_idx ON values_mapping(last_used_time);
+CREATE INDEX per_origin_mapping_creation_time_idx ON per_origin_mapping(creation_time);
 COMMIT;
diff --git a/components/test/data/storage/shared_storage.v1.empty_values_mapping.8origins.sql b/components/test/data/storage/shared_storage.v2.empty_values_mapping.8origins.sql
similarity index 64%
rename from components/test/data/storage/shared_storage.v1.empty_values_mapping.8origins.sql
rename to components/test/data/storage/shared_storage.v2.empty_values_mapping.8origins.sql
index abcceb50..8d77159a 100644
--- a/components/test/data/storage/shared_storage.v1.empty_values_mapping.8origins.sql
+++ b/components/test/data/storage/shared_storage.v2.empty_values_mapping.8origins.sql
@@ -1,12 +1,12 @@
 -- components_unittests --gtest_filter=SharedStorageDatabaseTest.EightOrigins
 --
--- .dump of a version 1 Shared Storage database.
+-- .dump of a version 2 Shared Storage database.
 BEGIN TRANSACTION;
 CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-INSERT INTO "meta" VALUES('version','1');
+INSERT INTO "meta" VALUES('version','2');
 INSERT INTO "meta" VALUES('last_compatible_version','1');
-CREATE TABLE values_mapping(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
-CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,last_used_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
+CREATE TABLE IF NOT EXISTS "values_mapping"(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,last_used_time INTEGER NOT NULL,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
+CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,creation_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
 INSERT INTO "per_origin_mapping" VALUES ('http://google.com',13266954476192362,20);
 INSERT INTO "per_origin_mapping" VALUES ('http://youtube.com',13266954593856733,100);
 INSERT INTO "per_origin_mapping" VALUES ('http://chromium.org',13268941676192362,40);
@@ -16,6 +16,7 @@
 INSERT INTO "per_origin_mapping" VALUES ('http://waymo.com',13269546064355176,1599);
 INSERT INTO "per_origin_mapping" VALUES ('http://google.org',13269546476192362,10);
 CREATE TABLE budget_mapping(id INTEGER NOT NULL PRIMARY KEY,context_origin TEXT NOT NULL,time_stamp INTEGER NOT NULL,bits_debit REAL NOT NULL);
-CREATE INDEX IF NOT EXISTS per_origin_mapping_last_used_time_idx ON per_origin_mapping(last_used_time);
-CREATE INDEX IF NOT EXISTS budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX values_mapping_last_used_time_idx ON values_mapping(last_used_time);
+CREATE INDEX per_origin_mapping_creation_time_idx ON per_origin_mapping(creation_time);
 COMMIT;
diff --git a/components/test/data/storage/shared_storage.v1.iterator.sql b/components/test/data/storage/shared_storage.v2.iterator.sql
similarity index 65%
rename from components/test/data/storage/shared_storage.v1.iterator.sql
rename to components/test/data/storage/shared_storage.v2.iterator.sql
index 251c134..af4ab94 100644
--- a/components/test/data/storage/shared_storage.v1.iterator.sql
+++ b/components/test/data/storage/shared_storage.v2.iterator.sql
@@ -1,242 +1,245 @@
--- components_unittests --gtest_filter=SharedStorageDatabaseTest.Version1_LoadFromFile
+-- components_unittests --gtest_filter=SharedStorageDatabaseIteratorTest
 --
--- .dump of a version 1 Shared Storage database.
+-- .dump of a version 2 Shared Storage database.
+PRAGMA foreign_keys=OFF;
 BEGIN TRANSACTION;
 CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-INSERT INTO "meta" VALUES('version','1');
+INSERT INTO meta VALUES('mmap_status','-1');
+INSERT INTO "meta" VALUES('version','2');
 INSERT INTO "meta" VALUES('last_compatible_version','1');
-CREATE TABLE values_mapping(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
-INSERT INTO "values_mapping" VALUES ('http://google.com','key1','value1');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key2','value2');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key3','value3');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key4','value4');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key5','value5');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key6','value6');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key7','value7');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key8','value8');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key9','value9');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key10','value10');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key11','value11');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key12','value12');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key13','value13');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key14','value14');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key15','value15');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key16','value16');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key17','value17');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key18','value18');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key19','value19');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key20','value20');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key21','value21');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key22','value22');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key23','value23');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key24','value24');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key25','value25');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key26','value26');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key27','value27');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key28','value28');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key29','value29');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key30','value30');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key31','value31');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key32','value32');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key33','value33');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key34','value34');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key35','value35');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key36','value36');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key37','value37');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key38','value38');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key39','value39');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key40','value40');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key41','value41');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key42','value42');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key43','value43');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key44','value44');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key45','value45');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key46','value46');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key47','value47');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key48','value48');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key49','value49');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key50','value50');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key51','value51');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key52','value52');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key53','value53');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key54','value54');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key55','value55');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key56','value56');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key57','value57');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key58','value58');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key59','value59');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key60','value60');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key61','value61');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key62','value62');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key63','value63');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key64','value64');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key65','value65');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key66','value66');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key67','value67');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key68','value68');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key69','value69');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key70','value70');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key71','value71');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key72','value72');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key73','value73');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key74','value74');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key75','value75');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key76','value76');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key77','value77');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key78','value78');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key79','value79');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key80','value80');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key81','value81');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key82','value82');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key83','value83');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key84','value84');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key85','value85');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key86','value86');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key87','value87');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key88','value88');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key89','value89');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key90','value90');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key91','value91');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key92','value92');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key93','value93');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key94','value94');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key95','value95');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key96','value96');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key97','value97');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key98','value98');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key99','value99');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key100','value100');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key101','value101');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key102','value102');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key103','value103');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key104','value104');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key105','value105');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key106','value106');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key107','value107');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key108','value108');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key109','value109');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key110','value110');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key111','value111');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key112','value112');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key113','value113');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key114','value114');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key115','value115');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key116','value116');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key117','value117');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key118','value118');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key119','value119');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key120','value120');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key121','value121');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key122','value122');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key123','value123');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key124','value124');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key125','value125');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key126','value126');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key127','value127');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key128','value128');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key129','value129');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key130','value130');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key131','value131');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key132','value132');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key133','value133');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key134','value134');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key135','value135');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key136','value136');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key137','value137');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key138','value138');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key139','value139');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key140','value140');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key141','value141');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key142','value142');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key143','value143');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key144','value144');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key145','value145');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key146','value146');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key147','value147');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key148','value148');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key149','value149');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key150','value150');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key151','value151');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key152','value152');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key153','value153');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key154','value154');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key155','value155');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key156','value156');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key157','value157');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key158','value158');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key159','value159');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key160','value160');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key161','value161');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key162','value162');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key163','value163');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key164','value164');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key165','value165');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key166','value166');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key167','value167');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key168','value168');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key169','value169');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key170','value170');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key171','value171');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key172','value172');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key173','value173');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key174','value174');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key175','value175');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key176','value176');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key177','value177');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key178','value178');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key179','value179');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key180','value180');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key181','value181');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key182','value182');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key183','value183');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key184','value184');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key185','value185');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key186','value186');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key187','value187');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key188','value188');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key189','value189');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key190','value190');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key191','value191');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key192','value192');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key193','value193');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key194','value194');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key195','value195');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key196','value196');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key197','value197');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key198','value198');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key199','value199');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key200','value200');
-INSERT INTO "values_mapping" VALUES ('http://google.com','key201','value201');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','A','valueA');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','B','valueB');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','C','valueC');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','D','valueD');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','E','valueE');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','F','valueF');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','G','valueG');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','H','valueH');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','I','valueI');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','J','valueJ');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','K','valueK');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','L','valueL');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','M','valueM');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','N','valueN');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','O','valueO');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','P','valueP');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','Q','valueQ');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','R','valueR');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','S','valueS');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','T','valueT');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','U','valueU');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','V','valueV');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','W','valueW');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','X','valueX');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','Y','valueY');
-INSERT INTO "values_mapping" VALUES ('http://chromium.org','Z','valueZ');
-CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,last_used_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
+CREATE TABLE IF NOT EXISTS "values_mapping"(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,last_used_time INTEGER NOT NULL,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
+INSERT INTO "values_mapping" VALUES ('http://google.com','key1','value1',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key2','value2',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key3','value3',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key4','value4',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key5','value5',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key6','value6',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key7','value7',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key8','value8',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key9','value9',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key10','value10',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key11','value11',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key12','value12',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key13','value13',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key14','value14',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key15','value15',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key16','value16',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key17','value17',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key18','value18',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key19','value19',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key20','value20',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key21','value21',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key22','value22',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key23','value23',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key24','value24',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key25','value25',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key26','value26',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key27','value27',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key28','value28',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key29','value29',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key30','value30',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key31','value31',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key32','value32',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key33','value33',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key34','value34',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key35','value35',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key36','value36',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key37','value37',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key38','value38',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key39','value39',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key40','value40',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key41','value41',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key42','value42',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key43','value43',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key44','value44',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key45','value45',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key46','value46',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key47','value47',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key48','value48',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key49','value49',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key50','value50',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key51','value51',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key52','value52',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key53','value53',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key54','value54',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key55','value55',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key56','value56',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key57','value57',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key58','value58',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key59','value59',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key60','value60',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key61','value61',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key62','value62',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key63','value63',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key64','value64',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key65','value65',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key66','value66',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key67','value67',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key68','value68',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key69','value69',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key70','value70',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key71','value71',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key72','value72',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key73','value73',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key74','value74',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key75','value75',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key76','value76',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key77','value77',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key78','value78',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key79','value79',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key80','value80',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key81','value81',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key82','value82',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key83','value83',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key84','value84',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key85','value85',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key86','value86',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key87','value87',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key88','value88',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key89','value89',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key90','value90',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key91','value91',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key92','value92',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key93','value93',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key94','value94',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key95','value95',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key96','value96',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key97','value97',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key98','value98',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key99','value99',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key100','value100',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key101','value101',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key102','value102',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key103','value103',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key104','value104',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key105','value105',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key106','value106',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key107','value107',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key108','value108',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key109','value109',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key110','value110',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key111','value111',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key112','value112',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key113','value113',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key114','value114',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key115','value115',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key116','value116',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key117','value117',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key118','value118',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key119','value119',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key120','value120',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key121','value121',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key122','value122',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key123','value123',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key124','value124',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key125','value125',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key126','value126',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key127','value127',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key128','value128',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key129','value129',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key130','value130',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key131','value131',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key132','value132',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key133','value133',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key134','value134',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key135','value135',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key136','value136',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key137','value137',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key138','value138',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key139','value139',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key140','value140',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key141','value141',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key142','value142',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key143','value143',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key144','value144',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key145','value145',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key146','value146',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key147','value147',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key148','value148',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key149','value149',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key150','value150',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key151','value151',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key152','value152',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key153','value153',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key154','value154',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key155','value155',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key156','value156',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key157','value157',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key158','value158',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key159','value159',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key160','value160',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key161','value161',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key162','value162',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key163','value163',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key164','value164',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key165','value165',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key166','value166',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key167','value167',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key168','value168',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key169','value169',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key170','value170',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key171','value171',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key172','value172',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key173','value173',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key174','value174',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key175','value175',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key176','value176',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key177','value177',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key178','value178',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key179','value179',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key180','value180',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key181','value181',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key182','value182',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key183','value183',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key184','value184',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key185','value185',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key186','value186',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key187','value187',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key188','value188',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key189','value189',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key190','value190',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key191','value191',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key192','value192',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key193','value193',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key194','value194',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key195','value195',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key196','value196',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key197','value197',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key198','value198',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key199','value199',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key200','value200',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key201','value201',13266954476192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','A','valueA',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','B','valueB',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','C','valueC',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','D','valueD',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','E','valueE',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','F','valueF',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','G','valueG',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','H','valueH',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','I','valueI',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','J','valueJ',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','K','valueK',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','L','valueL',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','M','valueM',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','N','valueN',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','O','valueO',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','P','valueP',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','Q','valueQ',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','R','valueR',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','S','valueS',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','T','valueT',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','U','valueU',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','V','valueV',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','W','valueW',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','X','valueX',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','Y','valueY',13268941676192362);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','Z','valueZ',13268941676192362);
+CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,creation_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
 INSERT INTO "per_origin_mapping" VALUES ('http://google.com',13266954476192362,201);
 INSERT INTO "per_origin_mapping" VALUES ('http://chromium.org',13268941676192362,26);
 CREATE TABLE budget_mapping(id INTEGER NOT NULL PRIMARY KEY,context_origin TEXT NOT NULL,time_stamp INTEGER NOT NULL,bits_debit REAL NOT NULL);
-CREATE INDEX IF NOT EXISTS per_origin_mapping_last_used_time_idx ON per_origin_mapping(last_used_time);
-CREATE INDEX IF NOT EXISTS budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX values_mapping_last_used_time_idx ON values_mapping(last_used_time);
+CREATE INDEX per_origin_mapping_creation_time_idx ON per_origin_mapping(creation_time);
 COMMIT;
diff --git a/components/test/data/storage/shared_storage.v2.single_origin.sql b/components/test/data/storage/shared_storage.v2.single_origin.sql
new file mode 100644
index 0000000..9f658a6
--- /dev/null
+++ b/components/test/data/storage/shared_storage.v2.single_origin.sql
@@ -0,0 +1,25 @@
+-- components_unittests --gtest_filter=SharedStorageDatabaseTest.SingleOrigin
+--
+-- .dump of a version 2 Shared Storage database.
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO "meta" VALUES('version','2');
+INSERT INTO "meta" VALUES('last_compatible_version','1');
+CREATE TABLE IF NOT EXISTS "values_mapping"(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,last_used_time INTEGER NOT NULL,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
+INSERT INTO "values_mapping" VALUES ('http://google.com','key1','value1',13312097333991364);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key2','value2',13313037427966159);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key3','value3',13313037435619704);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key4','value4',13313037416916308);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key5','value5',13313037416916308);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key6','value6',13312097333991364);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key7','value7',13312353831182651);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key8','value8',13313037487092131);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key9','value9',13269481776356965);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key10','value10',13269481776356965);
+CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,creation_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
+INSERT INTO "per_origin_mapping" VALUES ('http://google.com',13266954476192362,10);
+CREATE TABLE budget_mapping(id INTEGER NOT NULL PRIMARY KEY,context_origin TEXT NOT NULL,time_stamp INTEGER NOT NULL,bits_debit REAL NOT NULL);
+CREATE INDEX budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX values_mapping_last_used_time_idx ON values_mapping(last_used_time);
+CREATE INDEX per_origin_mapping_creation_time_idx ON per_origin_mapping(creation_time);
+COMMIT;
diff --git a/components/test/data/storage/shared_storage.v2.sql b/components/test/data/storage/shared_storage.v2.sql
new file mode 100644
index 0000000..04242a6
--- /dev/null
+++ b/components/test/data/storage/shared_storage.v2.sql
@@ -0,0 +1,54 @@
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO meta VALUES('mmap_status','-1');
+INSERT INTO meta VALUES('last_compatible_version','1');
+INSERT INTO meta VALUES('version','2');
+CREATE TABLE per_origin_mapping(context_origin TEXT NOT NULL PRIMARY KEY,creation_time INTEGER NOT NULL,length INTEGER NOT NULL) WITHOUT ROWID;
+INSERT INTO "per_origin_mapping" VALUES ('http://google.com',13266954476192362,2);
+INSERT INTO "per_origin_mapping" VALUES ('http://youtube.com',13266954593856733,1);
+INSERT INTO "per_origin_mapping" VALUES ('http://chromium.org',13268941676192362,3);
+INSERT INTO "per_origin_mapping" VALUES ('http://gv.com',13268941793856733,1);
+INSERT INTO "per_origin_mapping" VALUES ('http://abc.xyz',13269481776356965,2);
+INSERT INTO "per_origin_mapping" VALUES ('http://withgoogle.com',13269545986263676,1);
+INSERT INTO "per_origin_mapping" VALUES ('http://waymo.com',13269546064355176,1);
+INSERT INTO "per_origin_mapping" VALUES ('http://google.org',13269546476192362,4);
+INSERT INTO "per_origin_mapping" VALUES ('http://growwithgoogle.com',13269546593856733,3);
+CREATE TABLE budget_mapping(id INTEGER NOT NULL PRIMARY KEY,context_origin TEXT NOT NULL,time_stamp INTEGER NOT NULL,bits_debit REAL NOT NULL);
+INSERT INTO "budget_mapping" VALUES (1,'http://google.com',13266954476192332,1.0);
+INSERT INTO "budget_mapping" VALUES (2,'http://google.com',13266954476192344,2.0);
+INSERT INTO "budget_mapping" VALUES (3,'http://google.com',13266954476192362,1.1);
+INSERT INTO "budget_mapping" VALUES (4,'http://youtube.com',13266954593856693,1.3);
+INSERT INTO "budget_mapping" VALUES (5,'http://chromium.org',13268941676192362,1.5);
+INSERT INTO "budget_mapping" VALUES (6,'http://youtube.com',13266954593856733,2.3);
+INSERT INTO "budget_mapping" VALUES (7,'http://gv.com',13268941793856733,3.7);
+INSERT INTO "budget_mapping" VALUES (8,'http://abc.xyz',13269481776356845,2.2);
+INSERT INTO "budget_mapping" VALUES (9,'http://withgoogle.com',13269545986263676,1);
+INSERT INTO "budget_mapping" VALUES (10,'http://abc.xyz',13269481776356905,1.1);
+INSERT INTO "budget_mapping" VALUES (11,'http://waymo.com',13269546064355176,4.2);
+INSERT INTO "budget_mapping" VALUES (12,'http://abc.xyz',13269481776356965,2.0);
+INSERT INTO "budget_mapping" VALUES (13,'http://google.org',13269546476192362,4);
+INSERT INTO "budget_mapping" VALUES (14,'http://growwithgoogle.com',13269546593856733,1.2);
+CREATE TABLE IF NOT EXISTS "values_mapping"(context_origin TEXT NOT NULL,key TEXT NOT NULL,value TEXT,last_used_time INTEGER NOT NULL,PRIMARY KEY(context_origin,key)) WITHOUT ROWID;
+INSERT INTO "values_mapping" VALUES ('http://google.com','key1','value1',13312097333991364);
+INSERT INTO "values_mapping" VALUES ('http://google.com','key2','value2',13313037427966159);
+INSERT INTO "values_mapping" VALUES ('http://youtube.com','visited','1111111',13313037435619704);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','a','',13313037416916308);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','b','hello',13312097333991364);
+INSERT INTO "values_mapping" VALUES ('http://chromium.org','c','goodbye',13312353831182651);
+INSERT INTO "values_mapping" VALUES ('http://gv.com','cookie','13268941793856733',13313037487092131);
+INSERT INTO "values_mapping" VALUES ('http://abc.xyz','seed','387562094',13269481776356965);
+INSERT INTO "values_mapping" VALUES ('http://abc.xyz','bucket','1276',13269481776356965);
+INSERT INTO "values_mapping" VALUES ('http://withgoogle.com','count','389',13269545986263676);
+INSERT INTO "values_mapping" VALUES ('http://waymo.com','key','value',13269546064355176);
+INSERT INTO "values_mapping" VALUES ('http://google.org','1','ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',13269546476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.org','2',';',13269546476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.org','#','[]',13269546476192362);
+INSERT INTO "values_mapping" VALUES ('http://google.org','ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff','k',13269546476192362);
+INSERT INTO "values_mapping" VALUES ('http://growwithgoogle.com','_','down',13269546593856733);
+INSERT INTO "values_mapping" VALUES ('http://growwithgoogle.com','<','left',13269546593856733);
+INSERT INTO "values_mapping" VALUES ('http://growwithgoogle.com','>','right',13269546593856733);
+CREATE INDEX budget_mapping_origin_time_stamp_idx ON budget_mapping(context_origin,time_stamp);
+CREATE INDEX values_mapping_last_used_time_idx ON values_mapping(last_used_time);
+CREATE INDEX per_origin_mapping_creation_time_idx ON per_origin_mapping(creation_time);
+COMMIT;
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc
index cc4907dc..2644b3e 100644
--- a/components/variations/service/variations_service.cc
+++ b/components/variations/service/variations_service.cc
@@ -196,8 +196,8 @@
   *is_gzip_compressed = gzip_im != ims.end();
 
   // The IM field should not have anything but x-bm and gzip.
-  size_t im_count = (*is_delta_compressed ? 1 : 0) +
-      (*is_gzip_compressed ? 1 : 0);
+  size_t im_count =
+      (*is_delta_compressed ? 1 : 0) + (*is_gzip_compressed ? 1 : 0);
   if (im_count != ims.size()) {
     DVLOG(1) << "Unrecognized instance manipulations in "
              << base::JoinString(ims, ",")
@@ -924,12 +924,12 @@
   safe_seed_manager_.RecordSuccessfulFetch(field_trial_creator_.seed_store());
 }
 
-void VariationsService::GetClientFilterableStateForVersionCalledForTesting() {
+std::unique_ptr<ClientFilterableState>
+VariationsService::GetClientFilterableStateForVersion() {
   const base::Version current_version(version_info::GetVersionNumber());
-  if (!current_version.IsValid())
-    return;
-
-  field_trial_creator_.GetClientFilterableStateForVersion(current_version);
+  DCHECK(current_version.IsValid());
+  return field_trial_creator_.GetClientFilterableStateForVersion(
+      current_version);
 }
 
 std::string VariationsService::GetLatestCountry() const {
diff --git a/components/variations/service/variations_service.h b/components/variations/service/variations_service.h
index 4da742d33..b90a1f0b 100644
--- a/components/variations/service/variations_service.h
+++ b/components/variations/service/variations_service.h
@@ -35,7 +35,7 @@
 namespace base {
 class FeatureList;
 class Version;
-}
+}  // namespace base
 
 namespace metrics {
 class MetricsStateManager;
@@ -178,8 +178,9 @@
     policy_pref_service_ = service;
   }
 
-  // Exposed for testing.
-  void GetClientFilterableStateForVersionCalledForTesting();
+  // Returns the ClientFilterableState, i.e., the state used to do trial
+  // filtering. Should only be used for testing and debugging purposes.
+  std::unique_ptr<ClientFilterableState> GetClientFilterableStateForVersion();
 
   web_resource::ResourceRequestAllowedNotifier*
   GetResourceRequestAllowedNotifierForTesting() {
diff --git a/components/variations/service/variations_service_unittest.cc b/components/variations/service/variations_service_unittest.cc
index 01a01455..73f40ea 100644
--- a/components/variations/service/variations_service_unittest.cc
+++ b/components/variations/service/variations_service_unittest.cc
@@ -149,9 +149,7 @@
   ~TestVariationsService() override {}
 
   GURL interception_url() { return interception_url_; }
-  void set_intercepts_fetch(bool value) {
-    intercepts_fetch_ = value;
-  }
+  void set_intercepts_fetch(bool value) { intercepts_fetch_ = value; }
   void set_insecure_url(const GURL& url) {
     set_insecure_variations_server_url(url);
   }
@@ -257,9 +255,7 @@
     return best_effort_changes_notified_;
   }
 
-  int crticial_changes_notified() const {
-    return crticial_changes_notified_;
-  }
+  int crticial_changes_notified() const { return crticial_changes_notified_; }
 
  private:
   // Number of notification received with BEST_EFFORT severity.
@@ -602,7 +598,7 @@
 }
 
 TEST_F(VariationsServiceTest, InstanceManipulations) {
-  struct  {
+  struct {
     std::string im;
     bool delta_compressed;
     bool gzip_compressed;
@@ -678,17 +674,9 @@
     int expected_best_effort_notifications;
     int expected_crtical_notifications;
   } cases[] = {
-      {0, 0, 0, 0, 0},
-      {1, 0, 0, 0, 0},
-      {10, 0, 0, 0, 0},
-      {0, 1, 0, 1, 0},
-      {0, 10, 0, 1, 0},
-      {0, 0, 1, 0, 1},
-      {0, 0, 10, 0, 1},
-      {0, 1, 1, 0, 1},
-      {1, 1, 1, 0, 1},
-      {1, 1, 0, 1, 0},
-      {1, 0, 1, 0, 1},
+      {0, 0, 0, 0, 0},  {1, 0, 0, 0, 0}, {10, 0, 0, 0, 0}, {0, 1, 0, 1, 0},
+      {0, 10, 0, 1, 0}, {0, 0, 1, 0, 1}, {0, 0, 10, 0, 1}, {0, 1, 1, 0, 1},
+      {1, 1, 1, 0, 1},  {1, 1, 0, 1, 0}, {1, 0, 1, 0, 1},
   };
 
   for (size_t i = 0; i < std::size(cases); ++i) {
@@ -702,9 +690,11 @@
     service.NotifyObservers(result);
 
     EXPECT_EQ(cases[i].expected_best_effort_notifications,
-              observer.best_effort_changes_notified()) << i;
+              observer.best_effort_changes_notified())
+        << i;
     EXPECT_EQ(cases[i].expected_crtical_notifications,
-              observer.crticial_changes_notified()) << i;
+              observer.crticial_changes_notified())
+        << i;
 
     service.RemoveObserver(&observer);
   }
@@ -1005,7 +995,7 @@
 
   // Call will crash in service's VariationsFieldTrialCreator if not initialized
   // correctly.
-  service.GetClientFilterableStateForVersionCalledForTesting();
+  service.GetClientFilterableStateForVersion();
 }
 
 TEST_F(VariationsServiceTest, RetryOverHTTPIfURLisSet) {
@@ -1108,8 +1098,7 @@
                                       net::ERR_FAILED, 1);
 }
 
-TEST_F(VariationsServiceTest,
-       VariationsServiceStartsRequestOnNetworkChange) {
+TEST_F(VariationsServiceTest, VariationsServiceStartsRequestOnNetworkChange) {
   // Verifies VariationsService does a request when network status changes from
   // none to connected. This is a regression test for https://crbug.com/826930.
   VariationsService::EnableFetchForTesting();
diff --git a/components/viz/common/resources/resource_format_utils.cc b/components/viz/common/resources/resource_format_utils.cc
index cd96ec25..43a631c 100644
--- a/components/viz/common/resources/resource_format_utils.cc
+++ b/components/viz/common/resources/resource_format_utils.cc
@@ -601,9 +601,9 @@
   return static_cast<WGPUTextureFormat>(ToDawnFormat(format));
 }
 
-SkColorType ResourceFormatToClosestSkColorType(bool gpu_compositing,
-                                               SharedImageFormat format,
-                                               int plane_index) {
+SkColorType ToClosestSkColorType(bool gpu_compositing,
+                                 SharedImageFormat format,
+                                 int plane_index) {
   DCHECK(format.IsValidPlaneIndex(plane_index));
   if (!gpu_compositing) {
     // TODO(crbug.com/986405): Remove this assumption and have clients tag
diff --git a/components/viz/common/resources/resource_format_utils.h b/components/viz/common/resources/resource_format_utils.h
index cfe67e0b..7124a13f 100644
--- a/components/viz/common/resources/resource_format_utils.h
+++ b/components/viz/common/resources/resource_format_utils.h
@@ -87,9 +87,9 @@
 // default to 0; in such cases the corresponding function with ResourceFormat is
 // called. For multiplanar formats a plane_index is required.
 VIZ_RESOURCE_FORMAT_EXPORT SkColorType
-ResourceFormatToClosestSkColorType(bool gpu_compositing,
-                                   SharedImageFormat format,
-                                   int plane_index = 0);
+ToClosestSkColorType(bool gpu_compositing,
+                     SharedImageFormat format,
+                     int plane_index = 0);
 
 }  // namespace viz
 
diff --git a/components/viz/common/resources/resource_format_utils_unittest.cc b/components/viz/common/resources/resource_format_utils_unittest.cc
index c44ede6..82f45605 100644
--- a/components/viz/common/resources/resource_format_utils_unittest.cc
+++ b/components/viz/common/resources/resource_format_utils_unittest.cc
@@ -19,8 +19,7 @@
     for (int plane_index = 0; plane_index < format.NumberOfPlanes();
          plane_index++) {
       EXPECT_EQ(expected_types[plane_index],
-                ResourceFormatToClosestSkColorType(gpu_compositing, format,
-                                                   plane_index));
+                ToClosestSkColorType(gpu_compositing, format, plane_index));
     }
   }
 };
diff --git a/components/viz/common/resources/shared_image_format.cc b/components/viz/common/resources/shared_image_format.cc
index fc541e1..87da1877 100644
--- a/components/viz/common/resources/shared_image_format.cc
+++ b/components/viz/common/resources/shared_image_format.cc
@@ -127,6 +127,9 @@
 gfx::Size SharedImageFormat::GetPlaneSize(int plane_index,
                                           const gfx::Size& size) const {
   DCHECK(IsValidPlaneIndex(plane_index));
+  if (is_single_plane())
+    return size;
+
   switch (plane_config()) {
     case PlaneConfig::kY_V_U:
       if (plane_index == 0) {
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index cc67b30..bddb42c6 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -410,8 +410,8 @@
   if (image_context->has_image())
     return;
 
-  SkColorType color_type = ResourceFormatToClosestSkColorType(
-      true /* gpu_compositing */, image_context->format());
+  SkColorType color_type =
+      ToClosestSkColorType(true /* gpu_compositing */, image_context->format());
   GrBackendFormat backend_format = GetGrBackendFormatForTexture(
       image_context->format().resource_format(),
       image_context->mailbox_holder().texture_target,
diff --git a/components/viz/test/fake_skia_output_surface.cc b/components/viz/test/fake_skia_output_surface.cc
index 770d54e..b876e2d 100644
--- a/components/viz/test/fake_skia_output_surface.cc
+++ b/components/viz/test/fake_skia_output_surface.cc
@@ -123,8 +123,8 @@
     return;
   }
 
-  auto sk_color_type = ResourceFormatToClosestSkColorType(
-      true /* gpu_compositing */, image_context->format());
+  auto sk_color_type =
+      ToClosestSkColorType(true /* gpu_compositing */, image_context->format());
   image_context->SetImage(
       SkImage::MakeFromTexture(gr_context(), backend_texture,
                                kTopLeft_GrSurfaceOrigin, sk_color_type,
diff --git a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
index e7a33e2..6923f1ad 100644
--- a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
+++ b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
@@ -101,7 +101,7 @@
     // notification triggered by the hide.
     AccessibilityNotificationWaiter waiter(shell()->web_contents(),
                                            ui::kAXModeComplete,
-                                           ax::mojom::Event::kChildrenChanged);
+                                           ax::mojom::Event::kLayoutComplete);
     ASSERT_TRUE(ExecJs(
         shell(), "document.getElementById('p1').style.display = 'none';"));
     ASSERT_TRUE(waiter.WaitForNotification());
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index f0e9ff38..9c1afdc 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -1071,9 +1071,9 @@
   document_checker.CheckAccessible(GetRendererAccessible());
 
   // Change the children of the document body.
-  AccessibilityNotificationWaiter waiter(shell()->web_contents(),
-                                         ui::kAXModeComplete,
-                                         ax::mojom::Event::kChildrenChanged);
+  AccessibilityNotificationWaiter waiter(
+      shell()->web_contents(), ui::kAXModeComplete,
+      ui::AXEventGenerator::Event::CHILDREN_CHANGED);
   ExecuteScript(u"document.body.innerHTML='<b>new text</b>'");
   ASSERT_TRUE(waiter.WaitForNotification());
 
@@ -1096,9 +1096,9 @@
   document_checker.CheckAccessible(GetRendererAccessible());
 
   // Change the children of the document body.
-  AccessibilityNotificationWaiter waiter(shell()->web_contents(),
-                                         ui::kAXModeComplete,
-                                         ax::mojom::Event::kChildrenChanged);
+  AccessibilityNotificationWaiter waiter(
+      shell()->web_contents(), ui::kAXModeComplete,
+      ui::AXEventGenerator::Event::CHILDREN_CHANGED);
   ExecuteScript(u"document.body.children[0].style.visibility='visible'");
   ASSERT_TRUE(waiter.WaitForNotification());
 
@@ -2093,9 +2093,9 @@
   }
 
   // Delete the character in the input field.
-  AccessibilityNotificationWaiter waiter(shell()->web_contents(),
-                                         ui::kAXModeComplete,
-                                         ax::mojom::Event::kChildrenChanged);
+  AccessibilityNotificationWaiter waiter(
+      shell()->web_contents(), ui::kAXModeComplete,
+      ui::AXEventGenerator::Event::CHILDREN_CHANGED);
   ExecuteScript(u"document.querySelector('[contenteditable]').innerText='';");
   ASSERT_TRUE(waiter.WaitForNotification());
 
@@ -2568,9 +2568,9 @@
 
   base::win::ScopedVariant childid_self(CHILDID_SELF);
   base::win::ScopedBstr new_value(L"New value");
-  AccessibilityNotificationWaiter waiter(shell()->web_contents(),
-                                         ui::kAXModeComplete,
-                                         ax::mojom::Event::kChildrenChanged);
+  AccessibilityNotificationWaiter waiter(
+      shell()->web_contents(), ui::kAXModeComplete,
+      ui::AXEventGenerator::Event::CHILDREN_CHANGED);
   EXPECT_HRESULT_SUCCEEDED(
       paragraph->put_accValue(childid_self, new_value.Get()));
   ASSERT_TRUE(waiter.WaitForNotification());
diff --git a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
index dff48b3..bb65567 100644
--- a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
+++ b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
@@ -1085,9 +1085,9 @@
   // Now remove "Node 1" from the DOM and verify the text range created from
   // "Node 1" is still functional.
   {
-    AccessibilityNotificationWaiter waiter(shell()->web_contents(),
-                                           ui::kAXModeComplete,
-                                           ax::mojom::Event::kChildrenChanged);
+    AccessibilityNotificationWaiter waiter(
+        shell()->web_contents(), ui::kAXModeComplete,
+        ui::AXEventGenerator::Event::CHILDREN_CHANGED);
     EXPECT_TRUE(
         ExecJs(shell()->web_contents(),
                "document.getElementById('wrapper').removeChild(document."
@@ -1104,9 +1104,9 @@
   // Now remove all children from the DOM and verify the text range created from
   // "Node 1" is still valid (it got moved to a non-deleted ancestor node).
   {
-    AccessibilityNotificationWaiter waiter(shell()->web_contents(),
-                                           ui::kAXModeComplete,
-                                           ax::mojom::Event::kChildrenChanged);
+    AccessibilityNotificationWaiter waiter(
+        shell()->web_contents(), ui::kAXModeComplete,
+        ui::AXEventGenerator::Event::CHILDREN_CHANGED);
     EXPECT_TRUE(ExecJs(shell()->web_contents(),
                        "while(document.body.childElementCount > 0) {"
                        "  document.body.removeChild(document.body.firstChild);"
@@ -3296,7 +3296,7 @@
     EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"Before frame");
 
     AccessibilityNotificationWaiter waiter(web_contents, ui::kAXModeComplete,
-                                           ax::mojom::Event::kChildrenChanged);
+                                           ax::mojom::Event::kLayoutComplete);
 
     // Updating the style on that particular node is going to invalidate the
     // leaf text node and will replace it with a new one with the updated style.
@@ -3324,7 +3324,7 @@
         /*expected_count*/ 1);
 
     AccessibilityNotificationWaiter waiter(web_contents, ui::kAXModeComplete,
-                                           ax::mojom::Event::kChildrenChanged);
+                                           ax::mojom::Event::kLayoutComplete);
 
     // Updating the style on that particular node is going to invalidate the
     // leaf text node and will replace it with a new one with the updated style.
@@ -3356,7 +3356,7 @@
         /*expected_count*/ -1);
 
     AccessibilityNotificationWaiter waiter(web_contents, ui::kAXModeComplete,
-                                           ax::mojom::Event::kChildrenChanged);
+                                           ax::mojom::Event::kEndOfTest);
 
     // Updating the style on that particular node is going to invalidate the
     // leaf text node and will replace it with a new one with the updated style.
@@ -3365,6 +3365,7 @@
         web_contents,
         "document.getElementById('s2').style.outline = '1px solid black';"));
 
+    GetManager()->SignalEndOfTest();
     ASSERT_TRUE(waiter.WaitForNotification());
     EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"iframe\nAfter frame");
   }
@@ -3394,8 +3395,9 @@
     // Reloading changes the tree id, triggering an AXTreeManager replacement.
     shell()->Reload();
 
-    AccessibilityNotificationWaiter waiter(web_contents, ui::kAXModeComplete,
-                                           ax::mojom::Event::kChildrenChanged);
+    AccessibilityNotificationWaiter waiter(
+        web_contents, ui::kAXModeComplete,
+        ui::AXEventGenerator::Event::FOCUS_CHANGED);
 
     // We do a style change here only to trigger an AXTree update - apparently,
     // a shell reload doesn't update the tree by itself.
@@ -3433,13 +3435,14 @@
         /*expected_count*/ 1);
 
     AccessibilityNotificationWaiter waiter(web_contents, ui::kAXModeComplete,
-                                           ax::mojom::Event::kChildrenChanged);
+                                           ax::mojom::Event::kEndOfTest);
 
     // We do a style change here only to trigger an AXTree update.
     EXPECT_TRUE(ExecJs(
         web_contents,
         "document.getElementById('s2').style.outline = '1px solid black';"));
 
+    GetManager()->SignalEndOfTest();
     ASSERT_TRUE(waiter.WaitForNotification());
 
     // If the previous observer was not removed correctly, this will cause a
diff --git a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
index 07af6bd..98cca66 100644
--- a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
+++ b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
@@ -776,13 +776,16 @@
     // If there is a popup, expand it and wait for it to appear.
     // If it's a list, it will simply click on the list.
     {
-      AccessibilityNotificationWaiter waiter(
-          shell()->web_contents(), ui::kAXModeComplete,
-          ax::mojom::Event::kChildrenChanged);
+      // Note: the kEndOfTextSignal actually represents the next step in the
+      // test, when a response is received from the SignalEndOfTest() call.
+      AccessibilityNotificationWaiter waiter(shell()->web_contents(),
+                                             ui::kAXModeComplete,
+                                             ax::mojom::Event::kEndOfTest);
 
       ui::AXActionData action_data;
       action_data.action = ax::mojom::Action::kDoDefault;
       select->AccessibilityPerformAction(action_data);
+      GetManager()->SignalEndOfTest();
       ASSERT_TRUE(waiter.WaitForNotification());
     }
 
@@ -859,10 +862,10 @@
   }
 
   // Open popup.
-  AccessibilityNotificationWaiter waiter(shell()->web_contents(),
-                                         ui::kAXModeComplete,
-                                         ax::mojom::Event::kChildrenChanged);
   {
+    AccessibilityNotificationWaiter waiter(
+        shell()->web_contents(), ui::kAXModeComplete,
+        ui::AXEventGenerator::Event::EXPANDED);
     ui::AXActionData action_data;
     action_data.action = ax::mojom::Action::kDoDefault;
     select->AccessibilityPerformAction(action_data);
@@ -901,6 +904,9 @@
 
   // Close the popup.
   {
+    AccessibilityNotificationWaiter waiter(
+        shell()->web_contents(), ui::kAXModeComplete,
+        ui::AXEventGenerator::Event::COLLAPSED);
     ui::AXActionData action_data;
     action_data.action = ax::mojom::Action::kDoDefault;
     select->AccessibilityPerformAction(action_data);
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 0ecaeb1..e4ae627 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -2694,8 +2694,9 @@
 }
 */
 
+// TODO(crbug.com/1367886): This test is flaky.
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTestWithIgnoredNodes,
-                       AccessibilityModalDialogAndIframes) {
+                       DISABLED_AccessibilityModalDialogAndIframes) {
   RunHtmlTest(FILE_PATH_LITERAL("modal-dialog-and-iframes.html"));
 }
 
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 1122c02c..5a252d8c7 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -51,6 +51,7 @@
 #include "content/browser/renderer_host/file_utilities_host_impl.h"
 #include "content/browser/renderer_host/media/media_devices_dispatcher_host.h"
 #include "content/browser/renderer_host/media/media_stream_dispatcher_host.h"
+#include "content/browser/renderer_host/media/media_stream_manager.h"
 #include "content/browser/renderer_host/media/video_capture_host.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index ee95253..a5cbb9b 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -513,13 +513,11 @@
       // physical backing and screen info updates. So only process the pending
       // state once.
       pending_screen_state_.on_sync_display_properties_changed_received = true;
-      if (ScreenState::IsRotation(
-              pending_screen_state_.screen_info_size,
-              current_screen_state_.physical_backing_size)) {
-        start_rotation = true;
-      } else {
-        sync_needed = true;
-      }
+      // ScreenInfo explicitly lists an orientation, we always start a rotation
+      // when requested. It is possible in split-screen for
+      // `physical_backing_size` to become in a mixed orientation states, so we
+      // do not compare to them.
+      start_rotation = true;
     }
   }
 
@@ -3009,7 +3007,7 @@
   // Whether evicted or not, we stop batching for rotation in order to get
   // content ready for the new orientation.
   bool rotation_override = in_rotation_;
-  base::AutoReset<bool> in_rotation(&in_rotation_, false);
+  in_rotation_ = false;
 
   view_.GetLayer()->SetHideLayerAndSubtree(false);
 
@@ -3091,6 +3089,11 @@
     EndRotationBatching();
     rotation_metrics_.begin()->first = base::TimeTicks::Now();
     BeginRotationEmbed();
+  } else if (!rotation_metrics_.empty()) {
+    // If we have enqueued `rotation_metrics` but are not completing a rotation,
+    // then a timeout fired while we were hidden. As no synchronizing has
+    // previously occurred, set now to be the start of the rotation time.
+    rotation_metrics_.begin()->first = base::TimeTicks::Now();
   }
 
   // TODO(crbug.com/1385146): Ideally we would do no synchronizing at all when
@@ -3300,13 +3303,21 @@
 }
 
 void RenderWidgetHostViewAndroid::EndRotationAndSyncIfNecessary() {
-  if (!in_rotation_ || !is_showing_)
+  if (!in_rotation_)
     return;
   EndRotationBatching();
-  SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
-                              absl::nullopt,
-                              /*reuse_current_local_surface_id=*/false,
-                              /*ignore_ack=*/true);
+
+  if (is_showing_) {
+    SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
+                                absl::nullopt,
+                                /*reuse_current_local_surface_id=*/false,
+                                /*ignore_ack=*/true);
+  } else {
+    // If hidden, generate a new viz::LocalSurfaceId to represent the new set of
+    // blink::VisualProperties. However do not synchronize them to perform
+    // layout. The subsequent Show will lead to embedding (crbug.com/1383446)
+    local_surface_id_allocator_.GenerateId();
+  }
   BeginRotationEmbed();
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_android_unittest.cc b/content/browser/renderer_host/render_widget_host_view_android_unittest.cc
index 4386123..7b9024e 100644
--- a/content/browser/renderer_host/render_widget_host_view_android_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android_unittest.cc
@@ -430,6 +430,9 @@
   // viz::LocalSurfaceId.
   viz::LocalSurfaceId PortraitToFullscreenLanscape();
 
+  // Fires the rotation throttle timeout.
+  void FireRotationTimeout();
+
   // RenderWidgetHostViewAndroid:
   void EnterFullscreenMode();
   void ExitFullscreenMode();
@@ -543,6 +546,10 @@
   return GetLocalSurfaceIdAndConfirmNewerThan(initial_local_surface_id);
 }
 
+void RenderWidgetHostViewAndroidRotationTest::FireRotationTimeout() {
+  render_widget_host_view_android()->rotation_timeout_.FireNow();
+}
+
 void RenderWidgetHostViewAndroidRotationTest::EnterFullscreenMode() {
   blink::mojom::FullscreenOptions options;
   render_widget_host_view_android()->EnterFullscreenMode(options);
@@ -935,6 +942,68 @@
   GetLocalSurfaceIdAndConfirmNewerThan(local_surface_id);
 }
 
+// Tests that when Android fakes visibility to start a rotation, before hiding
+// and completing the rotation later, that the rotation timeout and subsequent
+// actual visibility change correctly updates the viz::LocalSurfaceId and stops
+// all throttling. (https://crbug.com/1383446)
+TEST_F(RenderWidgetHostViewAndroidRotationTest, FakeVisibilityScreenRotation) {
+  RenderWidgetHostViewAndroid* rwhva = render_widget_host_view_android();
+  auto local_surface_id = rwhva->GetLocalSurfaceId();
+
+  // Portrait orientation split screen. Same width, reduce height by half but
+  // keep inset for system status bar. This should not throttle, but should
+  // advance the viz::LocalSurfaceId
+  OnVisibleViewportSizeChanged(300, 195);
+  OnPhysicalBackingSizeChanged(gfx::Size(600, 390));
+  EXPECT_TRUE(rwhva->CanSynchronizeVisualProperties());
+  auto split_screen_local_surface_id =
+      GetLocalSurfaceIdAndConfirmNewerThan(local_surface_id);
+
+  // Rotate device to landscape orientation
+  SetLandscapeScreenInfo(/*rotation=*/true);
+  EXPECT_FALSE(rwhva->CanSynchronizeVisualProperties());
+  OnVisibleViewportSizeChanged(200, 295);
+  OnPhysicalBackingSizeChanged(gfx::Size(400, 590));
+  EXPECT_TRUE(rwhva->CanSynchronizeVisualProperties());
+  auto post_rotation_local_surface_id =
+      GetLocalSurfaceIdAndConfirmNewerThan(split_screen_local_surface_id);
+
+  // Hiding should not change throttle
+  rwhva->Hide();
+  EXPECT_TRUE(rwhva->CanSynchronizeVisualProperties());
+
+  // When turning off the screen some versions of Android lie. They set us
+  // visible even with the screen off, and rotate the ScreenInfo, but nothing
+  // else. Followed up by hiding us again.
+  rwhva->Show();
+  EXPECT_TRUE(rwhva->CanSynchronizeVisualProperties());
+  SetPortraitScreenInfo(/*rotation=*/true);
+  EXPECT_FALSE(rwhva->CanSynchronizeVisualProperties());
+  rwhva->Hide();
+
+  // When off the timeout can fire. It should clear the throttling and advance
+  // the viz::LocalSurfaceId
+  FireRotationTimeout();
+  EXPECT_TRUE(rwhva->CanSynchronizeVisualProperties());
+  auto post_timeout_local_surface_id =
+      GetLocalSurfaceIdAndConfirmNewerThan(post_rotation_local_surface_id);
+
+  // On some versions of Android the new post-rotation layout can be sent before
+  // we become visible.
+  OnVisibleViewportSizeChanged(300, 195);
+  OnPhysicalBackingSizeChanged(gfx::Size(600, 390));
+  EXPECT_TRUE(rwhva->CanSynchronizeVisualProperties());
+  auto post_hidden_rotation_local_surface_id =
+      GetLocalSurfaceIdAndConfirmNewerThan(post_timeout_local_surface_id);
+
+  // When becoming visible we should have the correct layout already and not
+  // need to advance the viz::LocalSurfaceId. We should also not be throttling.
+  rwhva->Show();
+  auto post_show_local_surface_id = rwhva->GetLocalSurfaceId();
+  EXPECT_EQ(post_show_local_surface_id, post_hidden_rotation_local_surface_id);
+  EXPECT_TRUE(rwhva->CanSynchronizeVisualProperties());
+}
+
 // Tests rotation and fullscreen cases that are supported by both the visual
 // properties analysis, and the fullscreen killswitch legacy path.
 //
@@ -1009,16 +1078,10 @@
   // the final state. So we advance the viz::LocalSurfaceId to have the newest
   // possible content ready.
   rwhva->Show();
-  const viz::LocalSurfaceId shown_local_surface_id =
-      GetLocalSurfaceIdAndConfirmNewerThan(initial_local_surface_id);
-  // However we should still block further updates until rotation has completed.
-  EXPECT_FALSE(rwhva->CanSynchronizeVisualProperties());
-
-  // When rotation has completed we should begin Surface Sync again. There
-  // should also be a new viz::LocalSurfaceId.
-  OnPhysicalBackingSizeChanged(landscape_physical_backing);
+  GetLocalSurfaceIdAndConfirmNewerThan(initial_local_surface_id);
+  // We do not block synchronization, as there is no platform consistency in
+  // resize messages when becoming visible.
   EXPECT_TRUE(rwhva->CanSynchronizeVisualProperties());
-  GetLocalSurfaceIdAndConfirmNewerThan(shown_local_surface_id);
 }
 
 // Test that when the view is hidden, and another enters Fullscreen, that we
@@ -1056,16 +1119,9 @@
   // viz::LocalSurfaceId upon becoming visible, to send all visual updates to
   // the Renderer.
   rwhva->Show();
-  const viz::LocalSurfaceId shown_local_surface_id =
-      GetLocalSurfaceIdAndConfirmNewerThan(initial_local_surface_id);
-  // However we should still block further updates until rotation has completed.
-  EXPECT_FALSE(rwhva->CanSynchronizeVisualProperties());
-
-  // When the Renderer submits the first post rotation frame we unblock further
-  // Surface Sync.
-  cc::RenderFrameMetadata metadata;
-  metadata.local_surface_id = shown_local_surface_id;
-  OnRenderFrameMetadataChangedAfterActivation(metadata, base::TimeTicks::Now());
+  GetLocalSurfaceIdAndConfirmNewerThan(initial_local_surface_id);
+  // We do not block synchronization, as there is no platform consistency in
+  // resize messages when becoming visible.
   EXPECT_TRUE(rwhva->CanSynchronizeVisualProperties());
 }
 
diff --git a/content/browser/webauth/authenticator_common_impl.cc b/content/browser/webauth/authenticator_common_impl.cc
index dbef052..538428e 100644
--- a/content/browser/webauth/authenticator_common_impl.cc
+++ b/content/browser/webauth/authenticator_common_impl.cc
@@ -523,12 +523,6 @@
   DCHECK(make_credential_response_callback_.is_null());
   make_credential_response_callback_ = std::move(callback);
 
-  if (!GetWebAuthenticationDelegate()->IsSecurityLevelAcceptableForWebAuthn(
-          GetRenderFrameHost(), caller_origin)) {
-    CompleteMakeCredentialRequest(
-        blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
-    return;
-  }
   BeginRequestTimeout(options->timeout);
 
   WebAuthRequestSecurityChecker::RequestType request_type =
@@ -567,6 +561,14 @@
     return;
   }
 
+  if (!request_delegate_->IsVirtualEnvironmentEnabled() &&
+      !GetWebAuthenticationDelegate()->IsSecurityLevelAcceptableForWebAuthn(
+          GetRenderFrameHost(), caller_origin)) {
+    CompleteMakeCredentialRequest(
+        blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
+    return;
+  }
+
   caller_origin_ = caller_origin;
   relying_party_id_ = options->relying_party.id;
 
@@ -841,12 +843,6 @@
   DCHECK(get_assertion_response_callback_.is_null());
   get_assertion_response_callback_ = std::move(callback);
 
-  if (!GetWebAuthenticationDelegate()->IsSecurityLevelAcceptableForWebAuthn(
-          GetRenderFrameHost(), caller_origin)) {
-    CompleteGetAssertionRequest(
-        blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
-    return;
-  }
   if (!options->is_conditional) {
     BeginRequestTimeout(options->timeout);
   }
@@ -893,6 +889,13 @@
         blink::mojom::AuthenticatorStatus::PENDING_REQUEST);
     return;
   }
+  if (!request_delegate_->IsVirtualEnvironmentEnabled() &&
+      !GetWebAuthenticationDelegate()->IsSecurityLevelAcceptableForWebAuthn(
+          GetRenderFrameHost(), caller_origin)) {
+    CompleteGetAssertionRequest(
+        blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
+    return;
+  }
 
   caller_origin_ = caller_origin;
   relying_party_id_ = options->relying_party_id;
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index a565a1e..55db07fb 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -2154,6 +2154,7 @@
 };
 
 TEST_F(AuthenticatorContentBrowserClientTest, MakeCredentialTLSError) {
+  NavigateAndCommit(GURL(kTestOrigin1));
   test_client_.GetTestWebAuthenticationDelegate()
       ->is_webauthn_security_level_acceptable = false;
   PublicKeyCredentialCreationOptionsPtr options =
@@ -2163,6 +2164,7 @@
 }
 
 TEST_F(AuthenticatorContentBrowserClientTest, GetAssertionTLSError) {
+  NavigateAndCommit(GURL(kTestOrigin1));
   test_client_.GetTestWebAuthenticationDelegate()
       ->is_webauthn_security_level_acceptable = false;
   PublicKeyCredentialRequestOptionsPtr options =
@@ -2171,6 +2173,40 @@
             AuthenticatorStatus::CERTIFICATE_ERROR);
 }
 
+TEST_F(AuthenticatorContentBrowserClientTest,
+       MakeCredentialSkipTLSCheckWithVirtualEnvironment) {
+  NavigateAndCommit(GURL(kTestOrigin1));
+  content::AuthenticatorEnvironmentImpl::GetInstance()
+      ->EnableVirtualAuthenticatorFor(
+          static_cast<content::RenderFrameHostImpl*>(main_rfh())
+              ->frame_tree_node(),
+          /*enable_ui=*/false);
+  test_client_.GetTestWebAuthenticationDelegate()
+      ->is_webauthn_security_level_acceptable = false;
+  PublicKeyCredentialCreationOptionsPtr options =
+      GetTestPublicKeyCredentialCreationOptions();
+  EXPECT_EQ(AuthenticatorMakeCredential(std::move(options)).status,
+            AuthenticatorStatus::SUCCESS);
+}
+
+TEST_F(AuthenticatorContentBrowserClientTest,
+       GetAssertionSkipTLSCheckWithVirtualEnvironment) {
+  NavigateAndCommit(GURL(kTestOrigin1));
+  content::AuthenticatorEnvironmentImpl::GetInstance()
+      ->EnableVirtualAuthenticatorFor(
+          static_cast<content::RenderFrameHostImpl*>(main_rfh())
+              ->frame_tree_node(),
+          /*enable_ui=*/false);
+  test_client_.GetTestWebAuthenticationDelegate()
+      ->is_webauthn_security_level_acceptable = false;
+  PublicKeyCredentialRequestOptionsPtr options =
+      GetTestPublicKeyCredentialRequestOptions();
+  ASSERT_TRUE(virtual_device_factory_->mutable_state()->InjectRegistration(
+      options->allow_credentials[0].id, kTestRelyingPartyId));
+  EXPECT_EQ(AuthenticatorGetAssertion(std::move(options)).status,
+            AuthenticatorStatus::SUCCESS);
+}
+
 // Test that credentials can be created and used from an extension origin when
 // permitted by the delegate.
 TEST_F(AuthenticatorContentBrowserClientTest, ChromeExtensions) {
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index b20e2c5c..72dd948d 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -567,7 +567,6 @@
       return true;
 
     case ax::mojom::Event::kAriaAttributeChanged:
-    case ax::mojom::Event::kChildrenChanged:
     case ax::mojom::Event::kDocumentTitleChanged:
     case ax::mojom::Event::kExpandedChanged:
     case ax::mojom::Event::kHide:
@@ -589,6 +588,7 @@
     // This list is duplicated in WebFrameTestProxy::PostAccessibilityEvent().
     case ax::mojom::Event::kAlert:
     case ax::mojom::Event::kAutocorrectionOccured:
+    case ax::mojom::Event::kChildrenChanged:
     case ax::mojom::Event::kControlsChanged:
     case ax::mojom::Event::kEndOfTest:
     case ax::mojom::Event::kFocusAfterMenuClose:
@@ -762,7 +762,7 @@
   if (obj.IsNull())
     return;
 
-  HandleAXEvent(ui::AXEvent(obj.AxID(), ax::mojom::Event::kChildrenChanged));
+  MarkWebAXObjectDirty(obj, /* subtree */ false);
 }
 
 void RenderAccessibilityImpl::ShowPluginContextMenu() {
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index f2c03b75..60b7180 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -453,8 +453,7 @@
 
   // Send a childrenChanged on "A".
   ClearHandledUpdates();
-  GetRenderAccessibilityImpl()->HandleAXEvent(
-      ui::AXEvent(node_a.AxID(), ax::mojom::Event::kChildrenChanged));
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(node_a, false);
 
   // Hide node "B" ("C" stays visible).
   ExecuteJavaScriptForTests(
@@ -466,7 +465,7 @@
 
   // Since ignored nodes are included in the ax tree with State::kIgnored set,
   // "C" is NOT reparented, only the changed nodes are re-serialized.
-  // "A" updates because it handled Event::kChildrenChanged
+  // "A" updates because it handled dirty object
   // "B" updates because its State::kIgnored has changed
   EXPECT_EQ(0, update.node_id_to_clear);
   EXPECT_EQ(node_a.AxID(), update.nodes[0].id);
@@ -502,8 +501,7 @@
   WebAXObject node_c = node_b.ChildAt(0);
 
   // Send a childrenChanged on "A" and show node "B",
-  GetRenderAccessibilityImpl()->HandleAXEvent(
-      ui::AXEvent(node_a.AxID(), ax::mojom::Event::kChildrenChanged));
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(node_a, false);
   ExecuteJavaScriptForTests(
       "document.getElementById('B').style.visibility = 'visible';");
 
@@ -514,7 +512,7 @@
 
   // Since ignored nodes are included in the ax tree with State::kIgnored set,
   // "C" is NOT reparented, only the changed nodes are re-serialized.
-  // "A" updates because it handled Event::kChildrenChanged
+  // "A" updates because it handled the dirty
   // "B" updates because its State::kIgnored has changed
   ASSERT_EQ(2U, update.nodes.size());
   EXPECT_EQ(0, update.node_id_to_clear);
@@ -1220,8 +1218,7 @@
   // No URL-keyed metrics should be fired after we send one event.
   WebDocument document = GetMainFrame()->GetDocument();
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
-  GetRenderAccessibilityImpl()->HandleAXEvent(
-      ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kChildrenChanged));
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
   SendPendingAccessibilityEvents();
   EXPECT_EQ(0, ukm_recorder()->calls());
   histogram_tester.ExpectTotalCount(
@@ -1230,8 +1227,7 @@
   // No URL-keyed metrics should be fired even after an event that takes
   // 300 ms, but we should now have something to send.
   // This must be >= kMinSerializationTimeToSendInMS
-  GetRenderAccessibilityImpl()->HandleAXEvent(
-      ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kChildrenChanged));
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
   SendPendingAccessibilityEvents();
   SetTimeDelayForNextSerialize(base::Milliseconds(300));
   EXPECT_EQ(0, ukm_recorder()->calls());
@@ -1241,8 +1237,7 @@
   // After 1000 seconds have passed, the next time we send an event we should
   // send URL-keyed metrics.
   task_environment_.FastForwardBy(base::Seconds(1000));
-  GetRenderAccessibilityImpl()->HandleAXEvent(
-      ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kChildrenChanged));
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
   SendPendingAccessibilityEvents();
   EXPECT_EQ(1, ukm_recorder()->calls());
   histogram_tester.ExpectTotalCount(
@@ -1251,8 +1246,7 @@
   // Send another event that takes a long (simulated) time to serialize.
   // This must be >= kMinSerializationTimeToSend
   SetTimeDelayForNextSerialize(base::Milliseconds(200));
-  GetRenderAccessibilityImpl()->HandleAXEvent(
-      ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kChildrenChanged));
+  GetRenderAccessibilityImpl()->MarkWebAXObjectDirty(root_obj, false);
   SendPendingAccessibilityEvents();
   histogram_tester.ExpectTotalCount(
       "Accessibility.Performance.SendPendingAccessibilityEvents", 4);
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index da1a026..c17ef270 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -313,6 +313,9 @@
 
 crbug.com/1297636 [ mac ] Pixel_WebGPUImportUnaccelerated2DCanvas [ Failure ]
 
+# Pixel_WebGPUDisplayP3 socket timeout on Mac FYI Retina Release (NVIDIA)
+crbug.com/1392819 [ mac nvidia no-asan ] Pixel_WebGPUDisplayP3 [ Failure ]
+
 # Failures on Sherlock
 # These two tests causes a crash on Swarming. Skipping so that we have a signal for other tests.
 crbug.com/1268144 [ fuchsia fuchsia-board-sherlock renderer-skia-vulkan ] Pixel_BackgroundImage [ Skip ]
diff --git a/content/web_test/renderer/web_frame_test_proxy.cc b/content/web_test/renderer/web_frame_test_proxy.cc
index 376a78b..30000db 100644
--- a/content/web_test/renderer/web_frame_test_proxy.cc
+++ b/content/web_test/renderer/web_frame_test_proxy.cc
@@ -559,9 +559,6 @@
     case ax::mojom::Event::kCheckedStateChanged:
       event_name = "CheckedStateChanged";
       break;
-    case ax::mojom::Event::kChildrenChanged:
-      event_name = "ChildrenChanged";
-      break;
     case ax::mojom::Event::kClicked:
       event_name = "Clicked";
       break;
@@ -631,6 +628,7 @@
     // RenderAccessibilityImpl::IsImmediateProcessingRequiredForEvent().
     case ax::mojom::Event::kAlert:
     case ax::mojom::Event::kAutocorrectionOccured:
+    case ax::mojom::Event::kChildrenChanged:
     case ax::mojom::Event::kControlsChanged:
     case ax::mojom::Event::kEndOfTest:
     case ax::mojom::Event::kFocusAfterMenuClose:
diff --git a/gin/v8_foreground_task_runner_with_locker.cc b/gin/v8_foreground_task_runner_with_locker.cc
index 814a6eb..2a3bea7 100644
--- a/gin/v8_foreground_task_runner_with_locker.cc
+++ b/gin/v8_foreground_task_runner_with_locker.cc
@@ -10,6 +10,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "v8/include/v8-isolate.h"
 #include "v8/include/v8-locker.h"
 
 namespace gin {
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 293b989c..6b28668 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -2181,8 +2181,8 @@
     return;
   }
 
-  if (SkColorTypeBytesPerPixel(viz::ResourceFormatToClosestSkColorType(
-          true, dest_shared_image->format())) !=
+  if (SkColorTypeBytesPerPixel(
+          viz::ToClosestSkColorType(true, dest_shared_image->format())) !=
       SkColorTypeBytesPerPixel(static_cast<SkColorType>(src_sk_color_type))) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glWritePixels",
                        "Bytes per pixel for src SkColorType and dst "
@@ -3111,7 +3111,7 @@
   DCHECK(locked_handles_.empty());
   DCHECK(!raster_canvas_);
 
-  SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
+  SkColorType sk_color_type = viz::ToClosestSkColorType(
       /*gpu_compositing=*/true, shared_image->format());
 
   int final_msaa_count;
diff --git a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
index d9dc5d8..518b3a8 100644
--- a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
@@ -91,8 +91,8 @@
     // reuse the cached SkSurface.
     if (!surface || surface_props != surface->props() ||
         final_msaa_count != backing_impl()->surface_msaa_count_) {
-      SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
-          true /* gpu_compositing */, format());
+      SkColorType sk_color_type =
+          viz::ToClosestSkColorType(true /* gpu_compositing */, format());
       surface = SkSurface::MakeFromBackendTexture(
           backing_impl()->gr_context(), backing_impl()->backend_texture_,
           surface_origin(), final_msaa_count, sk_color_type,
diff --git a/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc b/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc
index 139a86ab..21d2d74 100644
--- a/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc
@@ -281,7 +281,7 @@
   GrGLFramebufferInfo framebuffer_info = {0};
   DCHECK_EQ(gl_surface_->GetBackingFramebufferObject(), 0u);
 
-  SkColorType color_type = viz::ResourceFormatToClosestSkColorType(
+  SkColorType color_type = viz::ToClosestSkColorType(
       /*gpu_compositing=*/true, format());
   switch (color_type) {
     case kRGBA_8888_SkColorType:
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc b/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc
index b1eb1e9..e56746eb 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc
@@ -64,8 +64,8 @@
   // reuse the cached SkSurface.
   if (!surface || surface_props != surface->props() ||
       final_msaa_count != surface_msaa_count_) {
-    SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
-        true /* gpu_compositing */, format());
+    SkColorType sk_color_type =
+        viz::ToClosestSkColorType(true /* gpu_compositing */, format());
     surface = SkSurface::MakeFromBackendTexture(
         gr_context, promise_texture->backendTexture(), surface_origin(),
         final_msaa_count, sk_color_type,
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_common_representations.cc b/gpu/command_buffer/service/shared_image/gl_texture_common_representations.cc
index 5280515..87c9c58 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_common_representations.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_common_representations.cc
@@ -161,7 +161,7 @@
   if (!promise_texture_)
     return {};
 
-  SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
+  SkColorType sk_color_type = viz::ToClosestSkColorType(
       /*gpu_compositing=*/true, format());
   // Gray is not a renderable single channel format, but alpha is.
   if (sk_color_type == kGray_8_SkColorType)
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
index 7e1c9d8..013f40a 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
@@ -546,8 +546,7 @@
       alpha_type, usage, false /* is_thread_safe */);
   ASSERT_TRUE(backing);
 
-  SkColorType color_type =
-      viz::ResourceFormatToClosestSkColorType(true, format);
+  SkColorType color_type = viz::ToClosestSkColorType(true, format);
 
   // Allocate a bitmap with red pixels and upload from it. RED_8 will be filled
   // with 0xFF repeating and RG_88 will be filled with OxFF00 repeating.
@@ -595,8 +594,7 @@
       alpha_type, usage, false /* is_thread_safe */);
   ASSERT_TRUE(backing);
 
-  SkColorType color_type =
-      viz::ResourceFormatToClosestSkColorType(true, format);
+  SkColorType color_type = viz::ToClosestSkColorType(true, format);
 
   // Allocate a bitmap with red pixels and upload from it. RED_8 will be filled
   // with 0xFF repeating and RG_88 will be filled with OxFF00 repeating.
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
index b88a6025..89089cf 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
@@ -173,7 +173,7 @@
   if (!promise_texture_)
     return {};
 
-  SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
+  SkColorType sk_color_type = viz::ToClosestSkColorType(
       /*gpu_compositing=*/true, format());
   // Gray is not a renderable single channel format, but alpha is.
   if (sk_color_type == kGray_8_SkColorType)
diff --git a/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc b/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc
index a9e3425..c73da28 100644
--- a/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc
@@ -205,7 +205,7 @@
 
   auto mipmap = usage() & SHARED_IMAGE_USAGE_MIPMAP ? GrMipMapped::kYes
                                                     : GrMipMapped::kNo;
-  auto sk_color = viz::ResourceFormatToClosestSkColorType(
+  auto sk_color = viz::ToClosestSkColorType(
       /*gpu_compositing=*/true, format());
   const std::string label =
       "RawDrawImageBacking" + CreateLabelForSharedImageUsage(usage());
diff --git a/gpu/command_buffer/service/shared_image/shared_image_backing.cc b/gpu/command_buffer/service/shared_image/shared_image_backing.cc
index c16407b..3881855 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_backing.cc
@@ -97,7 +97,7 @@
 
 SkImageInfo SharedImageBacking::AsSkImageInfo() const {
   return SkImageInfo::Make(size_.width(), size_.height(),
-                           viz::ResourceFormatToClosestSkColorType(
+                           viz::ToClosestSkColorType(
                                /*gpu_compositing=*/true, format()),
                            alpha_type_, color_space_.ToSkColorSpace());
 }
diff --git a/gpu/command_buffer/service/shared_image/shared_image_representation.cc b/gpu/command_buffer/service/shared_image/shared_image_representation.cc
index d0fae12..bf72b61 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_representation.cc
@@ -270,8 +270,7 @@
     SkImage::TextureReleaseProc texture_release_proc,
     SkImage::ReleaseContext release_context) const {
   auto surface_origin = representation()->surface_origin();
-  auto color_type =
-      viz::ResourceFormatToClosestSkColorType(true, representation()->format());
+  auto color_type = viz::ToClosestSkColorType(true, representation()->format());
   auto alpha_type = representation()->alpha_type();
   auto sk_color_space =
       representation()->color_space().GetAsFullRangeRGB().ToSkColorSpace();
diff --git a/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc b/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc
index f7638d5d..345fcff 100644
--- a/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc
@@ -4,9 +4,11 @@
 
 #include "gpu/command_buffer/service/shared_image/skia_gl_image_representation.h"
 
+#include "base/check_op.h"
 #include "base/memory/ptr_util.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
@@ -39,22 +41,39 @@
     SharedImageManager* manager,
     SharedImageBacking* backing,
     MemoryTypeTracker* tracker) {
-  DCHECK(backing->format().is_single_plane());
-  GrBackendTexture backend_texture;
-  // TODO(hitawala): Use format/size per plane for multiplanar formats.
-  if (!GetGrBackendTexture(
-          context_state->feature_info(),
-          gl_representation->GetTextureBase()->target(), backing->size(),
-          gl_representation->GetTextureBase()->service_id(),
-          backing->format().resource_format(),
-          context_state->gr_context()->threadSafeProxy(), &backend_texture)) {
-    return nullptr;
+  std::vector<sk_sp<SkPromiseImageTexture>> promise_textures;
+  auto format = backing->format();
+  // Check if wrapped GL representation contains same number of
+  // SharedImageFormat plane(s) and essentially same number of texture(s) as the
+  // number for SharedImageFormat plane(s) from backing used to create
+  // GrBackendTexture(s).
+  DCHECK_EQ(format.NumberOfPlanes(),
+            gl_representation->format().NumberOfPlanes());
+  for (int plane_index = 0; plane_index < format.NumberOfPlanes();
+       plane_index++) {
+    GrBackendTexture backend_texture;
+    bool angle_rgbx_internal_format = context_state->feature_info()
+                                          ->feature_flags()
+                                          .angle_rgbx_internal_format;
+    // Use the format and size per plane for multiplanar formats.
+    GLenum plane_gl_storage_format =
+        TextureStorageFormat(format, angle_rgbx_internal_format, plane_index);
+    gfx::Size plane_size = format.GetPlaneSize(plane_index, backing->size());
+    if (!GetGrBackendTexture(
+            context_state->feature_info(),
+            gl_representation->GetTextureBase(plane_index)->target(),
+            plane_size,
+            gl_representation->GetTextureBase(plane_index)->service_id(),
+            plane_gl_storage_format,
+            context_state->gr_context()->threadSafeProxy(), &backend_texture)) {
+      return nullptr;
+    }
+    auto promise_texture = SkPromiseImageTexture::Make(backend_texture);
+    if (!promise_texture)
+      return nullptr;
+    promise_textures.push_back(promise_texture);
   }
-  auto promise_texture = SkPromiseImageTexture::Make(backend_texture);
-  if (!promise_texture)
-    return nullptr;
-  std::vector<sk_sp<SkPromiseImageTexture>> promise_textures = {
-      promise_texture};
+
   return base::WrapUnique(new SkiaGLImageRepresentation(
       std::move(gl_representation), std::move(promise_textures),
       std::move(context_state), manager, backing, tracker));
@@ -105,19 +124,24 @@
   if (!surfaces_.empty())
     return surfaces_;
 
-  DCHECK(format().is_single_plane());
-  // TODO(hitawala): Get SkColorType based on plane_idx for multiplanar
-  // formats.
-  SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
-      /*gpu_compositing=*/true, format());
-  auto surface = SkSurface::MakeFromBackendTexture(
-      context_state_->gr_context(), promise_textures_[0]->backendTexture(),
-      surface_origin(), final_msaa_count, sk_color_type,
-      backing()->color_space().ToSkColorSpace(), &surface_props);
-  if (!surface)
-    return {};
+  DCHECK_EQ(static_cast<int>(promise_textures_.size()),
+            format().NumberOfPlanes());
+  std::vector<sk_sp<SkSurface>> surfaces;
+  for (int plane_index = 0; plane_index < format().NumberOfPlanes();
+       plane_index++) {
+    // Use the color type per plane for multiplanar formats.
+    SkColorType sk_color_type = viz::ToClosestSkColorType(
+        /*gpu_compositing=*/true, format(), plane_index);
+    auto surface = SkSurface::MakeFromBackendTexture(
+        context_state_->gr_context(),
+        promise_textures_[plane_index]->backendTexture(), surface_origin(),
+        final_msaa_count, sk_color_type,
+        backing()->color_space().ToSkColorSpace(), &surface_props);
+    if (!surface)
+      return {};
+    surfaces.push_back(surface);
+  }
 
-  std::vector<sk_sp<SkSurface>> surfaces = {surface};
   surfaces_ = surfaces;
   return surfaces;
 }
diff --git a/gpu/command_buffer/service/shared_image/skia_vk_android_image_representation.cc b/gpu/command_buffer/service/shared_image/skia_vk_android_image_representation.cc
index a3b9a63..f0e1806 100644
--- a/gpu/command_buffer/service/shared_image/skia_vk_android_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/skia_vk_android_image_representation.cc
@@ -77,7 +77,7 @@
 
   if (!surface_ || final_msaa_count != surface_msaa_count_ ||
       surface_props != surface_->props()) {
-    SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
+    SkColorType sk_color_type = viz::ToClosestSkColorType(
         /*gpu_compositing=*/true, format());
     surface_ = SkSurface::MakeFromBackendTexture(
         gr_context, promise_texture_->backendTexture(), surface_origin(),
diff --git a/gpu/command_buffer/service/shared_image/skia_vk_ozone_image_representation.cc b/gpu/command_buffer/service/shared_image/skia_vk_ozone_image_representation.cc
index 482981ef4..720aa3e8 100644
--- a/gpu/command_buffer/service/shared_image/skia_vk_ozone_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/skia_vk_ozone_image_representation.cc
@@ -84,7 +84,7 @@
 
   if (!surface_ || final_msaa_count != surface_msaa_count_ ||
       surface_props != surface_->props()) {
-    SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
+    SkColorType sk_color_type = viz::ToClosestSkColorType(
         /*gpu_compositing=*/true, format());
     surface_ = SkSurface::MakeFromBackendTexture(
         gr_context, promise_texture_->backendTexture(), surface_origin(),
diff --git a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
index 63bef6e..b20772f0 100644
--- a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
@@ -165,8 +165,7 @@
   }
 
   SkColorType GetSkColorType() {
-    return viz::ResourceFormatToClosestSkColorType(
-        /*gpu_compositing=*/true, format());
+    return viz::ToClosestSkColorType(/*gpu_compositing=*/true, format());
   }
 
   sk_sp<SkSurface> GetSkSurface(
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 1f4284ed..7477ee77e 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -55883,7 +55883,7 @@
       name: "android-12-x64-rel-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:android-12-x64-rel-compilator"
-      dimensions: "cores:32"
+      dimensions: "cores:16|32"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -56512,7 +56512,7 @@
       name: "android-arm64-rel-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:android-arm64-rel-compilator"
-      dimensions: "cores:32"
+      dimensions: "cores:16|32"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -56843,7 +56843,7 @@
       name: "android-binary-size"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:android-binary-size"
-      dimensions: "cores:16"
+      dimensions: "cores:8|16"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -60152,7 +60152,7 @@
       name: "android-pie-arm64-rel-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:android-pie-arm64-rel-compilator"
-      dimensions: "cores:32"
+      dimensions: "cores:16|32"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -63274,7 +63274,7 @@
       name: "chromeos-amd64-generic-rel-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:chromeos-amd64-generic-rel-compilator"
-      dimensions: "cores:16"
+      dimensions: "cores:8|16"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -66456,7 +66456,7 @@
       name: "fuchsia-x64-cast-receiver-rel-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:fuchsia-x64-cast-receiver-rel-compilator"
-      dimensions: "cores:16"
+      dimensions: "cores:8|16"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -75896,7 +75896,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -76824,7 +76824,7 @@
       name: "linux-chromeos-rel-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:linux-chromeos-rel-compilator"
-      dimensions: "cores:32"
+      dimensions: "cores:16|32"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -79891,7 +79891,7 @@
       name: "linux-rel-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:linux-rel-compilator"
-      dimensions: "cores:16"
+      dimensions: "cores:8|16"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -81143,7 +81143,7 @@
       name: "linux-wayland-rel-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:linux-wayland-rel-compilator"
-      dimensions: "cores:16"
+      dimensions: "cores:8|16"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -82478,7 +82478,7 @@
       name: "linux_chromium_asan_rel_ng-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:linux_chromium_asan_rel_ng-compilator"
-      dimensions: "cores:16"
+      dimensions: "cores:8|16"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -83927,7 +83927,7 @@
       name: "linux_chromium_tsan_rel_ng-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:linux_chromium_tsan_rel_ng-compilator"
-      dimensions: "cores:16"
+      dimensions: "cores:8|16"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
@@ -86592,7 +86592,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -86701,7 +86701,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -86810,7 +86810,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -87028,7 +87028,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -87137,7 +87137,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -87575,7 +87575,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -87684,7 +87684,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -91202,7 +91202,7 @@
       name: "win-rel-compilator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:win-rel-compilator"
-      dimensions: "cores:32"
+      dimensions: "cores:16|32"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows-10"
       dimensions: "pool:luci.chromium.try"
@@ -92536,7 +92536,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
@@ -92866,7 +92866,7 @@
       }
       experiments {
         key: "luci.buildbucket.omit_python2"
-        value: 0
+        value: 100
       }
       experiments {
         key: "luci.recipes.use_python3"
diff --git a/infra/config/subprojects/chromium/try/tryserver.blink.star b/infra/config/subprojects/chromium/try/tryserver.blink.star
index 4a990e5..4e3ed64 100644
--- a/infra/config/subprojects/chromium/try/tryserver.blink.star
+++ b/infra/config/subprojects/chromium/try/tryserver.blink.star
@@ -16,9 +16,6 @@
     execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
     pool = try_.DEFAULT_POOL,
     service_account = try_.DEFAULT_SERVICE_ACCOUNT,
-
-    # TODO(crbug.com/1362440): remove this.
-    omit_python2 = False,
 )
 
 consoles.list_view(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 8197d6d..8a88aa6c 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -13,7 +13,7 @@
 try_.defaults.set(
     builder_group = "tryserver.chromium.android",
     cores = 8,
-    compilator_cores = 32,
+    compilator_cores = 16,
     orchestrator_cores = 4,
     executable = try_.DEFAULT_EXECUTABLE,
     execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
@@ -80,6 +80,8 @@
     # TODO(crbug.com/1225851): Enable it on branch after running on CQ
     # branch_selector = branches.STANDARD_MILESTONE,
     main_list_view = "try",
+    # TODO (gatong): Remove once we've migrated to n2s
+    cores = "16|32",
 )
 
 try_.builder(
@@ -125,6 +127,8 @@
     # branch_selector = branches.STANDARD_MILESTONE,
     check_for_flakiness = True,
     main_list_view = "try",
+    # TODO (gatong): Remove once we've migrated to n2s
+    cores = "16|32",
 )
 
 try_.builder(
@@ -146,9 +150,8 @@
     name = "android-binary-size",
     branch_selector = branches.STANDARD_MILESTONE,
     builderless = not settings.is_main,
-    # TODO (kimstephanie): Change to cores = 16 and ssd = True once bots have
-    # landed
-    cores = 16,
+    # TODO (gatong): Change to cores = 8 once we've migrated to n2s
+    cores = "8|16",
     executable = "recipe:binary_size_trybot",
     goma_backend = None,
     main_list_view = "try",
@@ -468,6 +471,8 @@
     check_for_flakiness = True,
     goma_backend = None,
     main_list_view = "try",
+    # TODO (gatong): Remove once we've migrated to n2s
+    cores = "16|32",
 )
 
 try_.builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
index 6268c27..7e02292d 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -14,7 +14,7 @@
     builder_group = "tryserver.chromium.chromiumos",
     cores = 8,
     orchestrator_cores = 2,
-    compilator_cores = 32,
+    compilator_cores = 16,
     executable = try_.DEFAULT_EXECUTABLE,
     execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
     goma_backend = goma.backend.RBE_PROD,
@@ -70,7 +70,8 @@
     name = "chromeos-amd64-generic-rel-compilator",
     branch_selector = branches.CROS_LTS_MILESTONE,
     main_list_view = "try",
-    cores = 16,
+    # TODO (gatong): Change to cores = 8 once we've migrated to n2s
+    cores = "8|16",
     goma_backend = None,
 )
 
@@ -247,6 +248,8 @@
     branch_selector = branches.CROS_LTS_MILESTONE,
     main_list_view = "try",
     goma_backend = None,
+    # TODO (gatong): Remove once we've migrated to n2s
+    cores = "16|32",
 )
 
 try_.orchestrator_builder(
@@ -269,6 +272,7 @@
 try_.compilator_builder(
     name = "linux-chromeos-rel-reclient-compilator",
     builderless = True,
+    cores = 32,
 )
 
 try_.builder(
@@ -300,6 +304,7 @@
     branch_selector = branches.STANDARD_MILESTONE,
     main_list_view = "try",
     goma_backend = None,
+    cores = 32,
 )
 
 try_.builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
index cb14c6f3..9894de17 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
@@ -14,7 +14,7 @@
     builder_group = "tryserver.chromium.fuchsia",
     cores = 8,
     orchestrator_cores = 2,
-    compilator_cores = 16,
+    compilator_cores = 8,
     executable = try_.DEFAULT_EXECUTABLE,
     execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
     goma_backend = goma.backend.RBE_PROD,
@@ -78,7 +78,7 @@
 try_.compilator_builder(
     name = "fuchsia-arm64-rel-compilator",
     branch_selector = branches.FUCHSIA_LTS_MILESTONE,
-    # TODO(crbug.com/1298110): Set to 16 once compilator bots are moved
+    # TODO (gatong): Remove once we've migrated to n2s
     cores = "8|16",
     # TODO(crbug.com/1298110): Set to True once compilator bots are moved
     ssd = None,
@@ -162,7 +162,7 @@
 try_.compilator_builder(
     name = "fuchsia-x64-cast-receiver-rel-compilator",
     branch_selector = branches.FUCHSIA_LTS_MILESTONE,
-    cores = 16,
+    cores = "8|16",
     ssd = True,
     main_list_view = "try",
     goma_backend = None,
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
index f0762a5..92f7bb5 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -14,7 +14,7 @@
     builder_group = "tryserver.chromium.linux",
     cores = 8,
     orchestrator_cores = 2,
-    compilator_cores = 16,
+    compilator_cores = 8,
     executable = try_.DEFAULT_EXECUTABLE,
     execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
     goma_backend = goma.backend.RBE_PROD,
@@ -226,6 +226,8 @@
     branch_selector = branches.STANDARD_MILESTONE,
     check_for_flakiness = True,
     main_list_view = "try",
+    # TODO (gatong): Remove once we've migrated to n2s
+    cores = "8|16",
 )
 
 try_.orchestrator_builder(
@@ -271,7 +273,8 @@
     name = "linux-wayland-rel-compilator",
     branch_selector = branches.STANDARD_MILESTONE,
     main_list_view = "try",
-    cores = 16,
+    # TODO (gatong): Remove once we've migrated to n2s
+    cores = "8|16",
     ssd = True,
 )
 
@@ -400,6 +403,8 @@
     name = "linux_chromium_asan_rel_ng-compilator",
     branch_selector = branches.STANDARD_MILESTONE,
     main_list_view = "try",
+    # TODO (gatong): Remove once we've migrated to n2s
+    cores = "8|16",
 )
 
 try_.builder(
@@ -579,6 +584,8 @@
     name = "linux_chromium_tsan_rel_ng-compilator",
     branch_selector = branches.STANDARD_MILESTONE,
     main_list_view = "try",
+    # TODO (gatong): Remove once we've migrated to n2s
+    cores = "8|16",
 )
 
 try_.builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
index 9610fd2..b108e9b 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
@@ -14,7 +14,7 @@
     builderless = True,
     cores = 8,
     orchestrator_cores = 2,
-    compilator_cores = 32,
+    compilator_cores = 16,
     executable = try_.DEFAULT_EXECUTABLE,
     execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
     goma_backend = goma.backend.RBE_PROD,
@@ -104,6 +104,8 @@
     main_list_view = "try",
     # TODO (crbug.com/1245171): Revert when root issue is fixed
     grace_period = 4 * time.minute,
+    # TODO (gatong): Remove once we've migrated to n2s
+    cores = "16|32",
 )
 
 try_.builder(
diff --git a/ios/chrome/browser/policy/policy_egtest.mm b/ios/chrome/browser/policy/policy_egtest.mm
index 30239c69..5c6f4f4 100644
--- a/ios/chrome/browser/policy/policy_egtest.mm
+++ b/ios/chrome/browser/policy/policy_egtest.mm
@@ -159,7 +159,6 @@
   // "com.apple.configuration.managed" key.
   AppLaunchConfiguration config = [super appConfigurationForTestCase];
   config.relaunch_policy = NoForceRelaunchAndResetState;
-  config.features_enabled.push_back(safe_browsing::kEnhancedProtection);
   config.features_enabled.push_back(
       password_manager::features::kIOSPasswordUISplit);
 
diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.mm b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.mm
index df4e583..cd2932d1b 100644
--- a/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.mm
+++ b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.mm
@@ -70,7 +70,7 @@
       prefs->GetBoolean(prefs::kSafeBrowsingProceedAnywayDisabled),
       /*should_open_links_in_new_tab=*/false,
       /*always_show_back_to_safety=*/true,
-      base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection),
+      /*is_enhanced_protection_message_enabled=*/true,
       /*is_safe_browsing_managed=*/false, "cpn_safe_browsing");
 }
 }  // namespace
@@ -200,10 +200,8 @@
 
 void SafeBrowsingBlockingPage::SafeBrowsingControllerClient::
     OpenEnhancedProtectionSettings() {
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-    if (web_state()) {
-      SafeBrowsingTabHelper::FromWebState(web_state())
-          ->OpenSafeBrowsingSettings();
-    }
+  if (web_state()) {
+    SafeBrowsingTabHelper::FromWebState(web_state())
+        ->OpenSafeBrowsingSettings();
   }
 }
diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm b/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm
index c8cc23e..cb337be 100644
--- a/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm
+++ b/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm
@@ -113,7 +113,6 @@
   config.additional_args.push_back(
       std::string("--mark_as_allowlisted_for_real_time=") + _safeURL1.spec());
   config.relaunch_policy = NoForceRelaunchAndResetState;
-  config.features_enabled.push_back(safe_browsing::kEnhancedProtection);
   return config;
 }
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.h b/ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.h
index 66a82eb..28746a6 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.h
@@ -49,6 +49,7 @@
       const autofill::CreditCard& card,
       const std::u16string& cvc) override;
   void OnFullCardRequestFailed(
+      autofill::CreditCard::RecordType card_type,
       autofill::payments::FullCardRequest::FailureType failure_type) override;
 
   __weak id<FullCardRequestResultDelegateObserving> delegate_ = nil;
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.mm b/ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.mm
index 8540b242..9734965 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.mm
@@ -33,6 +33,7 @@
 }
 
 void FullCardRequestResultDelegateBridge::OnFullCardRequestFailed(
+    autofill::CreditCard::RecordType /* card_type */,
     autofill::payments::FullCardRequest::FailureType /* failure_type */) {
   [delegate_ onFullCardRequestFailed];
 }
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester_unittest.mm b/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester_unittest.mm
index c537d514..21eaa84 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester_unittest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester_unittest.mm
@@ -12,6 +12,7 @@
 #import "base/time/time.h"
 #import "components/autofill/core/browser/autofill_test_utils.h"
 #import "components/autofill/core/browser/browser_autofill_manager.h"
+#import "components/autofill/core/browser/data_model/credit_card.h"
 #import "components/autofill/core/browser/test_personal_data_manager.h"
 #import "components/autofill/ios/browser/autofill_agent.h"
 #import "components/autofill/ios/browser/autofill_driver_ios.h"
@@ -53,6 +54,7 @@
       const std::u16string& cvc) override {}
 
   void OnFullCardRequestFailed(
+      autofill::CreditCard::RecordType /* card_type */,
       autofill::payments::FullCardRequest::FailureType /* failure_type */)
       override {}
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index ee545d7..77125ec0 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -953,12 +953,10 @@
       initWithBaseViewController:self.viewController
                          browser:self.browser];
 
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-    self.safeBrowsingCoordinator = [[SafeBrowsingCoordinator alloc]
-        initWithBaseViewController:self.viewController
-                           browser:self.browser];
-    [self.safeBrowsingCoordinator start];
-  }
+  self.safeBrowsingCoordinator = [[SafeBrowsingCoordinator alloc]
+      initWithBaseViewController:self.viewController
+                         browser:self.browser];
+  [self.safeBrowsingCoordinator start];
 
   self.textFragmentsCoordinator = [[TextFragmentsCoordinator alloc]
       initWithBaseViewController:self.viewController
@@ -1058,12 +1056,8 @@
   [self.sadTabCoordinator disconnect];
   self.sadTabCoordinator = nil;
 
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-    [self.safeBrowsingCoordinator stop];
-    self.safeBrowsingCoordinator = nil;
-  } else {
-    DCHECK(!self.safeBrowsingCoordinator);
-  }
+  [self.safeBrowsingCoordinator stop];
+  self.safeBrowsingCoordinator = nil;
 
   [self.sharingCoordinator stop];
   self.sharingCoordinator = nil;
diff --git a/ios/chrome/browser/ui/safe_browsing/safe_browsing_coordinator.mm b/ios/chrome/browser/ui/safe_browsing/safe_browsing_coordinator.mm
index 7902862..d140d8a5 100644
--- a/ios/chrome/browser/ui/safe_browsing/safe_browsing_coordinator.mm
+++ b/ios/chrome/browser/ui/safe_browsing/safe_browsing_coordinator.mm
@@ -34,7 +34,6 @@
 
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser {
-  DCHECK(base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection));
   self = [super initWithBaseViewController:viewController browser:browser];
   if (self) {
     _webStateList = browser->GetWebStateList();
@@ -59,7 +58,6 @@
 #pragma mark - SafeBrowsingTabHelperDelegate
 
 - (void)openSafeBrowsingSettings {
-  DCHECK(base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection));
   id<ApplicationCommands> applicationHandler = HandlerForProtocol(
       self.browser->GetCommandDispatcher(), ApplicationCommands);
   [applicationHandler showSafeBrowsingSettings];
@@ -71,7 +69,6 @@
     didInsertWebState:(web::WebState*)webState
               atIndex:(int)index
            activating:(BOOL)activating {
-  DCHECK(base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection));
   SafeBrowsingTabHelper::FromWebState(webState)->SetDelegate(self);
 }
 
@@ -79,7 +76,6 @@
     didReplaceWebState:(web::WebState*)oldWebState
           withWebState:(web::WebState*)newWebState
                atIndex:(int)atIndex {
-  DCHECK(base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection));
   DCHECK(newWebState);
   SafeBrowsingTabHelper::FromWebState(newWebState)->SetDelegate(self);
 }
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
index 49bc6ca..642076e9 100644
--- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
@@ -4,8 +4,6 @@
 
 #import "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
-#import "components/password_manager/core/common/password_manager_features.h"
-#import "components/password_manager/core/common/password_manager_pref_names.h"
 #import "components/policy/core/common/policy_loader_ios_constants.h"
 #import "components/policy/policy_constants.h"
 #import "components/safe_browsing/core/common/features.h"
@@ -107,20 +105,6 @@
   AppLaunchConfiguration config = [super appConfigurationForTestCase];
   config.relaunch_policy = ForceRelaunchByCleanShutdown;
 
-  if ([self isRunningTest:@selector
-            (testTogglePasswordLeakCheckForSignedOutUser)]) {
-    config.features_enabled.push_back(
-        password_manager::features::kLeakDetectionUnauthenticated);
-  }
-
-  if ([self isRunningTest:@selector(testToggleSafeBrowsing)] ||
-      [self isRunningTest:@selector
-            (testTogglePasswordLeakCheckForSignedInUser)] ||
-      [self isRunningTest:@selector
-            (testTogglePasswordLeakCheckForSignedOutUser)]) {
-    config.features_disabled.push_back(safe_browsing::kEnhancedProtection);
-  }
-
   return config;
 }
 
@@ -144,179 +128,6 @@
   [self assertNonPersonalizedServices];
 }
 
-// Tests that the Safe Browsing toggle reflects the current value of the
-// Safe Browsing preference, and updating the toggle also updates the
-// preference.
-- (void)testToggleSafeBrowsing {
-  // Start in the default (opted-in) state for Safe Browsing.
-  [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled];
-
-  [self openGoogleServicesSettings];
-
-  // Check that Safe Browsing is enabled, and toggle it off.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kSafeBrowsingItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/YES,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
-
-  // Check the underlying pref value.
-  GREYAssertFalse([ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnabled],
-                  @"Failed to toggle-off Safe Browsing");
-
-  // Close settings.
-  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
-      performAction:grey_tap()];
-
-  // Open settings again, verify Safe Browsing is still disabled, and re-enable
-  // it.
-  [self openGoogleServicesSettings];
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kSafeBrowsingItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/NO,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(YES)];
-
-  // Check the underlying pref value.
-  GREYAssertTrue([ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnabled],
-                 @"Failed to toggle-on Safe Browsing");
-}
-
-// Tests that password leak detection can only be toggled if Safe Browsing is
-// enabled for signed in user.
-- (void)testTogglePasswordLeakCheckForSignedInUser {
-  // Ensure that Safe Browsing and password leak detection opt-outs start in
-  // their default (opted-in) state.
-  [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled];
-  [ChromeEarlGrey
-      setBoolValue:YES
-       forUserPref:password_manager::prefs::kPasswordLeakDetectionEnabled];
-
-  // Sign in.
-  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
-  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
-  // Open "Google Services" settings.
-  [self openGoogleServicesSettings];
-
-  // Check that Safe Browsing is enabled, and toggle it off.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kSafeBrowsingItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/YES,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
-
-  // Check that the password leak check toggle is both toggled off and disabled.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kPasswordLeakCheckItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/NO,
-                 /*enabled=*/NO)] assertWithMatcher:grey_notNil()];
-
-  // Toggle Safe Browsing on.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kSafeBrowsingItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/NO,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(YES)];
-
-  // Check that the password leak check toggle is enabled, and toggle it off.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kPasswordLeakCheckItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/YES,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
-
-  // Check the underlying pref value.
-  GREYAssertFalse(
-      [ChromeEarlGrey userBooleanPref:password_manager::prefs::
-                                          kPasswordLeakDetectionEnabled],
-      @"Failed to toggle-off password leak checks");
-
-  // Toggle password leak check detection back on.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kPasswordLeakCheckItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/NO,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(YES)];
-
-  // Check the underlying pref value.
-  GREYAssertTrue(
-      [ChromeEarlGrey userBooleanPref:password_manager::prefs::
-                                          kPasswordLeakDetectionEnabled],
-      @"Failed to toggle-on password leak checks");
-}
-
-// Tests that password leak detection can only be toggled if Safe Browsing is
-// enabled for signed out user.
-- (void)testTogglePasswordLeakCheckForSignedOutUser {
-  // Ensure that Safe Browsing and password leak detection opt-outs start in
-  // their default (opted-in) state.
-  [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled];
-  [ChromeEarlGrey
-      setBoolValue:YES
-       forUserPref:password_manager::prefs::kPasswordLeakDetectionEnabled];
-
-  // Open "Google Services" settings.
-  [self openGoogleServicesSettings];
-
-  // Check that Safe Browsing is enabled, and toggle it off.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kSafeBrowsingItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/YES,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
-
-  // Check that the password leak check toggle is both toggled off and disabled.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kPasswordLeakCheckItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/NO,
-                 /*enabled=*/NO)] assertWithMatcher:grey_notNil()];
-
-  // Toggle Safe Browsing on.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kSafeBrowsingItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/NO,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(YES)];
-
-  // Check that the password leak check toggle is enabled, and toggle it off.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kPasswordLeakCheckItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/YES,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
-
-  // Check the underlying pref value.
-  GREYAssertFalse(
-      [ChromeEarlGrey userBooleanPref:password_manager::prefs::
-                                          kPasswordLeakDetectionEnabled],
-      @"Failed to toggle-off password leak checks");
-
-  // Toggle password leak check detection back on.
-  [[self elementInteractionWithGreyMatcher:
-             chrome_test_util::TableViewSwitchCell(
-                 kPasswordLeakCheckItemAccessibilityIdentifier,
-                 /*is_toggled_on=*/NO,
-                 /*enabled=*/YES)]
-      performAction:chrome_test_util::TurnTableViewSwitchOn(YES)];
-
-  // Check the underlying pref value.
-  GREYAssertTrue(
-      [ChromeEarlGrey userBooleanPref:password_manager::prefs::
-                                          kPasswordLeakDetectionEnabled],
-      @"Failed to toggle-on password leak checks");
-}
-
 // Tests that disabling the "Allow Chrome sign-in" > "Sign out" option blocks
 // the user from signing in to Chrome through the promo sign-in until it is
 // re-enabled.
diff --git a/ios/chrome/browser/ui/settings/privacy/BUILD.gn b/ios/chrome/browser/ui/settings/privacy/BUILD.gn
index 20d1e1f..4fb8a9c 100644
--- a/ios/chrome/browser/ui/settings/privacy/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/privacy/BUILD.gn
@@ -167,13 +167,18 @@
   deps = [
     "//base",
     "//base/test:test_support",
+    "//components/password_manager/core/common",
+    "//components/password_manager/core/common:features",
     "//components/safe_browsing/core/common",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//components/strings",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/signin:fake_system_identity",
+    "//ios/chrome/browser/ui/authentication:eg_test_support+eg2",
     "//ios/chrome/browser/ui/popup_menu/overflow_menu:feature_flags",
     "//ios/chrome/browser/ui/settings/privacy:privacy_constants",
     "//ios/chrome/browser/ui/settings/privacy/safe_browsing:safe_browsing_constants",
+    "//ios/chrome/common/ui/table_view:cells_constants",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_egtest.mm b/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_egtest.mm
index b13a096..9d788c43 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_egtest.mm
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_egtest.mm
@@ -3,13 +3,21 @@
 // found in the LICENSE file.
 
 #import "base/test/ios/wait_util.h"
+#import "components/password_manager/core/common/password_manager_features.h"
+#import "components/password_manager/core/common/password_manager_pref_names.h"
 #import "components/safe_browsing/core/common/features.h"
 #import "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #import "components/strings/grit/components_strings.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/authentication/signin_earl_grey_ui_test_util.h"
+#import "ios/chrome/browser/ui/authentication/signin_matchers.h"
 #import "ios/chrome/browser/ui/popup_menu/overflow_menu/feature_flags.h"
 #import "ios/chrome/browser/ui/settings/privacy/privacy_constants.h"
 #import "ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_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.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
@@ -54,10 +62,15 @@
 
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config;
-  config.features_enabled.push_back(safe_browsing::kEnhancedProtection);
   // TODO (crbug.com/1285974) Remove when bug is resolved.
   config.features_disabled.push_back(kNewOverflowMenu);
 
+  if ([self isRunningTest:@selector
+            (testTogglePasswordLeakCheckForSignedOutUser)]) {
+    config.features_enabled.push_back(
+        password_manager::features::kLeakDetectionUnauthenticated);
+  }
+
   return config;
 }
 
@@ -119,22 +132,7 @@
   GREYAssertTrue([ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnabled],
                  @"Failed to keep Standard Safe Browsing on");
 
-  // Taps "No Protection" and then the "Turn Off" Button on pop-up.
-  [ChromeEarlGreyUI tapPrivacySafeBrowsingMenuButton:
-                        grey_allOf(grey_accessibilityID(
-                                       kSettingsSafeBrowsingNoProtectionCellId),
-                                   grey_sufficientlyVisible(), nil)];
-  GREYAssert(
-      WaitForWarningAlert(l10n_util::GetNSString(
-          IDS_IOS_SAFE_BROWSING_NO_PROTECTION_CONFIRMATION_DIALOG_CONFIRM)),
-      @"The No Protection pop-up did not show up");
-  [[EarlGrey
-      selectElementWithMatcher:
-          ButtonWithAccessibilityLabelId(
-              IDS_IOS_SAFE_BROWSING_NO_PROTECTION_CONFIRMATION_DIALOG_CONFIRM)]
-      performAction:grey_tap()];
-  GREYAssertFalse([ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnabled],
-                  @"Failed to toggle-off Standard Safe Browsing");
+  [self turnOffSafeBrowsing];
 }
 
 - (void)testPrivacySafeBrowsingDoneButton {
@@ -196,8 +194,233 @@
       assertWithMatcher:grey_notNil()];
 }
 
+// Tests that Enhanced Protection page can be navigated to and populated
+// correctly.
+- (void)testEnhancedProtectionSettingsPage {
+  [self openPrivacySafeBrowsingSettings];
+  [self pressInfoButtonForCell:kSettingsSafeBrowsingEnhancedProtectionCellId];
+
+  // Check all rows exist.
+  [[self elementInteractionWithGreyMatcher:
+             grey_accessibilityID(kSafeBrowsingEnhancedProtectionShieldCellId)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingEnhancedProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+  [[self elementInteractionWithGreyMatcher:
+             grey_accessibilityID(kSafeBrowsingEnhancedProtectionGIconCellId)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingEnhancedProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+  [[self elementInteractionWithGreyMatcher:
+             grey_accessibilityID(kSafeBrowsingEnhancedProtectionGlobeCellId)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingEnhancedProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+  [[self elementInteractionWithGreyMatcher:
+             grey_accessibilityID(kSafeBrowsingEnhancedProtectionKeyCellId)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingEnhancedProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+  [[self elementInteractionWithGreyMatcher:
+             grey_accessibilityID(kSafeBrowsingEnhancedProtectionMetricCellId)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingEnhancedProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that Standard Protection page can be navigated to and populated
+// correctly.
+- (void)testStandardProtectionSettingsPage {
+  [self openPrivacySafeBrowsingSettings];
+  [self pressInfoButtonForCell:kSettingsSafeBrowsingStandardProtectionCellId];
+
+  // Check all rows exist.
+  [[self elementInteractionWithGreyMatcher:
+             grey_accessibilityID(kSafeBrowsingStandardProtectionShieldCellId)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+  [[self elementInteractionWithGreyMatcher:
+             grey_accessibilityID(kSafeBrowsingStandardProtectionMetricCellId)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+  [[self elementInteractionWithGreyMatcher:
+             grey_accessibilityID(
+                 kSafeBrowsingStandardProtectionPasswordLeakCellId)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+  [[self elementInteractionWithGreyMatcher:
+             grey_accessibilityID(kSafeBrowsingExtendedReportingCellId)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that password leak detection can only be toggled if Safe Browsing is
+// enabled for signed in user.
+- (void)testTogglePasswordLeakCheckForSignedInUser {
+  // Ensure that Safe Browsing and password leak detection opt-outs start in
+  // their default (opted-in) state.
+  [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled];
+  [ChromeEarlGrey
+      setBoolValue:YES
+       forUserPref:password_manager::prefs::kPasswordLeakDetectionEnabled];
+
+  // Sign in.
+  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
+  // Open Privacy Safe Browsing settings.
+  [self openPrivacySafeBrowsingSettings];
+
+  // Check that Safe Browsing is enabled, and toggle it off.
+  GREYAssertFalse([ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnhanced],
+                  @"Failed to keep Enhanced Safe Browsing off");
+  GREYAssertTrue([ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnabled],
+                 @"Failed to keep Standard Safe Browsing on");
+  [self turnOffSafeBrowsing];
+
+  // Open Standard Protection menu.
+  [self pressInfoButtonForCell:kSettingsSafeBrowsingStandardProtectionCellId];
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kSafeBrowsingStandardProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+
+  // Check that the password leak check toggle is both toggled off and disabled.
+  [[self elementInteractionWithGreyMatcher:
+             chrome_test_util::TableViewSwitchCell(
+                 kSafeBrowsingStandardProtectionPasswordLeakCellId,
+                 /*is_toggled_on=*/NO,
+                 /*enabled=*/NO)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+
+  // Toggle Safe Browsing on.
+  [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled];
+
+  // Check that the password leak check toggle is enabled, and toggle it off.
+  [[self elementInteractionWithGreyMatcher:
+             chrome_test_util::TableViewSwitchCell(
+                 kSafeBrowsingStandardProtectionPasswordLeakCellId,
+                 /*is_toggled_on=*/YES,
+                 /*enabled=*/YES)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
+
+  // Check the underlying pref value.
+  GREYAssertFalse(
+      [ChromeEarlGrey userBooleanPref:password_manager::prefs::
+                                          kPasswordLeakDetectionEnabled],
+      @"Failed to toggle-off password leak checks");
+
+  // Toggle password leak check detection back on.
+  [[self elementInteractionWithGreyMatcher:
+             chrome_test_util::TableViewSwitchCell(
+                 kSafeBrowsingStandardProtectionPasswordLeakCellId,
+                 /*is_toggled_on=*/NO,
+                 /*enabled=*/YES)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      performAction:chrome_test_util::TurnTableViewSwitchOn(YES)];
+
+  // Check the underlying pref value.
+  GREYAssertTrue(
+      [ChromeEarlGrey userBooleanPref:password_manager::prefs::
+                                          kPasswordLeakDetectionEnabled],
+      @"Failed to toggle-on password leak checks");
+}
+
+// Tests that password leak detection can only be toggled if Safe Browsing is
+// enabled for signed out user.
+- (void)testTogglePasswordLeakCheckForSignedOutUser {
+  // Ensure that Safe Browsing and password leak detection opt-outs start in
+  // their default (opted-in) state.
+  [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled];
+  [ChromeEarlGrey
+      setBoolValue:YES
+       forUserPref:password_manager::prefs::kPasswordLeakDetectionEnabled];
+
+  // Open Privacy Safe Browsing settings.
+  [self openPrivacySafeBrowsingSettings];
+
+  // Check that Safe Browsing is enabled, and toggle it off.
+  GREYAssertFalse([ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnhanced],
+                  @"Failed to keep Enhanced Safe Browsing off");
+  GREYAssertTrue([ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnabled],
+                 @"Failed to keep Standard Safe Browsing on");
+  [self turnOffSafeBrowsing];
+
+  // Enter Standard Protection settings page.
+  [self pressInfoButtonForCell:kSettingsSafeBrowsingStandardProtectionCellId];
+
+  // Check that the password leak check toggle is both toggled off and disabled.
+  [[self elementInteractionWithGreyMatcher:
+             chrome_test_util::TableViewSwitchCell(
+                 kSafeBrowsingStandardProtectionPasswordLeakCellId,
+                 /*is_toggled_on=*/NO,
+                 /*enabled=*/NO)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      assertWithMatcher:grey_notNil()];
+
+  // Toggle Safe Browsing on.
+  [ChromeEarlGrey setBoolValue:YES forUserPref:prefs::kSafeBrowsingEnabled];
+
+  // Check that the password leak check toggle is enabled, and toggle it off.
+  [[self elementInteractionWithGreyMatcher:
+             chrome_test_util::TableViewSwitchCell(
+                 kSafeBrowsingStandardProtectionPasswordLeakCellId,
+                 /*is_toggled_on=*/YES,
+                 /*enabled=*/YES)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
+
+  // Check the underlying pref value.
+  GREYAssertFalse(
+      [ChromeEarlGrey userBooleanPref:password_manager::prefs::
+                                          kPasswordLeakDetectionEnabled],
+      @"Failed to toggle-off password leak checks");
+
+  // Toggle password leak check detection back on.
+  [[self elementInteractionWithGreyMatcher:
+             chrome_test_util::TableViewSwitchCell(
+                 kSafeBrowsingStandardProtectionPasswordLeakCellId,
+                 /*is_toggled_on=*/NO,
+                 /*enabled=*/YES)
+                         scrollViewMatcher:
+                             grey_accessibilityID(
+                                 kSafeBrowsingStandardProtectionTableViewId)]
+      performAction:chrome_test_util::TurnTableViewSwitchOn(YES)];
+
+  // Check the underlying pref value.
+  GREYAssertTrue(
+      [ChromeEarlGrey userBooleanPref:password_manager::prefs::
+                                          kPasswordLeakDetectionEnabled],
+      @"Failed to toggle-on password leak checks");
+}
+
 #pragma mark - Helpers
 
+// Opens privacy safe browsing settings.
 - (void)openPrivacySafeBrowsingSettings {
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SettingsMenuPrivacyButton()];
@@ -206,4 +429,50 @@
                                IDS_IOS_PRIVACY_SAFE_BROWSING_TITLE)];
 }
 
+// Opens "i" button for a specific cell identifier.
+- (void)pressInfoButtonForCell:(NSString*)cellId {
+  [[EarlGrey
+      selectElementWithMatcher:grey_allOf(
+                                   grey_ancestor(grey_accessibilityID(cellId)),
+                                   grey_accessibilityID(
+                                       kTableViewCellInfoButtonViewId),
+                                   grey_sufficientlyVisible(), nil)]
+      performAction:grey_tap()];
+}
+
+// Taps "No Protection" and then the "Turn Off" Button on pop-up.
+- (void)turnOffSafeBrowsing {
+  [ChromeEarlGreyUI tapPrivacySafeBrowsingMenuButton:
+                        grey_allOf(grey_accessibilityID(
+                                       kSettingsSafeBrowsingNoProtectionCellId),
+                                   grey_sufficientlyVisible(), nil)];
+  GREYAssert(
+      WaitForWarningAlert(l10n_util::GetNSString(
+          IDS_IOS_SAFE_BROWSING_NO_PROTECTION_CONFIRMATION_DIALOG_CONFIRM)),
+      @"The No Protection pop-up did not show up");
+  [[EarlGrey
+      selectElementWithMatcher:
+          ButtonWithAccessibilityLabelId(
+              IDS_IOS_SAFE_BROWSING_NO_PROTECTION_CONFIRMATION_DIALOG_CONFIRM)]
+      performAction:grey_tap()];
+  GREYAssertFalse([ChromeEarlGrey userBooleanPref:prefs::kSafeBrowsingEnabled],
+                  @"Failed to toggle-off Standard Safe Browsing");
+}
+
+// Returns GREYElementInteraction for `matcher`, using `scrollViewMatcher` to
+// scroll.
+- (GREYElementInteraction*)
+    elementInteractionWithGreyMatcher:(id<GREYMatcher>)matcher
+                    scrollViewMatcher:(id<GREYMatcher>)scrollViewMatcher {
+  // 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.
+  const CGFloat kPixelsToScroll = 300;
+  id<GREYAction> searchAction =
+      grey_scrollInDirection(kGREYDirectionDown, kPixelsToScroll);
+  return [[EarlGrey selectElementWithMatcher:matcher]
+         usingSearchAction:searchAction
+      onElementWithMatcher:scrollViewMatcher];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller.mm b/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller.mm
index 31fb480..900ec70 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller.mm
@@ -146,12 +146,7 @@
     _browser = browser;
     _reauthModule = reauthModule;
     _browserState = browser->GetBrowserState();
-    if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-      self.title = l10n_util::GetNSString(IDS_IOS_SETTINGS_PRIVACY_TITLE);
-    } else {
-      self.title =
-          l10n_util::GetNSString(IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY);
-    }
+    self.title = l10n_util::GetNSString(IDS_IOS_SETTINGS_PRIVACY_TITLE);
 
     PrefService* prefService = _browserState->GetPrefs();
 
@@ -209,9 +204,7 @@
 
   TableViewModel* model = self.tableViewModel;
   [model addSectionWithIdentifier:SectionIdentifierPrivacyContent];
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-    [model addSectionWithIdentifier:SectionIdentifierSafeBrowsing];
-  }
+  [model addSectionWithIdentifier:SectionIdentifierSafeBrowsing];
 
   if (base::FeatureList::IsEnabled(
           security_interstitials::features::kHttpsOnlyMode)) {
@@ -231,18 +224,13 @@
       toSectionWithIdentifier:SectionIdentifierPrivacyContent];
 
   // Privacy Safe Browsing item.
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-    [model addItem:[self safeBrowsingDetailItem]
-        toSectionWithIdentifier:SectionIdentifierSafeBrowsing];
-    [model setFooter:[self showPrivacyFooterItem]
-        forSectionWithIdentifier:base::FeatureList::IsEnabled(
-                                     kIOS3PIntentsInIncognito)
-                                     ? SectionIdentifierIncognitoInterstitial
-                                     : SectionIdentifierIncognitoAuth];
-  } else {
-    [model setFooter:[self showPrivacyFooterItem]
-        forSectionWithIdentifier:SectionIdentifierPrivacyContent];
-  }
+  [model addItem:[self safeBrowsingDetailItem]
+      toSectionWithIdentifier:SectionIdentifierSafeBrowsing];
+  [model setFooter:[self showPrivacyFooterItem]
+      forSectionWithIdentifier:base::FeatureList::IsEnabled(
+                                   kIOS3PIntentsInIncognito)
+                                   ? SectionIdentifierIncognitoInterstitial
+                                   : SectionIdentifierIncognitoAuth];
 
   // Web Services item.
   [model addItem:[self handoffDetailItem]
@@ -525,14 +513,12 @@
     return;
   }
 
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
     DCHECK(_safeBrowsingDetailItem);
     if (preferenceName == prefs::kSafeBrowsingEnabled ||
         preferenceName == prefs::kSafeBrowsingEnhanced) {
       _safeBrowsingDetailItem.detailText = [self safeBrowsingDetailText];
       [self reconfigureCellsForItems:@[ _safeBrowsingDetailItem ]];
     }
-  }
 }
 
 #pragma mark - BooleanObserver
diff --git a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
index 84f7d2b1b..6aa29f4 100644
--- a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
@@ -81,7 +81,6 @@
     "//ios/chrome/browser/ui/settings/cells",
     "//ios/chrome/browser/ui/settings/cells:public",
     "//ios/chrome/browser/ui/settings/elements:enterprise_info_popover_view_controller",
-    "//ios/chrome/browser/ui/settings/google_services",
     "//ios/chrome/browser/ui/settings/password",
     "//ios/chrome/browser/ui/settings/privacy:privacy_ui",
     "//ios/chrome/browser/ui/settings/utils",
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
index 086df4f9..bdb4728 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
@@ -25,7 +25,6 @@
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.h"
-#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.h"
 #import "ios/chrome/browser/ui/settings/password/password_issues_coordinator.h"
 #import "ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_coordinator.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h"
@@ -44,7 +43,6 @@
 #endif
 
 @interface SafetyCheckCoordinator () <
-    GoogleServicesSettingsCoordinatorDelegate,
     PasswordIssuesCoordinatorDelegate,
     PopoverLabelViewControllerDelegate,
     PrivacySafeBrowsingCoordinatorDelegate,
@@ -64,11 +62,6 @@
 // Dispatcher which can handle changing passwords on sites.
 @property(nonatomic, strong) id<ApplicationCommands> handler;
 
-// Coordinator for the Google Services screen (SafeBrowsing toggle location
-// when Enhanced Safe Browsing is not available).
-@property(nonatomic, strong)
-    GoogleServicesSettingsCoordinator* googleServicesSettingsCoordinator;
-
 // Coordinator for the Privacy and Security screen (SafeBrowsing toggle
 // location).
 @property(nonatomic, strong)
@@ -131,22 +124,12 @@
 }
 
 - (void)stop {
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-    // If the Safe Browsing Settings page was accessed through the Safe
-    // Browsing row of the safety check, we need to explicity stop the
-    // privacySafeBrowsingCoordinator before closing the settings window.
-    [self.privacySafeBrowsingCoordinator stop];
-    self.privacySafeBrowsingCoordinator.delegate = nil;
-    self.privacySafeBrowsingCoordinator = nil;
-
-  } else {
-    // If the Google Services Settings page was accessed through the Safe
-    // Browsing row of the safety check, we need to explicity stop the
-    // googleServicesSettingsCoordinator before closing the settings window.
-    [self.googleServicesSettingsCoordinator stop];
-    self.googleServicesSettingsCoordinator.delegate = nil;
-    self.googleServicesSettingsCoordinator = nil;
-  }
+  // If the Safe Browsing Settings page was accessed through the Safe
+  // Browsing row of the safety check, we need to explicity stop the
+  // privacySafeBrowsingCoordinator before closing the settings window.
+  [self.privacySafeBrowsingCoordinator stop];
+  self.privacySafeBrowsingCoordinator.delegate = nil;
+  self.privacySafeBrowsingCoordinator = nil;
 }
 
 #pragma mark - SafetyCheckTableViewControllerPresentationDelegate
@@ -226,27 +209,16 @@
 }
 
 - (void)showSafeBrowsingPreferencePage {
-  DCHECK(!self.googleServicesSettingsCoordinator);
   DCHECK(!self.privacySafeBrowsingCoordinator);
   base::RecordAction(
       base::UserMetricsAction("Settings.SafetyCheck.ManageSafeBrowsing"));
   base::UmaHistogramEnumeration("Settings.SafetyCheck.Interactions",
                                 SafetyCheckInteractions::kSafeBrowsingManage);
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-    self.privacySafeBrowsingCoordinator =
-        [[PrivacySafeBrowsingCoordinator alloc]
-            initWithBaseNavigationController:self.baseNavigationController
-                                     browser:self.browser];
-    self.privacySafeBrowsingCoordinator.delegate = self;
-    [self.privacySafeBrowsingCoordinator start];
-  } else {
-    self.googleServicesSettingsCoordinator =
-        [[GoogleServicesSettingsCoordinator alloc]
-            initWithBaseNavigationController:self.baseNavigationController
-                                     browser:self.browser];
-    self.googleServicesSettingsCoordinator.delegate = self;
-    [self.googleServicesSettingsCoordinator start];
-  }
+  self.privacySafeBrowsingCoordinator = [[PrivacySafeBrowsingCoordinator alloc]
+      initWithBaseNavigationController:self.baseNavigationController
+                               browser:self.browser];
+  self.privacySafeBrowsingCoordinator.delegate = self;
+  [self.privacySafeBrowsingCoordinator start];
 }
 
 - (void)showManagedInfoFrom:(UIButton*)buttonView {
@@ -285,17 +257,6 @@
   return NO;
 }
 
-#pragma mark - GoogleServicesSettingsCoordinatorDelegate
-
-- (void)googleServicesSettingsCoordinatorDidRemove:
-    (GoogleServicesSettingsCoordinator*)coordinator {
-  DCHECK(!base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection));
-  DCHECK_EQ(_googleServicesSettingsCoordinator, coordinator);
-  [self.googleServicesSettingsCoordinator stop];
-  self.googleServicesSettingsCoordinator.delegate = nil;
-  self.googleServicesSettingsCoordinator = nil;
-}
-
 #pragma mark - PrivacySafeBrowsingCoordinatorDelegate
 
 - (void)privacySafeBrowsingCoordinatorDidRemove:
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
index 86adcff..e69314ea 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.mm
@@ -495,13 +495,13 @@
     [self.handler showManagedInfoFrom:buttonView];
     return;
   }
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
+
     if (itemType == SafeBrowsingItemType) {
       // Directly open Safe Browsing settings instead of showing a popover.
       [self.handler showSafeBrowsingPreferencePage];
       return;
     }
-  }
+
   if (itemType == UpdateItemType &&
       self.updateCheckRowState == UpdateCheckRowStateManaged) {
     [self.handler showManagedInfoFrom:buttonView];
@@ -533,18 +533,11 @@
   switch (type) {
     case PasswordItemType:
       return [self passwordCheckErrorInfo];
-    case SafeBrowsingItemType: {
-      DCHECK(!base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection));
-      NSString* message = l10n_util::GetNSString(
-          IDS_IOS_SETTINGS_SAFETY_CHECK_OPEN_SAFE_BROWSING_INFO);
-      GURL safeBrowsingURL(
-          base::SysNSStringToUTF8(kSafeBrowsingSafetyCheckStringURL));
-      return [self attributedStringWithText:message link:safeBrowsingURL];
-    }
     case UpdateItemType:
       return [self updateCheckErrorInfoString];
     case CheckStartItemType:
     case HeaderItem:
+    case SafeBrowsingItemType:
     case TimestampFooterItem:
       return nil;
   }
@@ -1222,20 +1215,15 @@
       self.safeBrowsingCheckItem.trailingImage = safeIconImage;
       self.safeBrowsingCheckItem.trailingImageTintColor =
           [UIColor colorNamed:kGreenColor];
-      if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-        self.safeBrowsingCheckItem.detailText =
-            [self safeBrowsingCheckItemDetailText];
-        if (base::FeatureList::IsEnabled(
-                safe_browsing::kEnhancedProtectionPhase2IOS)) {
-          if (safe_browsing::GetSafeBrowsingState(*self.userPrefService) ==
-              safe_browsing::SafeBrowsingState::STANDARD_PROTECTION) {
-            self.safeBrowsingCheckItem.accessoryType =
-                UITableViewCellAccessoryDisclosureIndicator;
-          }
+      self.safeBrowsingCheckItem.detailText =
+          [self safeBrowsingCheckItemDetailText];
+      if (base::FeatureList::IsEnabled(
+              safe_browsing::kEnhancedProtectionPhase2IOS)) {
+        if (safe_browsing::GetSafeBrowsingState(*self.userPrefService) ==
+            safe_browsing::SafeBrowsingState::STANDARD_PROTECTION) {
+          self.safeBrowsingCheckItem.accessoryType =
+              UITableViewCellAccessoryDisclosureIndicator;
         }
-      } else {
-        self.safeBrowsingCheckItem.detailText = GetNSString(
-            IDS_IOS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_DESC);
       }
       break;
     }
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 2bfcb21..107bbf2 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -1069,11 +1069,7 @@
 
 - (TableViewItem*)privacyDetailItem {
   NSString* title = nil;
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-    title = l10n_util::GetNSString(IDS_IOS_SETTINGS_PRIVACY_TITLE);
-  } else {
-    title = l10n_util::GetNSString(IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY);
-  }
+  title = l10n_util::GetNSString(IDS_IOS_SETTINGS_PRIVACY_TITLE);
 
   if (UseSymbols()) {
     return [self detailItemWithType:SettingsItemTypePrivacy
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index 16450fdb..3610714 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -664,14 +664,8 @@
 }
 
 + (id<GREYMatcher>)settingsMenuPrivacyButton {
-  if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedProtection)) {
-    return [ChromeMatchersAppInterface
-        buttonWithAccessibilityLabelID:(IDS_IOS_SETTINGS_PRIVACY_TITLE)];
-  }
-
   return [ChromeMatchersAppInterface
-      buttonWithAccessibilityLabelID:
-          (IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY)];
+      buttonWithAccessibilityLabelID:(IDS_IOS_SETTINGS_PRIVACY_TITLE)];
 }
 
 + (id<GREYMatcher>)settingsMenuPriceNotificationsButton {
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index 4d8fae1e..24c3b35 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-2cd497761d1c284b3d6aa56a7933ed0dfb20de3a
\ No newline at end of file
+6427b890b3f3a2c1c6fd6264ee5232b6ffb0fd81
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index 9e46f12..4a2a385 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-6c63eed8e186ca87ebb32895e04f245e348c0a79
\ No newline at end of file
+3fb80fcc8793e02bd26dbc00cf41cf51d767d1a6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index f55d563..d923b05 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-04c9742e498d9b54fc84ed76098455a123540f05
\ No newline at end of file
+ed8d396550d8ffbd4b91618e97bdb5f5bb67c826
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index a60655e..161c6db 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-b2adb48c4686d8715f25b04dfa48c8c6eec87a24
\ No newline at end of file
+6ca6f98e16df260857bec848f092ffe18bd31583
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 82c7c8fe..e56166fe 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-07fc9b96d9e09cfc446b382a62465ad25aa4cb9b
\ No newline at end of file
+d57dd7d67cfdd20eb567fde4b3c36c3911811ab7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 863de0b..b46740ba8 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-bd324feed8b02b1a65456ebf93987b2116a0ddfc
\ No newline at end of file
+ff325701588e7425bc664b9ac6e627440ff3ef91
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index 2e5caf5d..ae3141b 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-5bf0cbd2b778c64a4bf90a0148e3eeccb3639552
\ No newline at end of file
+87b47a86d254b07c13accde124aa09339d694188
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index cae9237..31a8f9f 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-c76712e564ac331b045753d8e2001276a6323b7f
\ No newline at end of file
+9b0c72c712699c0b90d3079e6c2a8aafd747ed14
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 1872f60..6d69143 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-ee025ebb6f36b448f3a4fef74ac89643abe03bb3
\ No newline at end of file
+2ed81b7b37c4796b1b2342be5468cd77ea43c317
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 52dc5c8..47596d5 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-7dabd39f35263eede1d4584277b92be3139efd66
\ No newline at end of file
+680ac6addb4d134628e16440f8b39b21c00e6278
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 4dedf3a..d01ca80 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-ae285533afc2b383fb80a9ca3b9f555ffbd3d8fa
\ No newline at end of file
+9287159d187f74acc04f4751a8c68dec8813a2d2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 02db3c6d..399e74c1 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-ca6f1a252c03e5e4f0948b631b592a08efff0eae
\ No newline at end of file
+a5047dffff50093331fa6205cfd153894c5fd422
\ No newline at end of file
diff --git a/net/base/mime_sniffer.cc b/net/base/mime_sniffer.cc
index b57d5c3..447bb0c3 100644
--- a/net/base/mime_sniffer.cc
+++ b/net/base/mime_sniffer.cc
@@ -793,18 +793,8 @@
   return have_enough_content;
 }
 
-bool SniffMimeType(const char* content,
-                   size_t content_size,
-                   const GURL& url,
-                   const std::string& type_hint,
-                   ForceSniffFileUrlsForHtml force_sniff_file_url_for_html,
-                   std::string* result) {
-  return SniffMimeType(base::StringPiece(content, content_size), url, type_hint,
-                       force_sniff_file_url_for_html, result);
-}
-
-NET_EXPORT bool SniffMimeTypeFromLocalData(base::StringPiece content,
-                                           std::string* result) {
+bool SniffMimeTypeFromLocalData(base::StringPiece content,
+                                std::string* result) {
   // First check the extra table.
   if (CheckForMagicNumbers(content, kExtraMagicNumbers, result))
     return true;
@@ -812,12 +802,6 @@
   return CheckForMagicNumbers(content, kMagicNumbers, result);
 }
 
-bool SniffMimeTypeFromLocalData(const char* content,
-                                size_t size,
-                                std::string* result) {
-  return SniffMimeTypeFromLocalData(base::StringPiece(content, size), result);
-}
-
 bool LooksLikeBinary(base::StringPiece content) {
   // The definition of "binary bytes" is from the spec at
   // https://mimesniff.spec.whatwg.org/#binary-data-byte
diff --git a/services/device/compute_pressure/pressure_manager_impl.cc b/services/device/compute_pressure/pressure_manager_impl.cc
index 2595825..8788a3bc 100644
--- a/services/device/compute_pressure/pressure_manager_impl.cc
+++ b/services/device/compute_pressure/pressure_manager_impl.cc
@@ -77,17 +77,11 @@
 void PressureManagerImpl::UpdateClients(mojom::PressureState state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   const base::Time timestamp = base::Time::Now();
-  std::vector<mojom::PressureFactor> factors;
 
+  // TODO(crbug.com/1365627): Implement algorithm for pressure factors.
   // https://wicg.github.io/compute-pressure/#contributing-factors
-  if (state == mojom::PressureState::kSerious ||
-      state == mojom::PressureState::kCritical) {
-    // TODO(crbug.com/1365627): Implement algorithm for pressure factors.
-    factors.push_back(mojom::PressureFactor::kThermal);
-    factors.push_back(mojom::PressureFactor::kPowerSupply);
-  }
 
-  mojom::PressureUpdate update(state, std::move(factors), timestamp);
+  mojom::PressureUpdate update(state, {}, timestamp);
   for (auto& client : clients_) {
     client->PressureStateChanged(update.Clone());
   }
diff --git a/services/metrics/public/cpp/ukm_source.cc b/services/metrics/public/cpp/ukm_source.cc
index 20530b69..4e7c5492 100644
--- a/services/metrics/public/cpp/ukm_source.cc
+++ b/services/metrics/public/cpp/ukm_source.cc
@@ -152,7 +152,6 @@
   DCHECK(!proto_source->has_id());
   DCHECK(!proto_source->has_type());
   DCHECK(!proto_source->has_url());
-  DCHECK(!proto_source->has_initial_url());
 
   proto_source->set_id(id_);
   proto_source->set_type(ToProtobufSourceType(type_));
diff --git a/services/viz/public/cpp/compositing/filter_operation_mojom_traits.cc b/services/viz/public/cpp/compositing/filter_operation_mojom_traits.cc
index d6bd2000..a82dc63 100644
--- a/services/viz/public/cpp/compositing/filter_operation_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/filter_operation_mojom_traits.cc
@@ -46,8 +46,6 @@
       return viz::mojom::FilterType::SATURATING_BRIGHTNESS;
     case cc::FilterOperation::ALPHA_THRESHOLD:
       return viz::mojom::FilterType::ALPHA_THRESHOLD;
-    case cc::FilterOperation::STRETCH:
-      return viz::mojom::FilterType::STRETCH;
   }
   NOTREACHED();
   return viz::mojom::FilterType::FILTER_TYPE_LAST;
@@ -86,8 +84,6 @@
       return cc::FilterOperation::SATURATING_BRIGHTNESS;
     case viz::mojom::FilterType::ALPHA_THRESHOLD:
       return cc::FilterOperation::ALPHA_THRESHOLD;
-    case viz::mojom::FilterType::STRETCH:
-      return cc::FilterOperation::STRETCH;
   }
   NOTREACHED();
   return cc::FilterOperation::FILTER_TYPE_LAST;
@@ -170,11 +166,6 @@
       out->set_shape(shape);
       return true;
     }
-    case cc::FilterOperation::STRETCH: {
-      out->set_amount(data.amount());
-      out->set_outer_threshold(data.outer_threshold());
-      return true;
-    }
   }
   return false;
 }
diff --git a/services/viz/public/cpp/compositing/filter_operation_mojom_traits.h b/services/viz/public/cpp/compositing/filter_operation_mojom_traits.h
index b7329127..82989a8 100644
--- a/services/viz/public/cpp/compositing/filter_operation_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/filter_operation_mojom_traits.h
@@ -31,10 +31,8 @@
   }
 
   static float outer_threshold(const cc::FilterOperation& operation) {
-    if (operation.type() != cc::FilterOperation::ALPHA_THRESHOLD &&
-        operation.type() != cc::FilterOperation::STRETCH) {
+    if (operation.type() != cc::FilterOperation::ALPHA_THRESHOLD)
       return 0.f;
-    }
     return operation.outer_threshold();
   }
 
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
index e83125a8..d30c96b 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -171,10 +171,6 @@
     case cc::FilterOperation::ALPHA_THRESHOLD:
       NOTREACHED();
       break;
-    case cc::FilterOperation::STRETCH:
-      EXPECT_EQ(input.amount(), output.amount());
-      EXPECT_EQ(input.outer_threshold(), output.outer_threshold());
-      break;
   }
 }
 
diff --git a/services/viz/public/mojom/compositing/filter_operation.mojom b/services/viz/public/mojom/compositing/filter_operation.mojom
index cbed8f7..cfcae13 100644
--- a/services/viz/public/mojom/compositing/filter_operation.mojom
+++ b/services/viz/public/mojom/compositing/filter_operation.mojom
@@ -25,8 +25,7 @@
   REFERENCE,
   SATURATING_BRIGHTNESS,
   ALPHA_THRESHOLD,
-  STRETCH,
-  FILTER_TYPE_LAST = STRETCH
+  FILTER_TYPE_LAST = ALPHA_THRESHOLD
 };
 
 // See cc/paint/filter_operation.h.
@@ -42,4 +41,3 @@
   skia.mojom.TileMode blur_tile_mode;
   array<gfx.mojom.Rect>? shape;
 };
-
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index d2498db..b22da7d 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3739,6 +3739,7 @@
   kV8DocumentPictureInPicture_Window_AttributeGetter = 4398,
   kV8DocumentPictureInPictureEvent_Window_AttributeGetter = 4399,
   kDocumentPictureInPictureEnterEvent = 4400,
+  kSoftNavigationHeuristics = 4401,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
index ca61345..289b83c0 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -305,9 +305,11 @@
     std::unique_ptr<CachedCSSTokenizer> cached_tokenizer) {
   absl::optional<LocalFrameUkmAggregator::ScopedUkmHierarchicalTimer> timer;
   if (context->GetDocument() && context->GetDocument()->View()) {
-    timer.emplace(
-        context->GetDocument()->View()->EnsureUkmAggregator().GetScopedTimer(
-            static_cast<size_t>(LocalFrameUkmAggregator::kParseStyleSheet)));
+    if (auto* metrics_aggregator =
+            context->GetDocument()->View()->GetUkmAggregator()) {
+      timer.emplace(metrics_aggregator->GetScopedTimer(
+          static_cast<size_t>(LocalFrameUkmAggregator::kParseStyleSheet)));
+    }
   }
   TRACE_EVENT_BEGIN2("blink,blink_style", "CSSParserImpl::parseStyleSheet",
                      "baseUrl", context->BaseURL().GetString().Utf8(), "mode",
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 50593d7..01799db 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2156,7 +2156,7 @@
     }
   }
 
-  SCOPED_UMA_AND_UKM_TIMER(View()->EnsureUkmAggregator(),
+  SCOPED_UMA_AND_UKM_TIMER(View()->GetUkmAggregator(),
                            LocalFrameUkmAggregator::kStyle);
   FontPerformance::StyleScope font_performance_scope;
   ENTER_EMBEDDER_STATE(V8PerIsolateData::MainThreadIsolate(), GetFrame(),
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 6db951a..e3626bbe 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -2622,15 +2622,15 @@
     }
     if (frame->DomWindow() && frame->DomWindow()->IsLocalDOMWindow()) {
       auto pageshow_start_time = base::TimeTicks::Now();
+      LocalDOMWindow* window = frame->DomWindow()->ToLocalDOMWindow();
 
-      frame->DomWindow()->ToLocalDOMWindow()->DispatchPersistedPageshowEvent(
-          navigation_start);
+      window->DispatchPersistedPageshowEvent(navigation_start);
 
-      if (RuntimeEnabledFeatures::NavigationIdEnabled()) {
+      if (RuntimeEnabledFeatures::NavigationIdEnabled(window)) {
         auto pageshow_end_time = base::TimeTicks::Now();
 
-        WindowPerformance* performance = DOMWindowPerformance::performance(
-            *frame->DomWindow()->ToLocalDOMWindow());
+        WindowPerformance* performance =
+            DOMWindowPerformance::performance(*window);
         DCHECK(performance);
 
         performance->AddBackForwardCacheRestoration(
@@ -2639,8 +2639,7 @@
       if (frame->IsOutermostMainFrame()) {
         UMA_HISTOGRAM_BOOLEAN(
             "BackForwardCache.MainFrameHasPageshowListenersOnRestore",
-            frame->DomWindow()->ToLocalDOMWindow()->HasEventListeners(
-                event_type_names::kPageshow));
+            window->HasEventListeners(event_type_names::kPageshow));
       }
     }
   }
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
index 5f0cbbe9..f6857b5 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
@@ -112,9 +112,10 @@
 //
 // |ukm_enum| should be an entry in LocalFrameUkmAggregator's enum of
 // metric names (which in turn corresponds to names from ukm.xml).
-#define SCOPED_UMA_AND_UKM_TIMER(aggregator, ukm_enum) \
-  auto scoped_ukm_hierarchical_timer =                 \
-      aggregator.GetScopedTimer(static_cast<size_t>(ukm_enum));
+#define SCOPED_UMA_AND_UKM_TIMER(aggregator, ukm_enum)                       \
+  absl::optional<LocalFrameUkmAggregator::ScopedUkmHierarchicalTimer> timer; \
+  if (aggregator)                                                            \
+    timer.emplace(aggregator->GetScopedTimer(static_cast<size_t>(ukm_enum)));
 
 class CORE_EXPORT LocalFrameUkmAggregator
     : public RefCounted<LocalFrameUkmAggregator> {
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
index 13df2ea..79a3afe3 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
@@ -656,7 +656,7 @@
 class LocalFrameUkmAggregatorSimTest : public SimTest {
  protected:
   LocalFrameUkmAggregator& local_root_aggregator() {
-    return LocalFrameRoot().GetFrame()->View()->EnsureUkmAggregator();
+    return *LocalFrameRoot().GetFrame()->View()->GetUkmAggregator();
   }
 
   void ChooseNextFrameForTest() {
@@ -712,9 +712,9 @@
         2);
 
     // Simulate the first contentful paint in the main frame.
-    document.View()->EnsureUkmAggregator().BeginMainFrame();
+    document.View()->GetUkmAggregator()->BeginMainFrame();
     PaintTiming::From(GetDocument()).MarkFirstContentfulPaint();
-    document.View()->EnsureUkmAggregator().RecordEndOfFrameMetrics(
+    document.View()->GetUkmAggregator()->RecordEndOfFrameMetrics(
         base::TimeTicks(), base::TimeTicks() + base::Microseconds(10), 0);
 
     target1->setAttribute(html_names::kStyleAttr, "width: 60px");
@@ -738,7 +738,7 @@
   }
 };
 
-TEST_F(LocalFrameUkmAggregatorSimTest, EnsureUkmAggregator) {
+TEST_F(LocalFrameUkmAggregatorSimTest, GetUkmAggregator) {
   SimRequest main_resource("https://example.com/", "text/html");
   SimRequest frame_resource("https://example.com/frame.html", "text/html");
   LoadURL("https://example.com/");
@@ -751,11 +751,11 @@
       To<HTMLFrameOwnerElement>(GetDocument().getElementById("frame"))
           ->contentDocument()
           ->View();
-  auto& aggregator_from_subframe = subframe_view->EnsureUkmAggregator();
-  auto& aggregator_from_root = root_view->EnsureUkmAggregator();
-  EXPECT_EQ(&aggregator_from_root, &aggregator_from_subframe);
-  EXPECT_EQ(&aggregator_from_root, &subframe_view->EnsureUkmAggregator());
-  EXPECT_EQ(&aggregator_from_root, &root_view->EnsureUkmAggregator());
+  auto* aggregator_from_subframe = subframe_view->GetUkmAggregator();
+  auto* aggregator_from_root = root_view->GetUkmAggregator();
+  EXPECT_EQ(aggregator_from_root, aggregator_from_subframe);
+  EXPECT_EQ(aggregator_from_root, subframe_view->GetUkmAggregator());
+  EXPECT_EQ(aggregator_from_root, root_view->GetUkmAggregator());
 }
 
 TEST_F(LocalFrameUkmAggregatorSimTest, IntersectionObserverCounts) {
@@ -946,4 +946,28 @@
   histogram_tester.ExpectTotalCount("Blink.MainFrame.UpdateTime.PostFCP", 1);
 }
 
+TEST_F(LocalFrameUkmAggregatorSimTest, SVGImageMetricsAreNotRecorded) {
+  base::HistogramTester histogram_tester;
+
+  WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(R"HTML(
+    <!doctype html>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'
+        fill='red' width='10' height='10'><path d='M0 0 L8 0 L4 7 Z'/></svg>">
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'
+        fill='green' width='10' height='10'><path d='M0 0 L8 0 L4 7 Z'/></svg>">
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'
+        fill='blue' width='10' height='10'><path d='M0 0 L8 0 L4 7 Z'/></svg>">
+  )HTML");
+
+  // Do a pre-FCP frame.
+  Compositor().BeginFrame();
+
+  // Metrics should only be reported for the root frame, not for each svg image.
+  histogram_tester.ExpectTotalCount("Blink.Style.UpdateTime.PreFCP", 1);
+  histogram_tester.ExpectTotalCount("Blink.MainFrame.UpdateTime.PreFCP", 1);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 55a3e36..9ec5eaa 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -934,16 +934,18 @@
   if (forced_layout_stack_depth_ > 1)
     return;
   forced_layout_start_time_ = base::TimeTicks::Now();
-  EnsureUkmAggregator().BeginForcedLayout();
+  if (auto* metrics_aggregator = GetUkmAggregator())
+    metrics_aggregator->BeginForcedLayout();
 }
 
 void LocalFrameView::DidFinishForcedLayout(DocumentUpdateReason reason) {
   CHECK_GT(forced_layout_stack_depth_, (unsigned)0);
   forced_layout_stack_depth_--;
   if (!forced_layout_stack_depth_ && base::TimeTicks::IsHighResolution()) {
-    LocalFrameUkmAggregator& aggregator = EnsureUkmAggregator();
-    aggregator.RecordForcedLayoutSample(reason, forced_layout_start_time_,
-                                        base::TimeTicks::Now());
+    if (auto* metrics_aggregator = GetUkmAggregator()) {
+      metrics_aggregator->RecordForcedLayoutSample(
+          reason, forced_layout_start_time_, base::TimeTicks::Now());
+    }
   }
 }
 
@@ -1082,7 +1084,7 @@
 
   TRACE_EVENT0("blink,benchmark",
                "LocalFrameView::UpdateViewportIntersectionsForSubtree");
-  SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
+  SCOPED_UMA_AND_UKM_TIMER(GetUkmAggregator(),
                            LocalFrameUkmAggregator::kIntersectionObservation);
 
   // Populating monotonic_time may be expensive, and may be unnecessary, so
@@ -1564,8 +1566,7 @@
 
   if (auto* controller =
           GetFrame().GetDocument()->GetIntersectionObserverController()) {
-    controller->ComputeIntersections(flags, EnsureUkmAggregator(),
-                                     monotonic_time);
+    controller->ComputeIntersections(flags, GetUkmAggregator(), monotonic_time);
   }
 
   for (Frame* child = frame_->Tree().FirstChild(); child;
@@ -2335,10 +2336,11 @@
   // Hit testing metrics include the entire time processing a document update
   // in preparation for a hit test.
   if (reason == DocumentUpdateReason::kHitTest) {
-    LocalFrameUkmAggregator& aggregator = EnsureUkmAggregator();
-    aggregator.RecordTimerSample(
-        static_cast<size_t>(LocalFrameUkmAggregator::kHitTestDocumentUpdate),
-        lifecycle_data_.start_time, base::TimeTicks::Now());
+    if (auto* metrics_aggregator = GetUkmAggregator()) {
+      metrics_aggregator->RecordTimerSample(
+          static_cast<size_t>(LocalFrameUkmAggregator::kHitTestDocumentUpdate),
+          lifecycle_data_.start_time, base::TimeTicks::Now());
+    }
   }
 
   return Lifecycle().GetState() == target_state;
@@ -2650,7 +2652,7 @@
   auto* layout_view = GetLayoutView();
   DCHECK(layout_view);
 
-  SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
+  SCOPED_UMA_AND_UKM_TIMER(GetUkmAggregator(),
                            LocalFrameUkmAggregator::kCompositingInputs);
   // TODO(pdr): This descendant dependent treewalk should be integrated into
   // the prepaint tree walk.
@@ -2730,7 +2732,7 @@
       kPostOrder);
 
   {
-    SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
+    SCOPED_UMA_AND_UKM_TIMER(GetUkmAggregator(),
                              LocalFrameUkmAggregator::kPrePaint);
 
     GetPage()->GetLinkHighlight().UpdateBeforePrePaint();
@@ -2845,7 +2847,7 @@
 void LocalFrameView::RunAccessibilitySteps() {
   TRACE_EVENT0("blink,benchmark", "LocalFrameView::RunAccessibilitySteps");
 
-  SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
+  SCOPED_UMA_AND_UKM_TIMER(GetUkmAggregator(),
                            LocalFrameUkmAggregator::kAccessibility);
 
   // Reduce redundant ancestor chain walking for display lock computations.
@@ -2904,8 +2906,7 @@
 
 bool LocalFrameView::PaintTree(PaintBenchmarkMode benchmark_mode,
                                PaintControllerCycleScope& cycle_scope) {
-  SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
-                           LocalFrameUkmAggregator::kPaint);
+  SCOPED_UMA_AND_UKM_TIMER(GetUkmAggregator(), LocalFrameUkmAggregator::kPaint);
 
   DCHECK(GetFrame().IsLocalRoot());
 
@@ -3065,7 +3066,7 @@
   paint_artifact_compositor_->SetPrefersLCDText(
       !page->GetSettings().GetPreferCompositingToLCDTextEnabled());
 
-  SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
+  SCOPED_UMA_AND_UKM_TIMER(GetUkmAggregator(),
                            LocalFrameUkmAggregator::kCompositingCommit);
 
   // Skip updating property trees, pushing cc::Layers, and issuing raster
@@ -3087,8 +3088,6 @@
   PaintArtifactCompositor::ViewportProperties viewport_properties;
   if (const auto& viewport = page->GetVisualViewport();
       GetFrame().IsMainFrame() && viewport.IsActiveViewport()) {
-    viewport_properties.overscroll_elasticity_effect =
-        viewport.GetOverscrollElasticityEffectNode();
     viewport_properties.overscroll_elasticity_transform =
         viewport.GetOverscrollElasticityTransformNode();
     viewport_properties.page_scale = viewport.GetPageScaleNode();
@@ -3298,7 +3297,7 @@
   }
 
   if (NeedsLayout()) {
-    SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
+    SCOPED_UMA_AND_UKM_TIMER(GetUkmAggregator(),
                              LocalFrameUkmAggregator::kLayout);
     UpdateLayout();
     return true;
@@ -4059,8 +4058,7 @@
                                              const CullRect& cull_rect) {
   DCHECK(PaintOutsideOfLifecycleIsAllowed(context, *this));
 
-  SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
-                           LocalFrameUkmAggregator::kPaint);
+  SCOPED_UMA_AND_UKM_TIMER(GetUkmAggregator(), LocalFrameUkmAggregator::kPaint);
 
   ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
     frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kInPaint);
@@ -4300,14 +4298,14 @@
     if (IntersectionObserverController* controller =
             GetFrame().GetDocument()->GetIntersectionObserverController()) {
       needs_occlusion_tracking |= controller->ComputeIntersections(
-          flags, EnsureUkmAggregator(), monotonic_time);
+          flags, GetUkmAggregator(), monotonic_time);
     }
     intersection_observation_state_ = kNotNeeded;
   }
 
   {
     SCOPED_UMA_AND_UKM_TIMER(
-        EnsureUkmAggregator(),
+        GetUkmAggregator(),
         LocalFrameUkmAggregator::kUpdateViewportIntersection);
     UpdateViewportIntersection(flags, needs_occlusion_tracking);
   }
@@ -4733,16 +4731,22 @@
   }
 }
 
-LocalFrameUkmAggregator& LocalFrameView::EnsureUkmAggregator() {
+LocalFrameUkmAggregator* LocalFrameView::GetUkmAggregator() {
   DCHECK(frame_->IsLocalRoot() || !ukm_aggregator_);
   LocalFrameView* local_root = frame_->LocalFrameRoot().View();
+
+  // TODO(crbug.com/1392462): Avoid checking whether we need to create the
+  // aggregator on every access.
   if (!local_root->ukm_aggregator_) {
-    local_root->ukm_aggregator_ = base::MakeRefCounted<LocalFrameUkmAggregator>(
-        local_root->frame_->GetDocument()->UkmSourceID(),
-        local_root->frame_->GetDocument()->UkmRecorder(),
-        local_root->frame_->IsMainFrame());
+    if (!local_root->frame_->GetChromeClient().IsSVGImageChromeClient()) {
+      local_root->ukm_aggregator_ =
+          base::MakeRefCounted<LocalFrameUkmAggregator>(
+              local_root->frame_->GetDocument()->UkmSourceID(),
+              local_root->frame_->GetDocument()->UkmRecorder(),
+              local_root->frame_->IsMainFrame());
+    }
   }
-  return *local_root->ukm_aggregator_;
+  return local_root->ukm_aggregator_.get();
 }
 
 void LocalFrameView::ResetUkmAggregatorForTesting() {
@@ -4758,7 +4762,8 @@
       FontPerformance::MarkFirstContentfulPaint();
   }
 
-  EnsureUkmAggregator().DidReachFirstContentfulPaint();
+  if (auto* metrics_aggregator = GetUkmAggregator())
+    metrics_aggregator->DidReachFirstContentfulPaint();
 }
 
 void LocalFrameView::RegisterForLifecycleNotifications(
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 4ef16aea..dac6f3c 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -709,8 +709,8 @@
   void RegisterTapEvent(Element* target);
 
   // Returns the UKM aggregator for this frame's local root, creating it if
-  // necessary.
-  LocalFrameUkmAggregator& EnsureUkmAggregator();
+  // necessary. Returns null if no aggregator is needed, such as for SVG images.
+  LocalFrameUkmAggregator* GetUkmAggregator();
   void ResetUkmAggregatorForTesting();
 
   // Report the First Contentful Paint signal to the LocalFrameView.
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.cc b/third_party/blink/renderer/core/frame/visual_viewport.cc
index 0c330127..191c29a 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport.cc
@@ -34,7 +34,6 @@
 
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
-#include "build/build_config.h"
 #include "cc/input/main_thread_scrolling_reason.h"
 #include "cc/layers/solid_color_scrollbar_layer.h"
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
@@ -72,7 +71,6 @@
 #include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/geometry/size_f.h"
@@ -84,13 +82,6 @@
 OverscrollType ComputeOverscrollType() {
   if (!Platform::Current()->IsElasticOverscrollEnabled())
     return OverscrollType::kNone;
-#if BUILDFLAG(IS_ANDROID)
-  if (base::GetFieldTrialParamValueByFeature(
-          ::features::kElasticOverscroll, ::features::kElasticOverscrollType) ==
-      ::features::kElasticOverscrollTypeFilter) {
-    return OverscrollType::kFilter;
-  }
-#endif
   return OverscrollType::kTransform;
 }
 
@@ -112,8 +103,6 @@
       unique_id, CompositorElementIdNamespace::kPrimary);
   scroll_element_id_ = CompositorElementIdFromUniqueObjectId(
       unique_id, CompositorElementIdNamespace::kScroll);
-  elasticity_effect_node_id_ = CompositorElementIdFromUniqueObjectId(
-      unique_id, CompositorElementIdNamespace::kEffectFilter);
   Reset();
 }
 
@@ -127,11 +116,6 @@
   return overscroll_elasticity_transform_node_.get();
 }
 
-const EffectPaintPropertyNode*
-VisualViewport::GetOverscrollElasticityEffectNode() const {
-  return overscroll_elasticity_effect_node_.get();
-}
-
 const TransformPaintPropertyNode* VisualViewport::GetPageScaleNode() const {
   return page_scale_node_.get();
 }
@@ -339,28 +323,6 @@
     }
   }
 
-#if BUILDFLAG(IS_ANDROID)
-  if (overscroll_type_ == OverscrollType::kFilter) {
-    bool needs_overscroll_effect_node = !MaximumScrollOffset().IsZero();
-    if (needs_overscroll_effect_node && !overscroll_elasticity_effect_node_) {
-      EffectPaintPropertyNode::State state;
-      state.output_clip = context.current.clip;
-      state.local_transform_space = transform_parent;
-      // The filter will be animated on the compositor in response to
-      // overscroll.
-      state.direct_compositing_reasons =
-          CompositingReason::kActiveFilterAnimation;
-      state.compositor_element_id = elasticity_effect_node_id_;
-      overscroll_elasticity_effect_node_ =
-          EffectPaintPropertyNode::Create(*effect_parent, std::move(state));
-    }
-    if (overscroll_elasticity_effect_node_) {
-      context.current_effect = effect_parent =
-          overscroll_elasticity_effect_node_.get();
-    }
-  }
-#endif
-
   if (scrollbar_layer_horizontal_) {
     EffectPaintPropertyNode::State state;
     state.local_transform_space = transform_parent;
diff --git a/third_party/blink/renderer/core/frame/visual_viewport.h b/third_party/blink/renderer/core/frame/visual_viewport.h
index 06950ba..68d1f97 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport.h
+++ b/third_party/blink/renderer/core/frame/visual_viewport.h
@@ -68,7 +68,7 @@
 class TransformPaintPropertyNode;
 struct PaintPropertyTreeBuilderFragmentContext;
 
-enum class OverscrollType { kNone, kTransform, kFilter };
+enum class OverscrollType { kNone, kTransform };
 
 // Represents the visual viewport the user is currently seeing the page through.
 // This class corresponds to the InnerViewport on the compositor. It is a
@@ -94,7 +94,6 @@
 //           +- scroll_translation_node_ (scroll: scroll_node_)
 // Effect tree:
 //  parent effect state
-//  +- overscroll_elasticity_effect_node_
 //  +- horizontal_scrollbar_effect_node_
 //  +- vertical_scrollbar_effect_node_
 //
@@ -273,7 +272,6 @@
   const TransformPaintPropertyNode* GetDeviceEmulationTransformNode() const;
   const TransformPaintPropertyNode* GetOverscrollElasticityTransformNode()
       const;
-  const EffectPaintPropertyNode* GetOverscrollElasticityEffectNode() const;
   const TransformPaintPropertyNode* GetPageScaleNode() const;
   const TransformPaintPropertyNode* GetScrollTranslationNode() const;
   const ScrollPaintPropertyNode* GetScrollNode() const;
@@ -367,7 +365,6 @@
   scoped_refptr<TransformPaintPropertyNode> page_scale_node_;
   scoped_refptr<TransformPaintPropertyNode> scroll_translation_node_;
   scoped_refptr<ScrollPaintPropertyNode> scroll_node_;
-  scoped_refptr<EffectPaintPropertyNode> overscroll_elasticity_effect_node_;
   scoped_refptr<EffectPaintPropertyNode> horizontal_scrollbar_effect_node_;
   scoped_refptr<EffectPaintPropertyNode> vertical_scrollbar_effect_node_;
 
@@ -401,8 +398,6 @@
   // For scrolling, on scroll_layer_, scroll_node_, and scroll element ids of
   // scrollbar layers.
   CompositorElementId scroll_element_id_;
-  // For overscroll elasticity.
-  CompositorElementId elasticity_effect_node_id_;
 
   bool needs_paint_property_update_;
 
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index fddd384..4a9f6a94 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -2305,10 +2305,6 @@
   UpdateAllLifecyclePhases();
   EXPECT_FALSE(visual_viewport.GetOverscrollElasticityTransformNode());
 
-  visual_viewport.SetOverscrollTypeForTesting(OverscrollType::kFilter);
-  UpdateAllLifecyclePhases();
-  EXPECT_FALSE(visual_viewport.GetOverscrollElasticityTransformNode());
-
   visual_viewport.SetOverscrollTypeForTesting(OverscrollType::kTransform);
   UpdateAllLifecyclePhases();
   EXPECT_TRUE(visual_viewport.GetOverscrollElasticityTransformNode());
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 01386e5b..5f0d8be9 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -2161,11 +2161,9 @@
 
   absl::optional<LocalFrameUkmAggregator::ScopedUkmHierarchicalTimer> ukm_timer;
   if (WidgetBase::ShouldRecordBeginMainFrameMetrics()) {
-    ukm_timer.emplace(LocalRootImpl()
-                          ->GetFrame()
-                          ->View()
-                          ->EnsureUkmAggregator()
-                          .GetScopedTimer(LocalFrameUkmAggregator::kAnimate));
+    ukm_timer.emplace(
+        LocalRootImpl()->GetFrame()->View()->GetUkmAggregator()->GetScopedTimer(
+            LocalFrameUkmAggregator::kAnimate));
   }
 
   GetPage()->Animate(last_frame_time);
@@ -2213,9 +2211,9 @@
   LocalRootImpl()
       ->GetFrame()
       ->View()
-      ->EnsureUkmAggregator()
-      .RecordImplCompositorSample(commit_compositor_frame_start_time_.value(),
-                                  commit_start_time, commit_finish_time);
+      ->GetUkmAggregator()
+      ->RecordImplCompositorSample(commit_compositor_frame_start_time_.value(),
+                                   commit_start_time, commit_finish_time);
   commit_compositor_frame_start_time_ =
       next_commit_compositor_frame_start_time_;
   next_commit_compositor_frame_start_time_.reset();
@@ -2264,13 +2262,9 @@
 void WebFrameWidgetImpl::RecordDispatchRafAlignedInputTime(
     base::TimeTicks raf_aligned_input_start_time) {
   if (LocalRootImpl()) {
-    LocalRootImpl()
-        ->GetFrame()
-        ->View()
-        ->EnsureUkmAggregator()
-        .RecordTimerSample(LocalFrameUkmAggregator::kHandleInputEvents,
-                           raf_aligned_input_start_time,
-                           base::TimeTicks::Now());
+    LocalRootImpl()->GetFrame()->View()->GetUkmAggregator()->RecordTimerSample(
+        LocalFrameUkmAggregator::kHandleInputEvents,
+        raf_aligned_input_start_time, base::TimeTicks::Now());
   }
 }
 
@@ -2310,8 +2304,8 @@
   return LocalRootImpl()
       ->GetFrame()
       ->View()
-      ->EnsureUkmAggregator()
-      .GetBeginMainFrameMetrics();
+      ->GetUkmAggregator()
+      ->GetBeginMainFrameMetrics();
 }
 
 std::unique_ptr<cc::WebVitalMetrics> WebFrameWidgetImpl::GetWebVitalMetrics() {
@@ -2359,13 +2353,9 @@
 void WebFrameWidgetImpl::EndUpdateLayers() {
   if (LocalRootImpl()) {
     DCHECK(update_layers_start_time_);
-    LocalRootImpl()
-        ->GetFrame()
-        ->View()
-        ->EnsureUkmAggregator()
-        .RecordTimerSample(LocalFrameUkmAggregator::kUpdateLayers,
-                           update_layers_start_time_.value(),
-                           base::TimeTicks::Now());
+    LocalRootImpl()->GetFrame()->View()->GetUkmAggregator()->RecordTimerSample(
+        LocalFrameUkmAggregator::kUpdateLayers,
+        update_layers_start_time_.value(), base::TimeTicks::Now());
     probe::LayerTreeDidChange(LocalRootImpl()->GetFrame());
   }
   update_layers_start_time_.reset();
@@ -2375,7 +2365,7 @@
   if (!LocalRootImpl())
     return;
 
-  LocalRootImpl()->GetFrame()->View()->EnsureUkmAggregator().BeginMainFrame();
+  LocalRootImpl()->GetFrame()->View()->GetUkmAggregator()->BeginMainFrame();
 }
 
 void WebFrameWidgetImpl::RecordEndOfFrameMetrics(
@@ -2387,9 +2377,9 @@
   LocalRootImpl()
       ->GetFrame()
       ->View()
-      ->EnsureUkmAggregator()
-      .RecordEndOfFrameMetrics(frame_begin_time, base::TimeTicks::Now(),
-                               trackers);
+      ->GetUkmAggregator()
+      ->RecordEndOfFrameMetrics(frame_begin_time, base::TimeTicks::Now(),
+                                trackers);
 }
 
 void WebFrameWidgetImpl::WillHandleGestureEvent(const WebGestureEvent& event,
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.cc
index e754d40..d33579c 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.cc
@@ -62,7 +62,7 @@
 
 bool IntersectionObserverController::ComputeIntersections(
     unsigned flags,
-    LocalFrameUkmAggregator& ukm_aggregator,
+    LocalFrameUkmAggregator* metrics_aggregator,
     absl::optional<base::TimeTicks>& monotonic_time) {
   needs_occlusion_tracking_ = false;
   if (!GetExecutionContext())
@@ -77,10 +77,13 @@
   int64_t internal_observation_count = 0;
   int64_t javascript_observation_count = 0;
   {
-    LocalFrameUkmAggregator::IterativeTimer ukm_timer(ukm_aggregator);
+    absl::optional<LocalFrameUkmAggregator::IterativeTimer> metrics_timer;
+    if (metrics_aggregator)
+      metrics_timer.emplace(*metrics_aggregator);
     for (auto& observer : observers_to_process) {
       if (observer->HasObservations()) {
-        ukm_timer.StartInterval(observer->GetUkmMetricId());
+        if (metrics_timer)
+          metrics_timer->StartInterval(observer->GetUkmMetricId());
         int64_t count = observer->ComputeIntersections(flags, monotonic_time);
         if (observer->IsInternal())
           internal_observation_count += count;
@@ -92,7 +95,8 @@
       }
     }
     for (auto& observation : observations_to_process) {
-      ukm_timer.StartInterval(observation->Observer()->GetUkmMetricId());
+      if (metrics_timer)
+        metrics_timer->StartInterval(observation->Observer()->GetUkmMetricId());
       int64_t count = observation->ComputeIntersection(flags, monotonic_time);
       if (observation->Observer()->IsInternal())
         internal_observation_count += count;
@@ -102,12 +106,14 @@
     }
   }
 
-  ukm_aggregator.RecordCountSample(
-      LocalFrameUkmAggregator::kIntersectionObservationInternalCount,
-      internal_observation_count);
-  ukm_aggregator.RecordCountSample(
-      LocalFrameUkmAggregator::kIntersectionObservationJavascriptCount,
-      javascript_observation_count);
+  if (metrics_aggregator) {
+    metrics_aggregator->RecordCountSample(
+        LocalFrameUkmAggregator::kIntersectionObservationInternalCount,
+        internal_observation_count);
+    metrics_aggregator->RecordCountSample(
+        LocalFrameUkmAggregator::kIntersectionObservationJavascriptCount,
+        javascript_observation_count);
+  }
 
   return needs_occlusion_tracking_;
 }
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h
index 7860afe..660bfe8 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h
@@ -38,7 +38,7 @@
   // communicates whether observer->trackVisibility() is true for any tracked
   // observer.
   bool ComputeIntersections(unsigned flags,
-                            LocalFrameUkmAggregator& ukm_aggregator,
+                            LocalFrameUkmAggregator* metrics_aggregator,
                             absl::optional<base::TimeTicks>& monotonic_time);
 
   // The second argument indicates whether the Element is a target of any
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
index c07508c..4d1c17b 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
@@ -842,9 +842,9 @@
   // Changing layout between root and target should invalidate.
   target1->parentElement()->SetInlineStyleProperty(CSSPropertyID::kMarginLeft,
                                                    "10px");
-  // Invalidation happens during compositing inputs update, so force it here.
-  GetDocument().View()->UpdateLifecycleToPrePaintClean(
-      DocumentUpdateReason::kTest);
+  // Invalidation happens during style recalc, so force it here.
+  GetDocument().EnsurePaintLocationDataValidForNode(
+      target1, DocumentUpdateReason::kTest);
   EXPECT_FALSE(observation1->CanUseCachedRectsForTesting());
 
   // Moving target2 out from the subscroller should allow it to cache rects.
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 77cc3f26..dfdc8d54 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -549,24 +549,14 @@
     }
     needs_descendant_dependent_flags_update_ = false;
 
-    if (needs_visual_overflow_recalc_) {
-      if (IsSelfPaintingLayer()) {
-        PhysicalRect old_visual_rect =
-            PhysicalVisualOverflowRectAllowingUnset(GetLayoutObject());
-        GetLayoutObject().RecalcVisualOverflow();
-        if (old_visual_rect != GetLayoutObject().PhysicalVisualOverflowRect()) {
-          MarkAncestorChainForFlagsUpdate(
-              kDoesNotNeedDescendantDependentUpdate);
-        }
-      }
-      if (base::FeatureList::IsEnabled(
-              features::kFastPathPaintPropertyUpdates)) {
-        GetLayoutObject().InvalidateIntersectionObserverCachedRects();
-        GetLayoutObject().GetFrameView()->SetIntersectionObservationState(
-            LocalFrameView::kDesired);
-      }
-      needs_visual_overflow_recalc_ = false;
+    if (IsSelfPaintingLayer() && needs_visual_overflow_recalc_) {
+      PhysicalRect old_visual_rect =
+          PhysicalVisualOverflowRectAllowingUnset(GetLayoutObject());
+      GetLayoutObject().RecalcVisualOverflow();
+      if (old_visual_rect != GetLayoutObject().PhysicalVisualOverflowRect())
+        MarkAncestorChainForFlagsUpdate(kDoesNotNeedDescendantDependentUpdate);
     }
+    needs_visual_overflow_recalc_ = false;
   }
 
   bool previously_has_visible_content = has_visible_content_;
@@ -818,15 +808,7 @@
   GetLayoutObject().InvalidateVisualOverflow();
 #endif
   needs_visual_overflow_recalc_ = true;
-  // |MarkAncestorChainForFlagsUpdate| will cause a paint property update which
-  // is only needed if visual overflow actually changes. To avoid this, only
-  // mark this as needing a descendant dependent flags update, which will
-  // cause a paint property update if needed (see:
-  // PaintLayer::UpdateDescendantDependentFlags).
-  if (base::FeatureList::IsEnabled(features::kFastPathPaintPropertyUpdates))
-    SetNeedsDescendantDependentFlagsUpdate();
-  else
-    MarkAncestorChainForFlagsUpdate();
+  MarkAncestorChainForFlagsUpdate();
 }
 
 bool PaintLayer::HasNonIsolatedDescendantWithBlendMode() const {
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 73e0b60..7fe53fc 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -137,11 +137,6 @@
   auto property_changed =
       visual_viewport.UpdatePaintPropertyNodesIfNeeded(context);
 
-  if (const EffectPaintPropertyNode* overscroll_elasticity_effect_node =
-          visual_viewport.GetOverscrollElasticityEffectNode()) {
-    context.current_effect = overscroll_elasticity_effect_node;
-  }
-
   context.current.transform = visual_viewport.GetScrollTranslationNode();
   context.absolute_position.transform =
       visual_viewport.GetScrollTranslationNode();
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc b/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc
index 3117ff78..b2e7f2a 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc
@@ -136,9 +136,7 @@
  public:
   static void AddVisualViewportProperties(
       const VisualViewport& visual_viewport,
-      PropertyTreePrinter<EffectPaintPropertyNodeOrAlias>& printer) {
-    printer.AddNode(visual_viewport.GetOverscrollElasticityEffectNode());
-  }
+      PropertyTreePrinter<EffectPaintPropertyNodeOrAlias>& printer) {}
 
   static void AddObjectPaintProperties(
       const ObjectPaintProperties& properties,
@@ -216,10 +214,6 @@
 void UpdateDebugNames(const VisualViewport& viewport) {
   if (auto* device_emulation_node = viewport.GetDeviceEmulationTransformNode())
     SetDebugName(device_emulation_node, "Device Emulation Node");
-  if (auto* overscroll_effect_node =
-          viewport.GetOverscrollElasticityEffectNode()) {
-    SetDebugName(overscroll_effect_node, "Overscroll Elasticity Effect Node");
-  }
   if (auto* overscroll_node = viewport.GetOverscrollElasticityTransformNode())
     SetDebugName(overscroll_node, "Overscroll Elasticity Node");
   SetDebugName(viewport.GetPageScaleNode(), "VisualViewport Scale Node");
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index 225252e..747a8e8 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -2047,40 +2047,4 @@
   EXPECT_NEAR(0.8, div_properties->Effect()->Opacity(), 0.001);
   EXPECT_EQ(200, div_properties->Transform()->Get2dTranslation().x());
 }
-
-TEST_P(PaintPropertyTreeUpdateTest,
-       DirectTransformUpdateSkipsPropertyTreeBuilderForAncestors) {
-  SetBodyInnerHTML(R"HTML(
-    <div id='positioned_ancestor' style="position: relative;">
-      <div id='dom_ancestor'>
-        <div id='div' style="transform:translateX(100px)"></div>
-      </div>
-    </div>
-  )HTML");
-
-  auto* div_properties = PaintPropertiesForElement("div");
-  ASSERT_TRUE(div_properties);
-  EXPECT_EQ(100, div_properties->Transform()->Get2dTranslation().x());
-  auto* div = GetDocument().getElementById("div");
-  EXPECT_FALSE(div->GetLayoutObject()->NeedsPaintPropertyUpdate());
-  auto* dom_ancestor = GetDocument().getElementById("dom_ancestor");
-  EXPECT_FALSE(dom_ancestor->GetLayoutObject()->NeedsPaintPropertyUpdate());
-  auto* positioned_ancestor =
-      GetDocument().getElementById("positioned_ancestor");
-  EXPECT_FALSE(
-      positioned_ancestor->GetLayoutObject()->NeedsPaintPropertyUpdate());
-
-  div->setAttribute(html_names::kStyleAttr, "transform: translateX(200px)");
-  GetDocument().View()->UpdateLifecycleToLayoutClean(
-      DocumentUpdateReason::kTest);
-
-  EXPECT_FALSE(div->GetLayoutObject()->NeedsPaintPropertyUpdate());
-  EXPECT_FALSE(dom_ancestor->GetLayoutObject()->NeedsPaintPropertyUpdate());
-  EXPECT_FALSE(
-      positioned_ancestor->GetLayoutObject()->NeedsPaintPropertyUpdate());
-
-  UpdateAllLifecyclePhasesExceptPaint();
-  EXPECT_EQ(200, div_properties->Transform()->Get2dTranslation().x());
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index f0e0d5b0..c5a8516 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -296,13 +296,18 @@
     InsertEntryIntoSortedList(entries_list, *first_contentful_paint_timing_);
   }
 
-  if (RuntimeEnabledFeatures::NavigationIdEnabled()) {
+  if (RuntimeEnabledFeatures::NavigationIdEnabled(GetExecutionContext())) {
     MergePerformanceEntryVectorIntoList(entries_list,
                                         back_forward_cache_restoration_buffer_);
   }
 
-  if (RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled())
+  if (RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled(
+          GetExecutionContext()) &&
+      soft_navigation_buffer_.size()) {
+    UseCounter::Count(GetExecutionContext(),
+                      WebFeature::kSoftNavigationHeuristics);
     MergePerformanceEntryVectorIntoList(entries_list, soft_navigation_buffer_);
+  }
 
   // Convert entries_list into a PerformanceEntryVector.
   PerformanceEntryVector entries;
@@ -411,13 +416,17 @@
       return visibility_state_buffer_;
 
     case PerformanceEntry::kBackForwardCacheRestoration:
-      if (RuntimeEnabledFeatures::NavigationIdEnabled())
+      if (RuntimeEnabledFeatures::NavigationIdEnabled(GetExecutionContext()))
         return back_forward_cache_restoration_buffer_;
       break;
 
     case PerformanceEntry::kSoftNavigation:
-      if (RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled())
+      if (RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled(
+              GetExecutionContext())) {
+        UseCounter::Count(GetExecutionContext(),
+                          WebFeature::kSoftNavigationHeuristics);
         return soft_navigation_buffer_;
+      }
       break;
 
     case PerformanceEntry::kInvalid:
diff --git a/third_party/blink/renderer/core/timing/performance_entry.cc b/third_party/blink/renderer/core/timing/performance_entry.cc
index e77d86d..1571f7327 100644
--- a/third_party/blink/renderer/core/timing/performance_entry.cc
+++ b/third_party/blink/renderer/core/timing/performance_entry.cc
@@ -165,7 +165,8 @@
   builder.AddString("entryType", entryType());
   builder.AddNumber("startTime", startTime());
   builder.AddNumber("duration", duration());
-  if (RuntimeEnabledFeatures::NavigationIdEnabled()) {
+  if (RuntimeEnabledFeatures::NavigationIdEnabled(
+          ExecutionContext::From(builder.GetScriptState()))) {
     builder.AddNumber("navigationId", navigationId());
   }
 }
diff --git a/third_party/blink/renderer/core/timing/performance_observer.cc b/third_party/blink/renderer/core/timing/performance_observer.cc
index d80d960..32fb6de 100644
--- a/third_party/blink/renderer/core/timing/performance_observer.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer.cc
@@ -61,7 +61,7 @@
   Vector<AtomicString> supportedEntryTypes;
   auto* execution_context = ExecutionContext::From(script_state);
   if (execution_context->IsWindow()) {
-    if (RuntimeEnabledFeatures::NavigationIdEnabled()) {
+    if (RuntimeEnabledFeatures::NavigationIdEnabled(execution_context)) {
       supportedEntryTypes.push_back(
           performance_entry_names::kBackForwardCacheRestoration);
     }
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
index d234fa58..ac0d6fa 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
@@ -22,9 +22,6 @@
 void LogToConsole(LocalFrame* frame,
                   mojom::blink::ConsoleMessageLevel level,
                   const String& message) {
-  if (!RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled()) {
-    return;
-  }
   if (!frame || !frame->IsMainFrame()) {
     return;
   }
@@ -32,6 +29,9 @@
   if (!window) {
     return;
   }
+  if (!RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled(window)) {
+    return;
+  }
   auto* console_message = MakeGarbageCollected<ConsoleMessage>(
       mojom::blink::ConsoleMessageSource::kJavaScript, level, message);
   window->AddConsoleMessage(console_message);
@@ -210,7 +210,7 @@
 void SoftNavigationHeuristics::ResetPaintsIfNeeded(LocalFrame* frame,
                                                    LocalDOMWindow* window) {
   if (!did_reset_paints_) {
-    if (RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled()) {
+    if (RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled(window)) {
       if (Document* document = window->document()) {
         PaintTiming::From(*document).ResetFirstPaintAndFCP();
       }
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index 464e730a..f074a847 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -669,15 +669,19 @@
 
 void WindowPerformance::AddSoftNavigationEntry(const AtomicString& name,
                                                base::TimeTicks timestamp) {
-  if (!RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled()) {
+  if (!RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled(
+          GetExecutionContext())) {
     return;
   }
   SoftNavigationEntry* entry = MakeGarbageCollected<SoftNavigationEntry>(
       name, MonotonicTimeToDOMHighResTimeStamp(timestamp),
       PerformanceEntry::GetNavigationId(GetExecutionContext()));
 
-  if (HasObserverFor(PerformanceEntry::kSoftNavigation))
+  if (HasObserverFor(PerformanceEntry::kSoftNavigation)) {
+    UseCounter::Count(GetExecutionContext(),
+                      WebFeature::kSoftNavigationHeuristics);
     NotifyObserversOfEntry(*entry);
+  }
 
   AddSoftNavigationToPerformanceTimeline(entry);
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc
index 6e7faad..7efd7ea6f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list_popup.cc
@@ -168,8 +168,8 @@
   AXObjectCacheImpl& cache = AXObjectCache();
   AXObject* descendant = ActiveDescendant();
   cache.PostNotification(this, ax::mojom::Event::kHide);
-  if (descendant)
-    cache.PostNotification(this, ax::mojom::Event::kChildrenChanged);
+  if (descendant)  // TODO(accessibility) Try removing. Line below is enough.
+    cache.MarkAXObjectDirtyWithCleanLayout(this);
   cache.MarkAXSubtreeDirtyWithCleanLayout(ParentObject());
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 8772a06..c531832 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -4753,8 +4753,7 @@
   DCHECK(!IsDetached()) << "None of the above should be able to detach |this|: "
                         << ToString(true, true);
 
-  AXObjectCache().PostNotification(this,
-                                   ax::mojom::blink::Event::kChildrenChanged);
+  AXObjectCache().MarkAXObjectDirtyWithCleanLayout(this);
 }
 
 void AXNodeObject::SelectedOptions(AXObjectVector& options) const {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 0e63c15..b184802 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -2470,6 +2470,7 @@
 
   if (obj) {
     obj->ChildrenChangedWithCleanLayout();
+    modification_count_++;
     // TODO(accessibility) Only needed for <select> size changes.
     // This can turn into a DCHECK if the shadow DOM is used for <select>
     // elements instead of AXMenuList* and AXListBox* classes.
@@ -3600,7 +3601,7 @@
     DCHECK(!obj->IsDetached());
     if (!obj->NeedsToUpdateChildren()) {
       obj->SetNeedsToUpdateChildren();
-      PostNotification(obj, ax::mojom::blink::Event::kChildrenChanged);
+      MarkAXObjectDirty(obj);
     }
   }
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index 63ef84b..c4f0116 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -680,8 +680,9 @@
   DCHECK(child);
   if (child->IsDetached())
     return;
-  if (AXObject* new_parent = object_cache_->RestoreParentOrPrune(child))
-    ChildrenChanged(new_parent);
+  if (AXObject* new_parent = object_cache_->RestoreParentOrPrune(child)) {
+    object_cache_->ChildrenChanged(new_parent);
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc
index dcee88c4..be4c43a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_virtual_object.cc
@@ -69,7 +69,7 @@
 
 void AXVirtualObject::ChildrenChangedWithCleanLayout() {
   ClearChildren();
-  AXObjectCache().PostNotification(this, ax::mojom::Event::kChildrenChanged);
+  AXObjectCache().MarkAXObjectDirtyWithCleanLayout(this);
 }
 
 const AtomicString& AXVirtualObject::GetAOMPropertyOrARIAAttribute(
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
index 3a177a4..893dd5d 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
@@ -201,7 +201,7 @@
   // encapsulate an external mailbox, synctoken and release callback.
   SkImageInfo resource_info = SkImageInfo::Make(
       transferable_resource.size.width(), transferable_resource.size.height(),
-      viz::ResourceFormatToClosestSkColorType(
+      viz::ToClosestSkColorType(
           /*gpu_compositing=*/true, transferable_resource.format),
       kPremul_SkAlphaType);
   auto canvas_resource = ExternalCanvasResource::Create(
@@ -251,7 +251,7 @@
   const auto& sk_image_sync_token =
       transferable_resource.mailbox_holder.sync_token;
 
-  auto sk_color_type = viz::ResourceFormatToClosestSkColorType(
+  auto sk_color_type = viz::ToClosestSkColorType(
       /*gpu_compositing=*/true, transferable_resource.format);
 
   const SkImageInfo sk_image_info = SkImageInfo::Make(
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index d262619..ea5e1d7 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -8,6 +8,7 @@
 import("//build/config/compiler/compiler.gni")
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
+import("//build/nocompile.gni")
 import("//media/media_options.gni")
 import("//skia/features.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
@@ -2684,3 +2685,20 @@
   ]
   public_deps = [ "//gpu:raster" ]
 }
+
+if (enable_nocompile_tests) {
+  nocompile_test("blink_platform_nocompile_tests") {
+    sources = [ "wtf/functional_test.nc" ]
+
+    deps = [
+      ":platform",
+      "//base",
+      "//base/test:run_all_unittests",
+      "//testing/gtest",
+    ]
+
+    # Since the nocompile test is not able to inspect the contents of
+    # include_dirs, we need to specify the v8 include dir manually.
+    include_dirs = [ "//v8/include" ]
+  }
+}
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 69e84822..9c665df 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -613,10 +613,6 @@
         property_tree_manager.EnsureCompositorTransformNode(
             *properties.overscroll_elasticity_transform);
   }
-  if (properties.overscroll_elasticity_effect) {
-    ids.overscroll_elasticity_effect =
-        properties.overscroll_elasticity_effect->GetCompositorElementId();
-  }
   if (properties.page_scale) {
     ids.page_scale_transform =
         property_tree_manager.EnsureCompositorPageScaleTransformNode(
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
index f4ff1f9..5f6887e 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -147,7 +147,6 @@
 
   struct ViewportProperties {
     const TransformPaintPropertyNode* overscroll_elasticity_transform = nullptr;
-    const EffectPaintPropertyNode* overscroll_elasticity_effect = nullptr;
     const TransformPaintPropertyNode* page_scale = nullptr;
     const TransformPaintPropertyNode* inner_scroll_translation = nullptr;
     const ClipPaintPropertyNode* outer_clip = nullptr;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 15523ef..6e2a24d8 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -701,7 +701,7 @@
   const auto& sk_image_sync_token =
       transferable_resource.mailbox_holder.sync_token;
 
-  auto sk_color_type = viz::ResourceFormatToClosestSkColorType(
+  auto sk_color_type = viz::ToClosestSkColorType(
       /*gpu_compositing=*/true, transferable_resource.format);
 
   const SkImageInfo sk_image_info = SkImageInfo::Make(
diff --git a/third_party/blink/renderer/platform/heap/DEPS b/third_party/blink/renderer/platform/heap/DEPS
index 37e3142c..fc87de7 100644
--- a/third_party/blink/renderer/platform/heap/DEPS
+++ b/third_party/blink/renderer/platform/heap/DEPS
@@ -8,6 +8,7 @@
     # Dependencies.
     "+base/atomicops.h",
     "+base/bits.h",
+    "+base/functional/disallow_unretained.h",
     "+base/sampling_heap_profiler/poisson_allocation_sampler.h",
     "+base/strings/string_number_conversions.h",
     "+base/synchronization/lock.h",
diff --git a/third_party/blink/renderer/platform/heap/garbage_collected.h b/third_party/blink/renderer/platform/heap/garbage_collected.h
index b24f9b3..52cd802 100644
--- a/third_party/blink/renderer/platform/heap/garbage_collected.h
+++ b/third_party/blink/renderer/platform/heap/garbage_collected.h
@@ -5,6 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_GARBAGE_COLLECTED_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_GARBAGE_COLLECTED_H_
 
+#include <type_traits>
+
+#include "base/functional/disallow_unretained.h"
 #include "third_party/blink/renderer/platform/heap/thread_state_storage.h"
 #include "v8/include/cppgc/allocation.h"
 #include "v8/include/cppgc/garbage-collected.h"
@@ -52,4 +55,18 @@
 
 }  // namespace blink
 
+namespace base::internal {
+
+// Do not copy this code. Chromium code should just use DISALLOW_UNRETAINED()
+// directly. This is needed because v8 lives outside the Chromium repository and
+// does not want to even indirectly rely on //base concepts.
+template <typename T>
+struct TypeSupportsUnretained<
+    T,
+    std::enable_if_t<cppgc::IsGarbageCollectedOrMixinTypeV<T>>> {
+  static constexpr inline bool kValue = false;
+};
+
+}  // namespace base::internal
+
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_GARBAGE_COLLECTED_H_
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 09de8f1..996f5cd8 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1826,6 +1826,7 @@
       name: "NavigationId",
       status: "experimental",
       base_feature: "NavigationId",
+      origin_trial_feature_name: "SoftNavigationHeuristics",
     },
     {
       name: "NavigatorContentUtils",
@@ -2563,6 +2564,7 @@
       status: "experimental",
       depends_on: ["NavigationId"],
       base_feature: "SoftNavigationHeuristics",
+      origin_trial_feature_name: "SoftNavigationHeuristics",
     },
     // An origin trial feature name is required for this, even though it's
     // actually enabled by the more specific trial. Having these as separate
diff --git a/third_party/blink/renderer/platform/wtf/functional_test.nc b/third_party/blink/renderer/platform/wtf/functional_test.nc
new file mode 100644
index 0000000..fc5e948ea
--- /dev/null
+++ b/third_party/blink/renderer/platform/wtf/functional_test.nc
@@ -0,0 +1,137 @@
+// 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.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include <utility>
+
+#include "base/functional/bind.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+// Classes deriving from Oilpan's garbage collected types should not be usable
+// with base::Unretained.
+class UnretainableObject : public GarbageCollected<UnretainableObject> {
+};
+
+class UnretainableMixin : public GarbageCollectedMixin {
+};
+
+class UnretainableImpl : public GarbageCollected<UnretainableImpl>, public UnretainableMixin {
+};
+
+#if defined(NCTEST_BASE_BIND_UNRETAINED_OBJECT)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableObject* ptr) {
+  base::BindOnce([] (UnretainableObject* ptr) {}, base::Unretained(ptr));
+}
+
+#elif defined(NCTEST_WTF_BIND_UNRETAINED_OBJECT)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableObject* ptr) {
+  WTF::BindOnce([] (UnretainableObject* ptr) {}, base::Unretained(ptr));
+}
+
+#elif defined(NCTEST_BASE_BIND_UNRETAINED_MIXIN)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableMixin* ptr) {
+  base::BindOnce([] (UnretainableMixin* ptr) {}, base::Unretained(ptr));
+}
+
+#elif defined(NCTEST_WTF_BIND_UNRETAINED_MIXIN)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableMixin* ptr) {
+  WTF::BindOnce([] (UnretainableMixin* ptr) {}, base::Unretained(ptr));
+}
+
+#elif defined(NCTEST_BASE_BIND_UNRETAINED_IMPL)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableImpl* ptr) {
+  base::BindOnce([] (UnretainableImpl* ptr) {}, base::Unretained(ptr));
+}
+
+#elif defined(NCTEST_WTF_BIND_UNRETAINED_IMPL)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableImpl* ptr) {
+  WTF::BindOnce([] (UnretainableImpl* ptr) {}, base::Unretained(ptr));
+}
+
+#elif defined(NCTEST_BASE_BIND_PTR_OBJECT)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableObject* ptr) {
+  base::BindOnce([] (UnretainableObject* ptr) {}, ptr);
+}
+
+#elif defined(NCTEST_WTF_BIND_PTR_OBJECT)  // [r"fatal error: .+ Raw pointers are not allowed to bind into WTF::Function\. .+"]
+
+void WontCompile(UnretainableObject* ptr) {
+  WTF::BindOnce([] (UnretainableObject* ptr) {}, ptr);
+}
+
+#elif defined(NCTEST_BASE_BIND_PTR_MIXIN)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableMixin* ptr) {
+  base::BindOnce([] (UnretainableMixin* ptr) {}, ptr);
+}
+
+#elif defined(NCTEST_WTF_BIND_PTR_MIXIN)  // [r"fatal error: .+ Raw pointers are not allowed to bind into WTF::Function\. .+"]
+
+void WontCompile(UnretainableMixin* ptr) {
+  WTF::BindOnce([] (UnretainableMixin* ptr) {}, ptr);
+}
+
+#elif defined(NCTEST_BASE_BIND_PTR_IMPL)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableImpl* ptr) {
+  base::BindOnce([] (UnretainableImpl* ptr) {}, ptr);
+}
+
+#elif defined(NCTEST_WTF_BIND_PTR_IMPL)  // [r"fatal error: .+ Raw pointers are not allowed to bind into WTF::Function\. .+"]
+
+void WontCompile(UnretainableImpl* ptr) {
+  WTF::BindOnce([] (UnretainableImpl* ptr) {}, ptr);
+}
+
+#elif defined(NCTEST_BASE_BIND_CONST_REF_OBJECT)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableObject& ref) {
+  base::BindOnce([] (const UnretainableObject& ref) {}, std::cref(ref));
+}
+
+#elif defined(NCTEST_WTF_BIND_CONST_REF_OBJECT)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableObject& ref) {
+  WTF::BindOnce([] (const UnretainableObject& ref) {}, std::cref(ref));
+}
+
+#elif defined(NCTEST_BASE_BIND_CONST_REF_MIXIN)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableMixin& ref) {
+  base::BindOnce([] (const UnretainableMixin& ref) {}, std::cref(ref));
+}
+
+#elif defined(NCTEST_WTF_BIND_CONST_REF_MIXIN)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableMixin& ref) {
+  WTF::BindOnce([] (const UnretainableMixin& ref) {}, std::cref(ref));
+}
+
+#elif defined(NCTEST_BASE_BIND_CONST_REF_IMPL)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableImpl& ref) {
+  base::BindOnce([] (const UnretainableImpl& ref) {}, std::cref(ref));
+}
+
+#elif defined(NCTEST_WTF_BIND_CONST_REF_IMPL)  // [r"fatal error: static assertion failed due to requirement 'TypeSupportsUnretainedV"]
+
+void WontCompile(UnretainableImpl& ref) {
+  WTF::BindOnce([] (const UnretainableImpl& ref) {}, std::cref(ref));
+}
+
+#endif
+
+}  // namespace blink
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
index 2bbf1169..7209a5c 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor.py
@@ -25,6 +25,8 @@
 
 path_finder.bootstrap_wpt_imports()
 from wptrunner import manifestexpected, wptmanifest
+from wptrunner.wptmanifest import node as wptnode
+from wptrunner.wptmanifest.backends.base import ManifestItem
 
 _log = logging.getLogger(__name__)
 
@@ -34,6 +36,43 @@
     return test_name if index == -1 else test_name[:index]
 
 
+def update_with_static_expectations(test_or_subtest: ManifestItem):
+    """Update a (sub)test's metadata with evaluated expectations.
+
+    wptrunner manages test expectations with a high-level API (i.e.,
+    manifestexpected) calling low-level ones dealing with the abstract syntax
+    tree (i.e., wptmanifest). This function transfers the expectations evaluated
+    against run info from the high-level `TestNode` object to the low-level AST.
+
+    Note:
+        This function destructively modifies the AST.
+    """
+    if test_or_subtest.node:
+        for child_node in test_or_subtest.node.children:
+            if (isinstance(child_node, wptnode.KeyValueNode)
+                    and child_node.data == 'expected'):
+                # Overwrite any branches or default values with the statically
+                # evaluated expectation.
+                for status_or_condition in list(child_node.children):
+                    status_or_condition.remove()
+                try:
+                    statuses = test_or_subtest.get('expected')
+                except KeyError:
+                    # Remove the `expected` key with no value
+                    child_node.remove()
+                    continue
+                if isinstance(statuses, str):
+                    child_node.append(wptnode.ValueNode(statuses))
+                else:
+                    assert isinstance(statuses, list)
+                    statuses_node = wptnode.ListNode()
+                    for status in statuses:
+                        statuses_node.append(wptnode.ValueNode(status))
+                    child_node.append(statuses_node)
+    for child_item in test_or_subtest.iterchildren():
+        update_with_static_expectations(child_item)
+
+
 class WPTResultsProcessor:
     def __init__(self,
                  host,
@@ -276,6 +315,7 @@
         test_manifest = manifest.get_test('/' + test_name)
         if not test_manifest:
             raise ValueError('test ID does not exist')
+        update_with_static_expectations(test_manifest)
         return wptmanifest.serialize(test_manifest.node)
 
     def _write_text_results(self, test_name: str, artifacts: Artifacts,
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py
index 8f7f9a5..f240cc8 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_results_processor_unittest.py
@@ -666,14 +666,18 @@
         # Also create a checked-in metadata file for this test
         checked_in_metadata = textwrap.dedent("""\
             [test.html]
-              expected: OK
+              expected:
+                if flag_specific != "highdpi": OK
               [Assert something]
-                expected: FAIL
+                expected:
+                  if flag_specific != "highdpi": PASS
+                  [FAIL, TIMEOUT]
             """)
         self.fs.write_text_file(
             self.fs.join(self.processor.web_tests_dir, 'external', 'wpt',
                          'test.html.ini'), checked_in_metadata)
 
+        self.processor.run_info['flag_specific'] = 'highdpi'
         with self.fs.patch_builtins():
             self.processor.process_wpt_results(OUTPUT_JSON_FILENAME)
         artifacts_subdir = self.fs.join(self.processor.artifacts_dir)
@@ -687,10 +691,15 @@
                     expected: CRASH
                 """), self.fs.read_text_file(actual_path))
 
-        # The checked-in metadata file gets renamed from .ini to -expected.txt
+        # The checked-in metadata file gets renamed from .ini to -expected.txt.
+        # Any conditions are also evaluated against the test run's properties.
         expected_path = self.fs.join(artifacts_subdir, 'test-expected.txt')
-        self.assertEqual(checked_in_metadata,
-                         self.fs.read_text_file(expected_path))
+        self.assertEqual(
+            textwrap.dedent("""\
+                [test.html]
+                  [Assert something]
+                    expected: [FAIL, TIMEOUT]
+                """), self.fs.read_text_file(expected_path))
 
         # Ensure the artifacts in the json were replaced with the locations of
         # the newly-created files.
@@ -710,7 +719,7 @@
         # validate the entire diff files to avoid checking line numbers/markup.
         diff_lines = self.fs.read_text_file(
             self.fs.join(artifacts_subdir, 'test-diff.txt')).splitlines()
-        self.assertIn('-    expected: FAIL', diff_lines)
+        self.assertIn('-    expected: [FAIL, TIMEOUT]', diff_lines)
         self.assertIn('+    expected: CRASH', diff_lines)
         self.assertEqual(
             [self.fs.join(path_from_out_dir_base, 'test-diff.txt')],
@@ -718,7 +727,7 @@
 
         pretty_diff_contents = self.fs.read_text_file(
             self.fs.join(artifacts_subdir, 'test-pretty-diff.html'))
-        self.assertIn('expected: FAIL', pretty_diff_contents)
+        self.assertIn('expected: [FAIL, TIMEOUT]', pretty_diff_contents)
         self.assertIn('expected: CRASH', pretty_diff_contents)
         self.assertEqual(
             [self.fs.join(path_from_out_dir_base, 'test-pretty-diff.html')],
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index bb650009..26a93d1 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3022,6 +3022,14 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac11 ] external/wpt/credential-management/fedcm-network-requests.https.html [ Timeout ]
+crbug.com/626703 external/wpt/css/css-writing-modes/forms/textarea-appearance-native-horizontal.optional.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-writing-modes/forms/textarea-appearance-native-vlr.optional.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-writing-modes/forms/textarea-appearance-native-vrl.optional.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-writing-modes/forms/textarea-appearance-none-horizontal.optional.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-writing-modes/forms/textarea-appearance-none-vlr.optional.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-writing-modes/forms/textarea-appearance-none-vrl.optional.html [ Failure ]
+crbug.com/626703 [ Win11 ] external/wpt/screen-orientation/hidden_document.html [ Timeout Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/forms/color-input-appearance-native-horizontal.optional.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/forms/color-input-appearance-native-vertical.optional.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/forms/color-input-appearance-none-horizontal.optional.html [ Failure ]
@@ -6379,6 +6387,9 @@
 crbug.com/1386214 [ Win ] external/wpt/html/semantics/forms/the-input-element/show-picker-user-gesture.html [ Skip Timeout ]
 crbug.com/1392516 [ Win ] pointer-lock/wheel-event-cancelable.html [ Timeout ]
 crbug.com/1392517 [ Debug Linux ] http/tests/serviceworker/postmessage-after-terminate.html [ Failure ]
+crbug.com/1392781 [ Win ] http/tests/fetch/serviceworker-proxied/thorough/auth-base-https-other-https.html [ Crash Failure Pass Timeout ]
+crbug.com/1392781 [ Win ] http/tests/fetch/serviceworker/thorough/cookie-base-https-other-https.html [ Crash Failure Pass Timeout ]
+crbug.com/1392781 [ Win ] http/tests/fetch/window/thorough/auth-base-https-other-https.html [ Crash Failure Pass Timeout ]
 
 # Sheriff 2022-10-07
 crbug.com/1372556 [ Linux ] external/wpt/css/css-text/text-transform/text-transform-capitalize-* [ Failure Pass ]
@@ -6901,7 +6912,7 @@
 # Sheriff 2022-11-23
 crbug.com/1393013 [ Win11 ] http/tests/persistent-origin-trials/critical-origin-trial.https.php [ Pass Failure ]
 crbug.com/1393013 [ Win11 ] http/tests/persistent-origin-trials/window-origin-trial.https.php [ Pass Failure ]
-crbug.com/1393067 [ Mac ] virtual/gpu-rasterization/images/182.html [ Failure ]
-crbug.com/1393067 [ Mac ] virtual/gpu-rasterization/images/23-55.html [ Failure ]
-crbug.com/1393067 [ Mac ] virtual/gpu-rasterization/images/55.html [ Failure ]
-crbug.com/1393056 [ Mac ] inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page.js [ Failure ]
+crbug.com/1393067 [ Mac ] virtual/gpu-rasterization/images/182.html [ Pass Failure ]
+crbug.com/1393067 [ Mac ] virtual/gpu-rasterization/images/23-55.html [ Pass Failure ]
+crbug.com/1393067 [ Mac ] virtual/gpu-rasterization/images/55.html [ Pass Failure ]
+crbug.com/1393056 [ Mac ] inspector-protocol/debugger/debugger-evaluate-in-worker-while-pause-in-page.js [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/accessibility/aria-owns-sends-notification.html b/third_party/blink/web_tests/accessibility/aria-owns-sends-notification.html
index eaf63d69..b51a093 100644
--- a/third_party/blink/web_tests/accessibility/aria-owns-sends-notification.html
+++ b/third_party/blink/web_tests/accessibility/aria-owns-sends-notification.html
@@ -13,7 +13,6 @@
       assert_equals(axFutureParent.childrenCount, 0);
 
       var expected_notifications = new Set();
-      expected_notifications.add("ChildrenChanged");
       expected_notifications.add("MarkDirty");
 
       while (expected_notifications.size) {
@@ -35,7 +34,7 @@
     var child = document.createElement("li");
     child.id = "future_child";
     futureParent.parentElement.appendChild(child);
-}, "A children changed notification is fired when an aria-owned child gets added to the tree.");
+}, "A mark dirty notification is fired when an aria-owned child gets added to the tree.");
 </script>
 
 <div id="container2">
@@ -54,7 +53,7 @@
     var axList1 = accessibilityController.accessibleElementById("list1");
     assert_equals(axList1.childrenCount, 0, "No children before setting aria-owns");
     var listener = t.step_func((notification) => {
-        assert_equals(notification, "ChildrenChanged");
+        assert_equals(notification, "MarkDirty");
         assert_equals(axList1.childrenCount, 1, "One child after setting aria-owns");
 
         axList1.removeNotificationListener(listener);
@@ -64,7 +63,7 @@
 
     var list1 = document.getElementById("list1");
     list1.setAttribute("aria-owns", "item1");
-}, "A children changed notification is fired when an aria-owned attribute is added to an element that causes it to reparent another element to become its child.");
+}, "A mark dirty notification is fired when an aria-owned attribute is added to an element that causes it to reparent another element to become its child.");
 </script>
 
 <div id="container3">
@@ -85,7 +84,7 @@
     var axList2 = accessibilityController.accessibleElementById("newlist2");
     assert_equals(axList2.childrenCount, 1);
     var listener = t.step_func((notification) => {
-        assert_equals(notification, "ChildrenChanged");
+        assert_equals(notification, "MarkDirty");
         assert_equals(axList2.childrenCount, 0);
         assert_equals(axList1.childrenCount, 1);
 
@@ -97,5 +96,5 @@
     list1.setAttribute("aria-owns", "newitem1");
     assert_equals(axList1.childrenCount, 1);
     assert_equals(axList2.childrenCount, 0);
-}, "A children changed notification is fired on the old parent when one of its children gets reparented to another element due to aria-owns.");
+}, "A mark dirty notification is fired on the old parent when one of its children gets reparented to another element due to aria-owns.");
 </script>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index a6cd619..17bb93d 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -226042,6 +226042,156 @@
         ],
         {}
        ]
+      ],
+      "textarea-appearance-native-horizontal.optional.html": [
+       "79c54d5063dbe9aec30795b9d2a102c83eec902a",
+       [
+        null,
+        [
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-vlr.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-vlr.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-vrl.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-vrl.optional.html",
+          "!="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "textarea-appearance-native-vlr.optional.html": [
+       "3ce811fd69286750b835d1e69e39b1c60c2edb12",
+       [
+        null,
+        [
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-horizontal.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-horizontal.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-vrl.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-vrl.optional.html",
+          "!="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "textarea-appearance-native-vrl.optional.html": [
+       "c5f8b4c3112addea3dc346e416136e328951a2cc",
+       [
+        null,
+        [
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-horizontal.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-horizontal.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-vlr.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-vlr.optional.html",
+          "!="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "textarea-appearance-none-horizontal.optional.html": [
+       "be4dde195ead643bb38c616d7461463cfeadd55d",
+       [
+        null,
+        [
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-vlr.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-vlr.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-vrl.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-vrl.optional.html",
+          "!="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "textarea-appearance-none-vlr.optional.html": [
+       "d5992962e883416049480f6973cc8693c6c24997",
+       [
+        null,
+        [
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-horizontal.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-horizontal.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-vrl.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-vrl.optional.html",
+          "!="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "textarea-appearance-none-vrl.optional.html": [
+       "cbaddc3a0d140b855caa3efe7ea0e035b7cca3a0",
+       [
+        null,
+        [
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-horizontal.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-horizontal.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-none-vlr.optional.html",
+          "!="
+         ],
+         [
+          "/css/css-writing-modes/forms/textarea-appearance-native-vlr.optional.html",
+          "!="
+         ]
+        ],
+        {}
+       ]
       ]
      },
      "height-width-inline-non-replaced-vlr-003.xht": [
@@ -260164,8 +260314,12 @@
      "8195095f4bc779a9854430e1f5acb9ea5bde8c72",
      []
     ],
-    "battery-allowed-by-feature-policy.https.sub.html.headers": [
-     "03b98040cc4d2e5cd352584b69ffd09ca1cb308c",
+    "battery-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub-expected.txt": [
+     "50daf927119b54b82091ae68c5f17a200e753ee9",
+     []
+    ],
+    "battery-allowed-by-permissions-policy.https.sub.html.headers": [
+     "989fdc3df8290abf2af65c67106e009e205c25eb",
      []
     ],
     "battery-default-feature-policy.https.sub-expected.txt": [
@@ -260176,18 +260330,26 @@
      "f450813bc38e4e1e7ed88c9563e2f0cbbeee7ade",
      []
     ],
+    "battery-default-permissions-policy.https.sub-expected.txt": [
+     "b7c8ccfb3b0c9c9fca1832c1fc9b9cc06cd4417a",
+     []
+    ],
     "battery-disabled-by-feature-policy.https.sub-expected.txt": [
      "8cf8e32cc55110598c75ddc1f8a355907c6782e5",
      []
     ],
-    "battery-disabled-by-feature-policy.https.sub.html.headers": [
-     "09a5ae84a123aac7591b20f78ffeea1cbd279a7c",
-     []
-    ],
     "battery-disabled-by-feature-policy.https.sub.html.ini": [
      "6f6c5ba4ceca46e3ae99c49e519d53063b041b45",
      []
     ],
+    "battery-disabled-by-permissions-policy.https.sub-expected.txt": [
+     "8a9a240e900397dd9f1ef666c1e461b49e36ca1b",
+     []
+    ],
+    "battery-disabled-by-permissions-policy.https.sub.html.headers": [
+     "9ff0bc6190a43f41e60f53e40e18c649662f8f2a",
+     []
+    ],
     "battery-disallowed-in-cross-origin-iframe.https.sub-expected.txt": [
      "0d7a2cc743f2b4843586adee3a3e97829398ca07",
      []
@@ -316767,6 +316929,28 @@
       "efb9f9ffd43a66532c410d46cc8a4b7adac96b72",
       []
      ],
+     "forms": {
+      "manual": {
+       "support": {
+        "form-controls-slr.png": [
+         "ac9b2a307255fcf7b7b70dbfe0c5a358e6642ff9",
+         []
+        ],
+        "form-controls-srl.png": [
+         "da9d401f917269f152a78f85b5a25229269eaee6",
+         []
+        ],
+        "form-controls-vlr.png": [
+         "ebe4df21f63a4cb1cbfb022cfe8be43b65d974c6",
+         []
+        ],
+        "vertical-form.png": [
+         "53a3af92cd650450b04b9d4f43b28e35ee95dc53",
+         []
+        ]
+       }
+      }
+     },
      "horizontal-rule-vlr-003-ref.xht": [
       "24130d157837083bf97eeff3420f58693f1daf27",
       []
@@ -318238,18 +318422,6 @@
        "4e67786f13b9dae59f366745452c6904b13a4f4f",
        []
       ],
-      "form-controls-slr.png": [
-       "ac9b2a307255fcf7b7b70dbfe0c5a358e6642ff9",
-       []
-      ],
-      "form-controls-srl.png": [
-       "da9d401f917269f152a78f85b5a25229269eaee6",
-       []
-      ],
-      "form-controls-vlr.png": [
-       "ebe4df21f63a4cb1cbfb022cfe8be43b65d974c6",
-       []
-      ],
       "left-bottom-200x300.png": [
        "6e30eba507a77ee266a7632ca657978fca15e707",
        []
@@ -318558,10 +318730,6 @@
        "fb6eb7d60de09c8c7bc55c06369b1ad76d80e8e7",
        []
       ],
-      "vertical-form.png": [
-       "53a3af92cd650450b04b9d4f43b28e35ee95dc53",
-       []
-      ],
       "width-test.css": [
        "4f15976ccc9629b26df2a8523006fb4f3e3daf99",
        []
@@ -327947,10 +328115,6 @@
       "79f8eefb9d619c3f54524fd53649cf03d66713b3",
       []
      ],
-     "feature-policy-battery.html": [
-      "dff4b3290d66c4c804267ecfe2fb9717a6449cff",
-      []
-     ],
      "feature-policy-clipboard-read.html": [
       "10fc45fd933ef0f77e5d53d4fac9ec70d372ce48",
       []
@@ -360098,7 +360262,7 @@
       []
      ],
      "permissions-policy-battery.html": [
-      "dff4b3290d66c4c804267ecfe2fb9717a6449cff",
+      "643d6a4ef5e3e9b4994a57e236fa5eae7ffc6581",
       []
      ],
      "permissions-policy-bluetooth.html": [
@@ -364073,7 +364237,7 @@
      []
     ],
     "hidden_document-expected.txt": [
-     "46575e6b01a54d370f99753cb7db2a1bb8ae70cf",
+     "f8824993112643a648d5459b40076f193e4e5f6f",
      []
     ],
     "lock-basic-expected.txt": [
@@ -364293,10 +364457,6 @@
       "58435be6312cb06f936117c631679663c4ed2b07",
       []
      ],
-     "idlharness.window-expected.txt": [
-      "e7f55fdffaf78d35856709317a1ec3c41d9f2ea4",
-      []
-     ],
      "idlharness.window.js.ini": [
       "cda970f7b33486f5977099a31b29ac9140a0ed3f",
       []
@@ -378489,11 +378649,11 @@
      ]
     },
     "usb-allowed-by-feature-policy.https.sub.html.headers": [
-     "5c7eac0d6231a48836700cb837578e485eff3167",
+     "022b027812ab3ea7e7b730d99f11f4229aa8fb2f",
      []
     ],
     "usb-disabled-by-feature-policy.https.sub.html.headers": [
-     "4fd1e269362c43d282ca8e3c5c35a5d648f0666b",
+     "ff22d62f104a3cf8cf4a659498f682dc1e83d34d",
      []
     ],
     "usb.https.window.js.ini": [
@@ -398710,22 +398870,22 @@
     ]
    },
    "battery-status": {
-    "battery-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html": [
-     "47a9cbac0fc4da3ec56e63896d62fa51feb512c6",
+    "battery-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html": [
+     "f65f3629df8ef9670b6a12148b1a0a303e0b74f0",
      [
       null,
       {}
      ]
     ],
-    "battery-allowed-by-feature-policy-attribute.https.sub.html": [
-     "96bca54ec8e087ba98961c303e7f96c955963450",
+    "battery-allowed-by-permissions-policy-attribute.https.sub.html": [
+     "fc4a7bad8134e63e198de6fa0021fd91bd0a97db",
      [
       null,
       {}
      ]
     ],
-    "battery-allowed-by-feature-policy.https.sub.html": [
-     "5849af5750148f9af220e9781827fd3b10b32de1",
+    "battery-allowed-by-permissions-policy.https.sub.html": [
+     "8cbf39997b9c5ff2e2df8acc3bf8bd52055404d9",
      [
       null,
       {}
@@ -398738,15 +398898,15 @@
       {}
      ]
     ],
-    "battery-default-feature-policy.https.sub.html": [
-     "5486a453e4e99c26e50b6de99a27a48c78f896de",
+    "battery-default-permissions-policy.https.sub.html": [
+     "7485250dae8052bc915e7b8bf3447ac7e70fcc8e",
      [
       null,
       {}
      ]
     ],
-    "battery-disabled-by-feature-policy.https.sub.html": [
-     "9e6d685ff4288ad91ae851424a48cdeffd9d27ba",
+    "battery-disabled-by-permissions-policy.https.sub.html": [
+     "4afdee8a0cbeb18d2df203d87efcda12c5c29944",
      [
       null,
       {}
@@ -444970,6 +445130,13 @@
         null,
         {}
        ]
+      ],
+      "textarea-rows-cols-sizing.html": [
+       "8cd9c27d36cba2f6c7a1863c3d1c95d1c3b97320",
+       [
+        null,
+        {}
+       ]
       ]
      },
      "inheritance.html": [
@@ -574747,7 +574914,7 @@
      ]
     ],
     "hidden_document.html": [
-     "6097cc9d28d2baae076e4b9949b2bf11b9e25e02",
+     "4d018a882a114aa6a29db60c2403e78734cdd034",
      [
       null,
       {
@@ -575143,7 +575310,7 @@
       ]
      ],
      "scroll-timeline-document-scroller-quirks.html": [
-      "26f88255fae3bd7e93d57087e55ae3a816804ebc",
+      "1a9e6a2b3aa5b72eae8ad740a4c15a5f66c6fbd8",
       [
        null,
        {}
@@ -575555,6 +575722,13 @@
        {}
       ]
      ],
+     "view-timeline-get-current-time-range-name.html": [
+      "8f385e7b6ebaa208582bc3bec068cc1beb791fd8",
+      [
+       null,
+       {}
+      ]
+     ],
      "view-timeline-inset.html": [
       "4864d63b386a5fc4f6ec94247fe0984a3bf2f00a",
       [
@@ -611485,7 +611659,7 @@
      ]
     ],
     "hardware-capability-stats.https.html": [
-     "eb56344e5947a7eae63162a185c0bae3821e14be",
+     "1cd3a370c377c92b4cf1f73ec6789358877555b6",
      [
       null,
       {
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy.https.sub.html.headers
deleted file mode 100644
index 03b98040..0000000
--- a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy.https.sub.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: battery *
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub-expected.txt b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub-expected.txt
new file mode 100644
index 0000000..50daf927
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS permissions policy allow="battery" allows same-origin relocation.
+FAIL permissions policy allow="battery" disallows cross-origin relocation. assert_false: navigator.getBattery() expected false got true
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html
similarity index 71%
rename from third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
rename to third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html
index 47a9cbac..f65f362 100644
--- a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html
@@ -2,16 +2,16 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
-const relative_path = '/feature-policy/resources/feature-policy-battery.html';
-const base_src = '/feature-policy/resources/redirect-on-load.html#';
+const relative_path = '/permissions-policy/resources/permissions-policy-battery.html';
+const base_src = '/permissions-policy/resources/redirect-on-load.html#';
 const same_origin_src = base_src + relative_path;
 const cross_origin_src = base_src + 'https://{{domains[www]}}:{{ports[https][0]}}' +
   relative_path;
-const header = 'Feature-Policy allow="battery"';
+const header = 'permissions policy allow="battery"';
 
 async_test(t => {
   test_feature_availability(
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy-attribute.https.sub.html b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy-attribute.https.sub.html
similarity index 77%
rename from third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy-attribute.https.sub.html
rename to third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy-attribute.https.sub.html
index 96bca54..fc4a7ba 100644
--- a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy-attribute.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy-attribute.https.sub.html
@@ -2,14 +2,14 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
-const same_origin_src = '/feature-policy/resources/feature-policy-battery.html';
+const same_origin_src = '/permissions-policy/resources/permissions-policy-battery.html';
 const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
   same_origin_src;
-const feature_name = 'Feature policy "battery"';
+const feature_name = 'permissions policy "battery"';
 const header = 'allow="battery" attribute';
 
 async_test(t => {
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy.https.sub.html
similarity index 76%
rename from third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy.https.sub.html
rename to third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy.https.sub.html
index 5849af5..8cbf399 100644
--- a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy.https.sub.html
@@ -2,14 +2,14 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
-const same_origin_src = '/feature-policy/resources/feature-policy-battery.html';
+const same_origin_src = '/permissions-policy/resources/permissions-policy-battery.html';
 const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
   same_origin_src;
-const header = 'Feature-Policy header {"battery" : ["*"]}';
+const header = 'Permissions-Policy header "battery=*"';
 
 promise_test(
     async () => await navigator.getBattery(),
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy.https.sub.html.headers
new file mode 100644
index 0000000..989fdc3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-allowed-by-permissions-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: battery=*
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-default-permissions-policy.https.sub-expected.txt b/third_party/blink/web_tests/external/wpt/battery-status/battery-default-permissions-policy.https.sub-expected.txt
new file mode 100644
index 0000000..b7c8ccfb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-default-permissions-policy.https.sub-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS Default "battery" permissions policy allows the top-level document.
+PASS Default "battery" permissions policy allows same-origin iframes.
+FAIL Default "battery" permissions policy disallows cross-origin iframes. assert_false: navigator.getBattery() expected false got true
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-default-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/battery-status/battery-default-permissions-policy.https.sub.html
similarity index 77%
rename from third_party/blink/web_tests/external/wpt/battery-status/battery-default-feature-policy.https.sub.html
rename to third_party/blink/web_tests/external/wpt/battery-status/battery-default-permissions-policy.https.sub.html
index 5486a45..7485250d 100644
--- a/third_party/blink/web_tests/external/wpt/battery-status/battery-default-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-default-permissions-policy.https.sub.html
@@ -2,14 +2,14 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
-const same_origin_src = '/feature-policy/resources/feature-policy-battery.html';
+const same_origin_src = '/permissions-policy/resources/permissions-policy-battery.html';
 const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
   same_origin_src;
-const header = 'Default "battery" feature policy ["self"]';
+const header = 'Default "battery" permissions policy';
 
 promise_test(
     async () => await navigator.getBattery(),
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-feature-policy.https.sub.html.headers
deleted file mode 100644
index 09a5ae8..0000000
--- a/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-feature-policy.https.sub.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: battery 'none'
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-permissions-policy.https.sub-expected.txt b/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-permissions-policy.https.sub-expected.txt
new file mode 100644
index 0000000..8a9a240
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-permissions-policy.https.sub-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL Permissions-Policy header "battery=()" disallows the top-level document. assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Permissions-Policy header "battery=()" disallows same-origin iframes. assert_false: navigator.getBattery() expected false got true
+FAIL Permissions-Policy header "battery=()" disallows cross-origin iframes. assert_false: navigator.getBattery() expected false got true
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-permissions-policy.https.sub.html
similarity index 77%
rename from third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-feature-policy.https.sub.html
rename to third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-permissions-policy.https.sub.html
index 9e6d685..4afdee8 100644
--- a/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-permissions-policy.https.sub.html
@@ -2,14 +2,14 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
-const same_origin_src = '/feature-policy/resources/feature-policy-battery.html';
+const same_origin_src = '/permissions-policy/resources/permissions-policy-battery.html';
 const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
   same_origin_src;
-const header = 'Feature-Policy header {"battery" : []}';
+const header = 'Permissions-Policy header "battery=()"';
 
 promise_test(async t => {
   await promise_rejects_dom(t, 'NotAllowedError', navigator.getBattery());
diff --git a/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-permissions-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-permissions-policy.https.sub.html.headers
new file mode 100644
index 0000000..9ff0bc61
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/battery-status/battery-disabled-by-permissions-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: battery=()
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/support/form-controls-slr.png b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/manual/support/form-controls-slr.png
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-writing-modes/support/form-controls-slr.png
rename to third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/manual/support/form-controls-slr.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/support/form-controls-srl.png b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/manual/support/form-controls-srl.png
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-writing-modes/support/form-controls-srl.png
rename to third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/manual/support/form-controls-srl.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/support/form-controls-vlr.png b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/manual/support/form-controls-vlr.png
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-writing-modes/support/form-controls-vlr.png
rename to third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/manual/support/form-controls-vlr.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/support/vertical-form.png b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/manual/support/vertical-form.png
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-writing-modes/support/vertical-form.png
rename to third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/manual/support/vertical-form.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-native-horizontal.optional.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-native-horizontal.optional.html
new file mode 100644
index 0000000..79c54d50
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-native-horizontal.optional.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/#the-textarea-element">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#block-flow">
+<title>native textarea in horizontal writing mode</title>
+<meta charset="utf-8">
+<link rel="mismatch" href="textarea-appearance-native-vlr.optional.html">
+<link rel="mismatch" href="textarea-appearance-none-vlr.optional.html">
+<link rel="mismatch" href="textarea-appearance-native-vrl.optional.html">
+<link rel="mismatch" href="textarea-appearance-none-vrl.optional.html">
+
+<!-- Note test description should be the same across all files to mismatch on. -->
+<p>The textarea below should match the correct writing mode.</p>
+<textarea>Some sample text</textarea>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-native-vlr.optional.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-native-vlr.optional.html
new file mode 100644
index 0000000..3ce811fd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-native-vlr.optional.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/#the-textarea-element">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#block-flow">
+<title>native textarea in vertical writing mode</title>
+<meta charset="utf-8">
+<link rel="mismatch" href="textarea-appearance-native-horizontal.optional.html">
+<link rel="mismatch" href="textarea-appearance-none-horizontal.optional.html">
+<link rel="mismatch" href="textarea-appearance-native-vrl.optional.html">
+<link rel="mismatch" href="textarea-appearance-none-vrl.optional.html">
+
+<!-- Note test description should be the same across all files to mismatch on. -->
+<p>The textarea below should match the correct writing mode.</p>
+<textarea style="writing-mode: vertical-lr">Some sample text</textarea>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-native-vrl.optional.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-native-vrl.optional.html
new file mode 100644
index 0000000..c5f8b4c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-native-vrl.optional.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/#the-textarea-element">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#block-flow">
+<title>native textarea in vertical writing mode</title>
+<meta charset="utf-8">
+<link rel="mismatch" href="textarea-appearance-native-horizontal.optional.html">
+<link rel="mismatch" href="textarea-appearance-none-horizontal.optional.html">
+<link rel="mismatch" href="textarea-appearance-native-vlr.optional.html">
+<link rel="mismatch" href="textarea-appearance-none-vlr.optional.html">
+
+<!-- Note test description should be the same across all files to mismatch on. -->
+<p>The textarea below should match the correct writing mode.</p>
+<textarea style="writing-mode: vertical-rl">Some sample text</textarea>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-none-horizontal.optional.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-none-horizontal.optional.html
new file mode 100644
index 0000000..be4dde1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-none-horizontal.optional.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/#the-textarea-element">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#block-flow">
+<title>unstyled textarea in horizontal writing mode</title>
+<meta charset="utf-8">
+<link rel="mismatch" href="textarea-appearance-none-vlr.optional.html">
+<link rel="mismatch" href="textarea-appearance-native-vlr.optional.html">
+<link rel="mismatch" href="textarea-appearance-none-vrl.optional.html">
+<link rel="mismatch" href="textarea-appearance-native-vrl.optional.html">
+
+<!-- Note test description should be the same across all files to mismatch on. -->
+<p>The textarea below should match the correct writing mode.</p>
+<textarea style="appearance: none;">Some sample text</textarea>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-none-vlr.optional.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-none-vlr.optional.html
new file mode 100644
index 0000000..d5992962
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-none-vlr.optional.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/#the-textarea-element">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#block-flow">
+<title>unstyled textarea in vertical writing mode</title>
+<meta charset="utf-8">
+<link rel="mismatch" href="textarea-appearance-none-horizontal.optional.html">
+<link rel="mismatch" href="textarea-appearance-native-horizontal.optional.html">
+<link rel="mismatch" href="textarea-appearance-native-vrl.optional.html">
+<link rel="mismatch" href="textarea-appearance-none-vrl.optional.html">
+
+<!-- Note test description should be the same across all files to mismatch on. -->
+<p>The textarea below should match the correct writing mode.</p>
+<textarea style="writing-mode: vertical-lr; appearance: none;">Some sample text</textarea>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-none-vrl.optional.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-none-vrl.optional.html
new file mode 100644
index 0000000..cbaddc3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-appearance-none-vrl.optional.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/#the-textarea-element">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#block-flow">
+<title>unstyled textarea in vertical writing mode</title>
+<meta charset="utf-8">
+<link rel="mismatch" href="textarea-appearance-none-horizontal.optional.html">
+<link rel="mismatch" href="textarea-appearance-native-horizontal.optional.html">
+<link rel="mismatch" href="textarea-appearance-none-vlr.optional.html">
+<link rel="mismatch" href="textarea-appearance-native-vlr.optional.html">
+
+<!-- Note test description should be the same across all files to mismatch on. -->
+<p>The textarea below should match the correct writing mode.</p>
+<textarea style="writing-mode: vertical-rl; appearance: none;">Some sample text</textarea>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-rows-cols-sizing.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-rows-cols-sizing.html
new file mode 100644
index 0000000..8cd9c27
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/forms/textarea-rows-cols-sizing.html
@@ -0,0 +1,105 @@
+<!doctype html>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://html.spec.whatwg.org/#the-textarea-element">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#block-flow">
+<title>Textarea rows/cols size mapping in different writing modes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<textarea></textarea>
+
+<script>
+for (const writingMode of ["horizontal-tb", "vertical-lr", "vertical-rl", "sideways-lr", "sideways-rl"]) {
+    if (!CSS.supports(`writing-mode: ${writingMode}`))
+        continue;
+
+    const isHorizontal = writingMode === "horizontal-tb";
+    const textarea = document.querySelector("textarea");
+
+    test(t => {
+        textarea.style.writingMode = writingMode;
+        t.add_cleanup(() => {
+            textarea.removeAttribute("style");
+            textarea.removeAttribute("rows");
+        });
+
+        const rowsDimension = isHorizontal ? "height" : "width";
+        const colsDimension = isHorizontal ? "width" : "height";
+
+        let previousRect = textarea.getBoundingClientRect();
+        textarea.rows = 10;
+        assert_equals(
+            textarea.getBoundingClientRect()[colsDimension],
+            previousRect[colsDimension],
+            `${colsDimension} shouldn't change when changing rows`
+        );
+        previousRect = textarea.getBoundingClientRect();
+
+        textarea.rows = 9;
+        assert_true(
+            textarea.getBoundingClientRect()[rowsDimension] < previousRect[rowsDimension],
+            `${rowsDimension} should decrease when decreasing rows`
+        );
+        assert_equals(
+            textarea.getBoundingClientRect()[colsDimension],
+            previousRect[colsDimension],
+            `${colsDimension} shouldn't change when changing rows`
+        );
+        previousRect = textarea.getBoundingClientRect();
+
+        textarea.rows = 11;
+        assert_true(
+            textarea.getBoundingClientRect()[rowsDimension] > previousRect[rowsDimension],
+            `${rowsDimension} should increase when increasing rows`
+        );
+        assert_equals(
+            textarea.getBoundingClientRect()[colsDimension],
+            previousRect[colsDimension],
+            `${colsDimension} shouldn't change when changing rows`
+        );
+    }, `textarea[style="writing-mode: ${writingMode}"] rows attribute changes the size correctly`);
+
+    test(t => {
+        textarea.style.writingMode = writingMode;
+        t.add_cleanup(() => {
+            textarea.removeAttribute("style");
+            textarea.removeAttribute("cols");
+        });
+
+        const rowsDimension = isHorizontal ? "height" : "width";
+        const colsDimension = isHorizontal ? "width" : "height";
+
+        let previousRect = textarea.getBoundingClientRect();
+        textarea.cols = 40;
+        assert_equals(
+            textarea.getBoundingClientRect()[rowsDimension],
+            previousRect[rowsDimension],
+            `${rowsDimension} shouldn't change when changing cols`
+        );
+        previousRect = textarea.getBoundingClientRect();
+
+        textarea.cols = 30;
+        assert_true(
+            textarea.getBoundingClientRect()[colsDimension] < previousRect[colsDimension],
+            `${colsDimension} should decrease when decreasing cols`
+        );
+        assert_equals(
+            textarea.getBoundingClientRect()[rowsDimension],
+            previousRect[rowsDimension],
+            `${rowsDimension} shouldn't change when changing cols`
+        );
+        previousRect = textarea.getBoundingClientRect();
+
+        textarea.cols = 50;
+        assert_true(
+            textarea.getBoundingClientRect()[colsDimension] > previousRect[colsDimension],
+            `${colsDimension} should increase when increasing cols`
+        );
+        assert_equals(
+            textarea.getBoundingClientRect()[rowsDimension],
+            previousRect[rowsDimension],
+            `${rowsDimension} shouldn't change when changing cols`
+        );
+    }, `textarea[style="writing-mode: ${writingMode}"] cols attribute changes the size correctly`);
+};
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-battery.html b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-battery.html
deleted file mode 100644
index dff4b32..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-battery.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<script>
-'use strict';
-
-Promise.resolve().then(() => navigator.getBattery()).then(battery => {
-  window.parent.postMessage({ enabled: true }, '*');
-}, error => {
-  window.parent.postMessage({ enabled: false }, '*');
-});
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-idle-detection-worker.html b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-idle-detection-worker.html
deleted file mode 100644
index 1ef5298a..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-idle-detection-worker.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<script>
-'use strict';
-
-let worker = new Worker('feature-policy-idle-detection-worker.js');
-
-worker.onmessage = event => {
-  window.parent.postMessage(event.data, '*');
-};
-worker.postMessage({ type: 'ready' });
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-idle-detection-worker.js b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-idle-detection-worker.js
deleted file mode 100644
index 2e4e3cd..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-idle-detection-worker.js
+++ /dev/null
@@ -1,16 +0,0 @@
-'use strict';
-
-// Dedicated worker
-if (typeof postMessage === 'function') {
-  onmessage = event => {
-    switch(event.data.type) {
-      case 'ready':
-        new IdleDetector().start().then(() => {
-          postMessage({ enabled: true });
-        }, error => {
-          postMessage ({ enabled: false });
-        });
-        break;
-    }
-  };
-}
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-idle-detection.html b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-idle-detection.html
deleted file mode 100644
index 65a5781..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-idle-detection.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<script>
-'use strict';
-
-new IdleDetector().start().then(() => {
-  window.parent.postMessage({ enabled: true }, '*');
-}, error => {
-  window.parent.postMessage({ enabled: false }, '*');
-});
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-serial-worker.html b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-serial-worker.html
deleted file mode 100644
index 9e6a7d02..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-serial-worker.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<script>
-'use strict';
-
-let worker = new Worker('feature-policy-serial-worker.js');
-
-worker.onmessage = event => {
-  window.parent.postMessage(event.data, '*');
-};
-worker.postMessage({ type: 'ready' });
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-serial-worker.js b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-serial-worker.js
deleted file mode 100644
index 2e8e6f5..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-serial-worker.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict';
-
-// Dedicated worker
-if (typeof postMessage === 'function') {
-  onmessage = event => {
-    switch(event.data.type) {
-      case 'ready':
-        navigator.serial.getPorts().then(
-            () => postMessage({ enabled: true }),
-            error => postMessage ({ enabled: false }));
-        break;
-    }
-  };
-}
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-serial.html b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-serial.html
deleted file mode 100644
index caf716d..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-serial.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<script>
-'use strict';
-
-navigator.serial.getPorts().then(ports => {
-  window.parent.postMessage({ enabled: true }, '*');
-}, error => {
-  window.parent.postMessage({ enabled: false }, '*');
-});
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute.https.sub.html.ini b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute.https.sub.html.ini
deleted file mode 100644
index a4d0e1d..0000000
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute.https.sub.html.ini
+++ /dev/null
@@ -1,13 +0,0 @@
-[idle-detection-allowed-by-feature-policy-attribute.https.sub.html]
-  expected: ERROR
-  [Attribute allow="idle-detection" in top-level frame can be enabled in same-origin iframe using Feature policy "idle-detection".]
-    expected: NOTRUN
-
-  [Attribute allow="idle-detection" in top-level frame can be enabled in a worker in same-origin iframe using Feature policy "idle-detection".]
-    expected: NOTRUN
-
-  [Attribute allow="idle-detection" in top-level frame can be enabled in cross-origin iframe using Feature policy "idle-detection".]
-    expected: NOTRUN
-
-  [Attribute allow="idle-detection" in top-level frame can be enabled in a worker in cross-origin iframe using Feature policy "idle-detection".]
-    expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html.headers
deleted file mode 100644
index 729e942..0000000
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: idle-detection *
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html.ini b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html.ini
deleted file mode 100644
index cda09c87..0000000
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[idle-detection-allowed-by-feature-policy.https.sub.html]
-  expected: ERROR
-  [Feature-Policy {"idle-detection" : ["*"\]} explicity set by top-level frame allows the top-level document.]
-    expected: NOTRUN
-
-  [Feature-Policy {"idle-detection" : ["*"\]} explicity set by top-level frame allows same-origin iframes.]
-    expected: NOTRUN
-
-  [Feature-Policy {"idle-detection" : ["*"\]} explicity set by top-level frame allows workers in same-origin iframes.]
-    expected: NOTRUN
-
-  [Feature-Policy {"idle-detection" : ["*"\]} explicity set by top-level frame allows cross-origin iframes.]
-    expected: NOTRUN
-
-  [Feature-Policy {"idle-detection" : ["*"\]} explicity set by top-level frame allows workers in cross-origin iframes.]
-    expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html
similarity index 86%
rename from third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
rename to third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html
index 6c6ff6f..fc92a09f 100644
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html
@@ -4,16 +4,16 @@
 <script src=/resources/testdriver-vendor.js></script>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
-const base_src = '/feature-policy/resources/redirect-on-load.html#';
+const base_src = '/permissions-policy/resources/redirect-on-load.html#';
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
 const relative_path =
-  '/feature-policy/resources/feature-policy-idle-detection.html';
+  '/permissions-policy/resources/permissions-policy-idle-detection.html';
 const relative_worker_frame_path =
-  '/feature-policy/resources/feature-policy-idle-detection-worker.html';
+  '/permissions-policy/resources/permissions-policy-idle-detection-worker.html';
 const same_origin_src = base_src + relative_path;
 const same_origin_worker_frame_src = base_src + relative_worker_frame_path;
 const cross_origin_src = base_src + sub + relative_path;
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html.ini
similarity index 84%
rename from third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
rename to third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html.ini
index 45c77e5..e00e270 100644
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html.ini
@@ -1,4 +1,4 @@
-[idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html]
+[idle-detection-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html]
   expected: ERROR
   [Attribute allow="idle-detection" in top-level frame allows same-origin relocation.]
     expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute.https.sub.html b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute.https.sub.html
similarity index 74%
rename from third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute.https.sub.html
rename to third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute.https.sub.html
index f3cfa0e..be2de1d7 100644
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy-attribute.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute.https.sub.html
@@ -4,15 +4,15 @@
 <script src=/resources/testdriver-vendor.js></script>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
 const same_origin_src =
-  '/feature-policy/resources/feature-policy-idle-detection.html'
+  '/permissions-policy/resources/permissions-policy-idle-detection.html'
 const same_origin_worker_frame_src =
-    '/feature-policy/resources/feature-policy-idle-detection-worker.html';
+    '/permissions-policy/resources/permissions-policy-idle-detection-worker.html';
 const cross_origin_src = sub + same_origin_src;
 const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
 
@@ -24,25 +24,25 @@
   test_feature_availability('new IdleDetector().start()', t, same_origin_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
-   'in same-origin iframe using Feature policy "idle-detection".');
+   'in same-origin iframe using Permissions Policy "idle-detection".');
 
 promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
-   'in a worker in same-origin iframe using Feature policy "idle-detection".');
+   'in a worker in same-origin iframe using Permissions Policy "idle-detection".');
 
 promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
-   'in cross-origin iframe using Feature policy "idle-detection".');
+   'in cross-origin iframe using Permissions Policy "idle-detection".');
 
 promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
-   'in a worker in cross-origin iframe using Feature policy "idle-detection".');
+   'in a worker in cross-origin iframe using Permissions Policy "idle-detection".');
 
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute.https.sub.html.ini b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute.https.sub.html.ini
new file mode 100644
index 0000000..d0037302
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy-attribute.https.sub.html.ini
@@ -0,0 +1,13 @@
+[idle-detection-allowed-by-permissions-policy-attribute.https.sub.html]
+  expected: ERROR
+  [Attribute allow="idle-detection" in top-level frame can be enabled in same-origin iframe using Permissions Policy "idle-detection".]
+    expected: NOTRUN
+
+  [Attribute allow="idle-detection" in top-level frame can be enabled in a worker in same-origin iframe using Permissions Policy "idle-detection".]
+    expected: NOTRUN
+
+  [Attribute allow="idle-detection" in top-level frame can be enabled in cross-origin iframe using Permissions Policy "idle-detection".]
+    expected: NOTRUN
+
+  [Attribute allow="idle-detection" in top-level frame can be enabled in a worker in cross-origin iframe using Permissions Policy "idle-detection".]
+    expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy.https.sub.html
similarity index 70%
rename from third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html
rename to third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy.https.sub.html
index 75d1540..47a7d69 100644
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy.https.sub.html
@@ -4,15 +4,15 @@
 <script src=/resources/testdriver-vendor.js></script>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
 const same_origin_src =
-  '/feature-policy/resources/feature-policy-idle-detection.html'
+  '/permissions-policy/resources/permissions-policy-idle-detection.html'
 const same_origin_worker_frame_src =
-    '/feature-policy/resources/feature-policy-idle-detection-worker.html';
+    '/permissions-policy/resources/permissions-policy-idle-detection-worker.html';
 const cross_origin_src = sub + same_origin_src;
 const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
 
@@ -23,35 +23,35 @@
 promise_test(async t => {
   await new IdleDetector().start();
 },
-  'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
+  'Permissions-Policy idle-detection=* explicity set by top-level ' +
   'frame allows the top-level document.');
 
 promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_src,
       expect_feature_available_default);
-}, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
+}, 'Permissions-Policy idle-detection=* explicity set by top-level ' +
    'frame allows same-origin iframes.');
 
 promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
       expect_feature_available_default);
-}, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
+}, 'Permissions-Policy idle-detection=* explicity set by top-level ' +
    'frame allows workers in same-origin iframes.');
 
 promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
       expect_feature_available_default);
-}, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
+}, 'Permissions-Policy idle-detection=* explicity set by top-level ' +
    'frame allows cross-origin iframes.');
 
 promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
       expect_feature_available_default);
-}, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
+}, 'Permissions-Policy idle-detection=* explicity set by top-level ' +
    'frame allows workers in cross-origin iframes.');
 
 fetch_tests_from_worker(new Worker(
-  'resources/idle-detection-allowed-by-feature-policy-worker.js'))
+  'resources/idle-detection-allowed-by-permissions-policy-worker.js'))
 
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy.https.sub.html.headers
new file mode 100644
index 0000000..2bf18afe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: idle-detection=*
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy.https.sub.html.ini b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy.https.sub.html.ini
new file mode 100644
index 0000000..6f363c1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-allowed-by-permissions-policy.https.sub.html.ini
@@ -0,0 +1,16 @@
+[idle-detection-allowed-by-permissions-policy.https.sub.html]
+  expected: ERROR
+  [Permissions-Policy idle-detection=* explicity set by top-level frame allows the top-level document.]
+    expected: NOTRUN
+
+  [Permissions-Policy idle-detection=* explicity set by top-level frame allows same-origin iframes.]
+    expected: NOTRUN
+
+  [Permissions-Policy idle-detection=* explicity set by top-level frame allows workers in same-origin iframes.]
+    expected: NOTRUN
+
+  [Permissions-Policy idle-detection=* explicity set by top-level frame allows cross-origin iframes.]
+    expected: NOTRUN
+
+  [Permissions-Policy idle-detection=* explicity set by top-level frame allows workers in cross-origin iframes.]
+    expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-feature-policy.https.sub.html.ini b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-feature-policy.https.sub.html.ini
deleted file mode 100644
index 08fd1d3..0000000
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-feature-policy.https.sub.html.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[idle-detection-default-feature-policy.https.sub.html]
-  expected: ERROR
-  [Default "idle-detection" feature policy ["self"\] allows the top-level document.]
-    expected: NOTRUN
-
-  [Default "idle-detection" feature policy ["self"\] allows same-origin iframes.]
-    expected: NOTRUN
-
-  [Default "idle-detection" feature policy ["self"\] disallows cross-origin iframes.]
-    expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-permissions-policy.https.sub.html
similarity index 74%
rename from third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-feature-policy.https.sub.html
rename to third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-permissions-policy.https.sub.html
index f0766ae9..762e299 100644
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-permissions-policy.https.sub.html
@@ -4,12 +4,12 @@
 <script src=/resources/testdriver-vendor.js></script>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
 const same_origin_src =
-  '/feature-policy/resources/feature-policy-idle-detection.html'
+  '/permissions-policy/resources/permissions-policy-idle-detection.html'
 const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
   same_origin_src;
 
@@ -20,19 +20,19 @@
 promise_test(async t => {
   await new IdleDetector().start()
 },
-  'Default "idle-detection" feature policy ["self"] ' +
+  'Default "idle-detection" permissions policy "self" ' +
   'allows the top-level document.');
 
 promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_src,
       expect_feature_available_default);
-}, 'Default "idle-detection" feature policy ["self"] ' +
+}, 'Default "idle-detection" permissions policy "self" ' +
    'allows same-origin iframes.');
 
 promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
       expect_feature_unavailable_default);
-}, 'Default "idle-detection" feature policy ["self"] ' +
+}, 'Default "idle-detection" permissions policy "self" ' +
    'disallows cross-origin iframes.');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-permissions-policy.https.sub.html.ini b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-permissions-policy.https.sub.html.ini
new file mode 100644
index 0000000..cb296f98
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-default-permissions-policy.https.sub.html.ini
@@ -0,0 +1,10 @@
+[idle-detection-default-permissions-policy.https.sub.html]
+  expected: ERROR
+  [Default "idle-detection" permissions policy "self" allows the top-level document.]
+    expected: NOTRUN
+
+  [Default "idle-detection" permissions policy "self" allows same-origin iframes.]
+    expected: NOTRUN
+
+  [Default "idle-detection" permissions policy "self" disallows cross-origin iframes.]
+    expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-feature-policy.https.sub.html.headers
deleted file mode 100644
index f27e1a2..0000000
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-feature-policy.https.sub.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: idle-detection 'none'
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-permissions-policy.https.sub.html
similarity index 68%
rename from third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-feature-policy.https.sub.html
rename to third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-permissions-policy.https.sub.html
index 2814a22..90317dec 100644
--- a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-permissions-policy.https.sub.html
@@ -2,15 +2,15 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
 const same_origin_src =
-  '/feature-policy/resources/feature-policy-idle-detection.html'
+  '/permissions-policy/resources/permissions-policy-idle-detection.html'
 const same_origin_worker_frame_src =
-    '/feature-policy/resources/feature-policy-idle-detection-worker.html';
+    '/permissions-policy/resources/permissions-policy-idle-detection-worker.html';
 const cross_origin_src = sub + same_origin_src;
 const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
 
@@ -22,35 +22,35 @@
   } catch (error) {
     assert_equals(error.name, 'SecurityError');
   }
-}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
+}, 'Permissions-Policy idle-detection=() explicitly set by top-level frame ' +
    'disallows query in the top-level document.');
 
 async_test(t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_src,
       expect_feature_unavailable_default);
-}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
+}, 'Permissions-Policy idle-detection=() explicitly set by top-level frame ' +
    'disallows same-origin iframes.');
 
 async_test(t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
       expect_feature_unavailable_default);
-}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
+}, 'Permissions-Policy idle-detection=() explicitly set by top-level frame ' +
    'disallows workers in same-origin iframes.');
 
 async_test(t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
       expect_feature_unavailable_default);
-}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
+}, 'Permissions-Policy idle-detection=() explicitly set by top-level frame ' +
    'disallows cross-origin iframes.');
 
 async_test(t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
       expect_feature_unavailable_default);
-}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
+}, 'Permissions-Policy idle-detection=() explicitly set by top-level frame ' +
    'disallows workers in cross-origin iframes.');
 
 fetch_tests_from_worker(new Worker(
-  'resources/idle-detection-disabled-by-feature-policy-worker.js'))
+  'resources/idle-detection-disabled-by-permissions-policy-worker.js'))
 
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-permissions-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-permissions-policy.https.sub.html.headers
new file mode 100644
index 0000000..289463c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/idle-detection-disabled-by-permissions-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: idle-detection=()
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-allowed-by-feature-policy-worker.js b/third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-allowed-by-permissions-policy-worker.js
similarity index 75%
rename from third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-allowed-by-feature-policy-worker.js
rename to third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-allowed-by-permissions-policy-worker.js
index e643414f..1fe410e 100644
--- a/third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-allowed-by-feature-policy-worker.js
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-allowed-by-permissions-policy-worker.js
@@ -11,6 +11,6 @@
 promise_test(async () => {
   await new IdleDetector().start()
 },
-    `Inherited header feature policy allows ${workerType} workers.`)
+    `Inherited header permissions policy allows ${workerType} workers.`)
 
 done();
diff --git a/third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-disabled-by-feature-policy-worker.js b/third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-disabled-by-permissions-policy-worker.js
similarity index 86%
rename from third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-disabled-by-feature-policy-worker.js
rename to third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-disabled-by-permissions-policy-worker.js
index 138b2917..c30f7c1 100644
--- a/third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-disabled-by-feature-policy-worker.js
+++ b/third_party/blink/web_tests/external/wpt/idle-detection/resources/idle-detection-disabled-by-permissions-policy-worker.js
@@ -2,7 +2,7 @@
 
 importScripts('/resources/testharness.js');
 
-const header = 'Feature-Policy header {"idle-detection" : []}';
+const header = 'Permissions-Policy header idle-detection=()';
 let workerType;
 
 if (typeof postMessage === 'function') {
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-battery.html b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-battery.html
index dff4b32..643d6a4 100644
--- a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-battery.html
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-battery.html
@@ -2,8 +2,8 @@
 'use strict';
 
 Promise.resolve().then(() => navigator.getBattery()).then(battery => {
-  window.parent.postMessage({ enabled: true }, '*');
+  window.parent.postMessage({ type: 'availability-result', enabled: true }, '*');
 }, error => {
-  window.parent.postMessage({ enabled: false }, '*');
+  window.parent.postMessage({ type: 'availability-result', enabled: false }, '*');
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-idle-detection-worker.js b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-idle-detection-worker.js
index 2e4e3cd..0d348c7 100644
--- a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-idle-detection-worker.js
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-idle-detection-worker.js
@@ -6,9 +6,9 @@
     switch(event.data.type) {
       case 'ready':
         new IdleDetector().start().then(() => {
-          postMessage({ enabled: true });
+          postMessage({ type: 'availability-result', enabled: true });
         }, error => {
-          postMessage ({ enabled: false });
+          postMessage ({ type: 'availability-result', enabled: false });
         });
         break;
     }
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-idle-detection.html b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-idle-detection.html
index 65a5781..f21a3851 100644
--- a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-idle-detection.html
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-idle-detection.html
@@ -2,9 +2,9 @@
 'use strict';
 
 new IdleDetector().start().then(() => {
-  window.parent.postMessage({ enabled: true }, '*');
+  window.parent.postMessage({ type: 'availability-result', enabled: true }, '*');
 }, error => {
-  window.parent.postMessage({ enabled: false }, '*');
+  window.parent.postMessage({ type: 'availability-result', enabled: false }, '*');
 });
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-serial-worker.js b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-serial-worker.js
index 2e8e6f5..59bb678 100644
--- a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-serial-worker.js
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-serial-worker.js
@@ -6,8 +6,8 @@
     switch(event.data.type) {
       case 'ready':
         navigator.serial.getPorts().then(
-            () => postMessage({ enabled: true }),
-            error => postMessage ({ enabled: false }));
+            () => postMessage({ type: 'availability-result', enabled: true }),
+            error => postMessage ({ type: 'availability-result', enabled: false }));
         break;
     }
   };
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-serial.html b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-serial.html
index caf716d..fe25f03 100644
--- a/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-serial.html
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/resources/permissions-policy-serial.html
@@ -2,8 +2,8 @@
 'use strict';
 
 navigator.serial.getPorts().then(ports => {
-  window.parent.postMessage({ enabled: true }, '*');
+  window.parent.postMessage({ type: 'availability-result', enabled: true }, '*');
 }, error => {
-  window.parent.postMessage({ enabled: false }, '*');
+  window.parent.postMessage({ type: 'availability-result', enabled: false }, '*');
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/screen-orientation/hidden_document-expected.txt b/third_party/blink/web_tests/external/wpt/screen-orientation/hidden_document-expected.txt
index 46575e6..f882499 100644
--- a/third_party/blink/web_tests/external/wpt/screen-orientation/hidden_document-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/screen-orientation/hidden_document-expected.txt
@@ -1,7 +1,8 @@
 This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Test named 'Once maximized, a minimized window can lock or unlock the screen orientation again' specified 3 'cleanup' functions, and 1 failed.
 FAIL hidden documents must reject went trying to call lock or unlock assert_unreached: Should have rejected: undefined Reached unreachable code
-FAIL hidden documents must reject went trying to call unlock promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of undefined (reading 'then')"
-FAIL hidden documents must not unlock the screen orientation promise_test: Unhandled rejection with value: object "ReferenceError: make_cleanup is not defined"
-FAIL Once maximized, a minimized window can lock or unlock the screen orientation again promise_test: Unhandled rejection with value: object "ReferenceError: make_cleanup is not defined"
+FAIL hidden documents must reject went trying to call unlock assert_throws_dom: function "() => screen.orientation.unlock()" did not throw
+FAIL hidden documents must not unlock the screen orientation assert_throws_dom: function "() => screen.orientation.unlock()" did not throw
+FAIL Once maximized, a minimized window can lock or unlock the screen orientation again assert_unreached: Should have rejected: undefined Reached unreachable code
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/screen-orientation/hidden_document.html b/third_party/blink/web_tests/external/wpt/screen-orientation/hidden_document.html
index 6097cc9..4d018a88 100644
--- a/third_party/blink/web_tests/external/wpt/screen-orientation/hidden_document.html
+++ b/third_party/blink/web_tests/external/wpt/screen-orientation/hidden_document.html
@@ -9,8 +9,9 @@
 <script src="/resources/testdriver.js"></script>
 <script src="/resources/testdriver-vendor.js"></script>
 <script src="/page-visibility/resources/window_state_context.js"></script>
-<script src="/screen-orientation/resources/orientation_.js"></script>
-<script>
+<script type="module">
+  import { makeCleanup, getOppositeOrientation } from "./resources/orientation-utils.js";
+
   promise_test(async (t) => {
     const { minimize, restore } = window_state_context(t);
     t.add_cleanup(restore);
@@ -28,25 +29,25 @@
     await minimize();
 
     assert_equals(document.visibilityState, "hidden", "Document must be hidden");
-    await promise_rejects_dom(t, "SecurityError", screen.orientation.unlock() );
+    assert_throws_dom("SecurityError", () => screen.orientation.unlock());
   }, "hidden documents must reject went trying to call unlock");
 
   promise_test(async (t) => {
     const { minimize, restore } = window_state_context(t);
     t.add_cleanup(restore);
-    t.add_cleanup(make_cleanup());
+    t.add_cleanup(makeCleanup());
     await screen.orientation.lock(getOppositeOrientation());
 
     await minimize();
 
     assert_equals(document.visibilityState, "hidden", "Document must be hidden");
-    await promise_rejects_dom(t, "SecurityError", screen.orientation.unlock() );
+    assert_throws_dom("SecurityError", () => screen.orientation.unlock());
   }, "hidden documents must not unlock the screen orientation");
 
   promise_test(async (t) => {
     const { minimize, restore } = window_state_context(t);
     t.add_cleanup(restore);
-    t.add_cleanup(make_cleanup());
+    t.add_cleanup(makeCleanup());
     await screen.orientation.lock(getOppositeOrientation());
 
     await minimize();
@@ -59,6 +60,6 @@
 
     assert_equals(document.visibilityState, "visible");
     await screen.orientation.lock(getOppositeOrientation());
-    await screen.orientation.unlock();
+    screen.orientation.unlock();
   }, "Once maximized, a minimized window can lock or unlock the screen orientation again");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/serial/resources/serial-allowed-by-feature-policy-worker.js b/third_party/blink/web_tests/external/wpt/serial/resources/serial-allowed-by-permissions-policy-worker.js
similarity index 73%
rename from third_party/blink/web_tests/external/wpt/serial/resources/serial-allowed-by-feature-policy-worker.js
rename to third_party/blink/web_tests/external/wpt/serial/resources/serial-allowed-by-permissions-policy-worker.js
index 46c338e..cef0aac 100644
--- a/third_party/blink/web_tests/external/wpt/serial/resources/serial-allowed-by-feature-policy-worker.js
+++ b/third_party/blink/web_tests/external/wpt/serial/resources/serial-allowed-by-permissions-policy-worker.js
@@ -9,6 +9,6 @@
 }
 
 promise_test(() => navigator.serial.getPorts(),
-    `Inherited header feature policy allows ${workerType} workers.`);
+    `Inherited header permissions policy allows ${workerType} workers.`);
 
 done();
diff --git a/third_party/blink/web_tests/external/wpt/serial/resources/serial-disabled-by-feature-policy-worker.js b/third_party/blink/web_tests/external/wpt/serial/resources/serial-disabled-by-permissions-policy-worker.js
similarity index 87%
rename from third_party/blink/web_tests/external/wpt/serial/resources/serial-disabled-by-feature-policy-worker.js
rename to third_party/blink/web_tests/external/wpt/serial/resources/serial-disabled-by-permissions-policy-worker.js
index b64b1a8..afac4429 100644
--- a/third_party/blink/web_tests/external/wpt/serial/resources/serial-disabled-by-feature-policy-worker.js
+++ b/third_party/blink/web_tests/external/wpt/serial/resources/serial-disabled-by-permissions-policy-worker.js
@@ -2,7 +2,7 @@
 
 importScripts('/resources/testharness.js');
 
-const header = 'Feature-Policy header {"serial" : []}';
+const header = 'Permissions-Policy header serial=()';
 let workerType;
 
 if (typeof postMessage === 'function') {
diff --git a/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy.https.sub.html.headers
deleted file mode 100644
index 113ce29..0000000
--- a/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy.https.sub.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: serial *
diff --git a/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html b/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html
similarity index 79%
rename from third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
rename to third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html
index 7c3a88d..ac278ff0cf 100644
--- a/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html
@@ -2,20 +2,20 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
-const relative_path = '/feature-policy/resources/feature-policy-serial.html';
-const base_src = '/feature-policy/resources/redirect-on-load.html#';
+const relative_path = '/permissions-policy/resources/permissions-policy-serial.html';
+const base_src = '/permissions-policy/resources/redirect-on-load.html#';
 const relative_worker_frame_path =
-    '/feature-policy/resources/feature-policy-serial-worker.html';
+    '/permissions-policy/resources/permissions-policy-serial-worker.html';
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
 const same_origin_src = base_src + relative_path;
 const cross_origin_src = base_src + sub + relative_path;
 const same_origin_worker_frame_src = base_src + relative_worker_frame_path;
 const cross_origin_worker_frame_src = base_src + sub +
     relative_worker_frame_path;
-const header = 'Feature-Policy allow="serial"';
+const header = 'Permissions-Policy allow="serial"';
 
 async_test(t => {
   test_feature_availability(
diff --git a/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy-attribute.https.sub.html b/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy-attribute.https.sub.html
similarity index 79%
rename from third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy-attribute.https.sub.html
rename to third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy-attribute.https.sub.html
index 1420c5c0..02560b1 100644
--- a/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy-attribute.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy-attribute.https.sub.html
@@ -2,16 +2,16 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
-const same_origin_src = '/feature-policy/resources/feature-policy-serial.html';
+const same_origin_src = '/permissions-policy/resources/permissions-policy-serial.html';
 const cross_origin_src = sub + same_origin_src;
 const same_origin_worker_frame_src =
-    '/feature-policy/resources/feature-policy-serial-worker.html';
+    '/permissions-policy/resources/permissions-policy-serial-worker.html';
 const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
-const feature_name = 'Feature policy "serial"';
+const feature_name = 'Permissions policy "serial"';
 const header = 'allow="serial" attribute';
 
 async_test(t => {
@@ -41,6 +41,6 @@
     header);
 
 fetch_tests_from_worker(new Worker(
-    'resources/serial-allowed-by-feature-policy-worker.js'));
+    'resources/serial-allowed-by-permissions-policy-worker.js'));
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy.https.sub.html
similarity index 80%
rename from third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy.https.sub.html
rename to third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy.https.sub.html
index 3699069..1be8eb9 100644
--- a/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy.https.sub.html
@@ -3,16 +3,16 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
-const same_origin_src = '/feature-policy/resources/feature-policy-serial.html';
+const same_origin_src = '/permissions-policy/resources/permissions-policy-serial.html';
 const cross_origin_src = sub + same_origin_src;
 const same_origin_worker_frame_src =
-    '/feature-policy/resources/feature-policy-serial-worker.html';
+    '/permissions-policy/resources/permissions-policy-serial-worker.html';
 const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
-const header = 'Feature-Policy header {"serial" : ["*"]}';
+const header = 'Permissions-Policy header serial=*';
 
 promise_test(
     () => navigator.serial.getPorts(),
@@ -44,6 +44,6 @@
 }, header + ' allows workers in cross-origin iframes.');
 
 fetch_tests_from_worker(new Worker(
-    'resources/serial-allowed-by-feature-policy-worker.js'));
+    'resources/serial-allowed-by-permissions-policy-worker.js'));
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy.https.sub.html.headers
new file mode 100644
index 0000000..27deb82
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/serial/serial-allowed-by-permissions-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: serial=*
diff --git a/third_party/blink/web_tests/external/wpt/serial/serial-default-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/serial/serial-default-permissions-policy.https.sub.html
similarity index 76%
rename from third_party/blink/web_tests/external/wpt/serial/serial-default-feature-policy.https.sub.html
rename to third_party/blink/web_tests/external/wpt/serial/serial-default-permissions-policy.https.sub.html
index 61a872f..e3908d9 100644
--- a/third_party/blink/web_tests/external/wpt/serial/serial-default-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/serial/serial-default-permissions-policy.https.sub.html
@@ -2,13 +2,13 @@
 <body>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
-<script src=/feature-policy/resources/featurepolicy.js></script>
+<script src=/permissions-policy/resources/permissions-policy.js></script>
 <script>
 'use strict';
-var same_origin_src = '/feature-policy/resources/feature-policy-serial.html';
+var same_origin_src = '/permissions-policy/resources/permissions-policy-serial.html';
 var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
   same_origin_src;
-var header = 'Default "serial" feature policy ["self"]';
+var header = 'Default "serial" permissions policy "self"';
 
 promise_test(
     () => navigator.serial.getPorts(),
diff --git a/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-feature-policy.https.sub.html.headers
deleted file mode 100644
index be3e6af..0000000
--- a/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-feature-policy.https.sub.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: serial 'none'
diff --git a/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-permissions-policy.https.sub.html
similarity index 80%
rename from third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-feature-policy.https.sub.html
rename to third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-permissions-policy.https.sub.html
index cddf157..53646c5 100644
--- a/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-feature-policy.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-permissions-policy.https.sub.html
@@ -2,16 +2,16 @@
 <body>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="/permissions-policy/resources/permissions-policy.js"></script>
 <script>
 'use strict';
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
-const same_origin_src = '/feature-policy/resources/feature-policy-serial.html';
+const same_origin_src = '/permissions-policy/resources/permissions-policy-serial.html';
 const cross_origin_src = sub + same_origin_src;
 const same_origin_worker_frame_src =
-    '/feature-policy/resources/feature-policy-serial-worker.html';
+    '/permissions-policy/resources/permissions-policy-serial-worker.html';
 const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
-const header = 'Feature-Policy header {"serial" : []}';
+const header = 'Permissions-Policy header serial=()';
 
 promise_test(() => {
   return navigator.serial.getPorts().then(() => {
@@ -43,6 +43,6 @@
 }, header + ' disallows workers in cross-origin iframes.');
 
 fetch_tests_from_worker(new Worker(
-    'resources/serial-disabled-by-feature-policy-worker.js'));
+    'resources/serial-disabled-by-permissions-policy-worker.js'));
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-permissions-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-permissions-policy.https.sub.html.headers
new file mode 100644
index 0000000..690b696
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/serial/serial-disabled-by-permissions-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: serial=()
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/accessibility/aria-owns-sends-notification-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/accessibility/aria-owns-sends-notification-expected.txt
index f48253f9..07bd145e 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/accessibility/aria-owns-sends-notification-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/accessibility/aria-owns-sends-notification-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
-PASS A children changed notification is fired when an aria-owned child gets added to the tree.
-FAIL A children changed notification is fired when an aria-owned attribute is added to an element that causes it to reparent another element to become its child. assert_equals: expected "ChildrenChanged" but got "MarkDirty"
-PASS A children changed notification is fired on the old parent when one of its children gets reparented to another element due to aria-owns.
+PASS A mark dirty notification is fired when an aria-owned child gets added to the tree.
+PASS A mark dirty notification is fired when an aria-owned attribute is added to an element that causes it to reparent another element to become its child.
+PASS A mark dirty notification is fired on the old parent when one of its children gets reparented to another element due to aria-owns.
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/anchor-focus.https-expected.txt b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/anchor-focus.https-expected.txt
index bb40a13..15dda60 100644
--- a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/anchor-focus.https-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/fenced-frame-mparch/wpt_internal/fenced_frame/anchor-focus.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
 PASS Anchor focusing is blocked on an element in a fenced frame without user activation.
-FAIL Anchor focusing is allowed on an element in a fenced frame with user activation. assert_true: element should get focus through anchor focusing expected true got false
+PASS Anchor focusing is allowed on an element in a fenced frame with user activation.
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac-mac11/external/wpt/screen-orientation/hidden_document-expected.txt b/third_party/blink/web_tests/platform/mac-mac11/external/wpt/screen-orientation/hidden_document-expected.txt
new file mode 100644
index 0000000..a7e9a96b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac11/external/wpt/screen-orientation/hidden_document-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL hidden documents must reject went trying to call lock or unlock assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL hidden documents must reject went trying to call unlock assert_throws_dom: function "() => screen.orientation.unlock()" did not throw
+FAIL hidden documents must not unlock the screen orientation assert_throws_dom: function "() => screen.orientation.unlock()" did not throw
+FAIL Once maximized, a minimized window can lock or unlock the screen orientation again assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/external/wpt/screen-orientation/hidden_document-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/external/wpt/screen-orientation/hidden_document-expected.txt
new file mode 100644
index 0000000..a7e9a96b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/external/wpt/screen-orientation/hidden_document-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL hidden documents must reject went trying to call lock or unlock assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL hidden documents must reject went trying to call unlock assert_throws_dom: function "() => screen.orientation.unlock()" did not throw
+FAIL hidden documents must not unlock the screen orientation assert_throws_dom: function "() => screen.orientation.unlock()" did not throw
+FAIL Once maximized, a minimized window can lock or unlock the screen orientation again assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-writing-modes/forms/textarea-rows-cols-sizing-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-writing-modes/forms/textarea-rows-cols-sizing-expected.txt
new file mode 100644
index 0000000..d6b47769
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-writing-modes/forms/textarea-rows-cols-sizing-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS textarea[style="writing-mode: horizontal-tb"] rows attribute changes the size correctly
+PASS textarea[style="writing-mode: horizontal-tb"] cols attribute changes the size correctly
+FAIL textarea[style="writing-mode: vertical-lr"] rows attribute changes the size correctly assert_equals: height shouldn't change when changing rows expected 36 but got 156
+FAIL textarea[style="writing-mode: vertical-lr"] cols attribute changes the size correctly assert_equals: width shouldn't change when changing cols expected 181 but got 341
+FAIL textarea[style="writing-mode: vertical-rl"] rows attribute changes the size correctly assert_equals: height shouldn't change when changing rows expected 36 but got 156
+FAIL textarea[style="writing-mode: vertical-rl"] cols attribute changes the size correctly assert_equals: width shouldn't change when changing cols expected 181 but got 341
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/css/css-writing-modes/forms/textarea-rows-cols-sizing-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/css/css-writing-modes/forms/textarea-rows-cols-sizing-expected.txt
new file mode 100644
index 0000000..e9d416a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/external/wpt/css/css-writing-modes/forms/textarea-rows-cols-sizing-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS textarea[style="writing-mode: horizontal-tb"] rows attribute changes the size correctly
+PASS textarea[style="writing-mode: horizontal-tb"] cols attribute changes the size correctly
+FAIL textarea[style="writing-mode: vertical-lr"] rows attribute changes the size correctly assert_equals: height shouldn't change when changing rows expected 38 but got 166
+FAIL textarea[style="writing-mode: vertical-lr"] cols attribute changes the size correctly assert_equals: width shouldn't change when changing cols expected 181 but got 341
+FAIL textarea[style="writing-mode: vertical-rl"] rows attribute changes the size correctly assert_equals: height shouldn't change when changing rows expected 38 but got 166
+FAIL textarea[style="writing-mode: vertical-rl"] cols attribute changes the size correctly assert_equals: width shouldn't change when changing cols expected 181 but got 341
+Harness: the test ran to completion.
+
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn
index 3da9e92..9cca1d5 100644
--- a/third_party/nearby/BUILD.gn
+++ b/third_party/nearby/BUILD.gn
@@ -762,6 +762,7 @@
   ]
   public_deps = [
     ":connections_credential_proto",
+    ":connections_device_metadata_proto",
     ":platform_base",
     ":platform_base_connection_info",
     ":platform_public_logging",
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index 26cf4f0..300e8045 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby
-Version: c499d18af696bf73b36a5db8c3388e01b5c41b0c
+Version: 8bd3d3458056c59801ecf2d86145844c4137070c
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 653b530..eca78917 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -6657,6 +6657,24 @@
   <int value="2" label="Not-Allowed error"/>
 </enum>
 
+<enum name="AutofillCvcAuthEvent">
+  <int value="0" label="Unknown result"/>
+  <int value="1" label="CVC auth succeeded"/>
+  <int value="2" label="CVC auth failed due to flow being cancelled"/>
+  <int value="3"
+      label="CVC auth failed due to auth error in UnmaskCard request"/>
+  <int value="4"
+      label="CVC auth failed due to virtual card retrieval error in
+             UnmaskCard request"/>
+  <int value="5"
+      label="CVC auth failed due to technical reason, such as closing the
+             page or lack of network connection"/>
+  <int value="6"
+      label="CVC auth temporarily failed due to the CVC being incorrect"/>
+  <int value="7"
+      label="CVC auth temporarily failed due to the card being expired"/>
+</enum>
+
 <enum name="AutofillDeveloperEngagement">
   <int value="0" label="Fillable form parsed without type hints"/>
   <int value="1" label="Fillable form parsed with type hints"/>
@@ -41633,6 +41651,7 @@
   <int value="4399"
       label="V8DocumentPictureInPictureEvent_Window_AttributeGetter"/>
   <int value="4400" label="DocumentPictureInPictureEnterEvent"/>
+  <int value="4401" label="SoftNavigationHeuristics"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
diff --git a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
index 2679487..5986333 100644
--- a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
@@ -124,6 +124,7 @@
 # enterprise
 poromov@chromium.org
 zmin@chromium.org
+seblalancette@chromium.org
 # event
 jonross@chromium.org
 # extensions
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index a91a42e7..fe645f2 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -1167,6 +1167,30 @@
   <token key="CardType" variants="Autofill.PaymentsRpcCardType"/>
 </histogram>
 
+<histogram name="Autofill.CvcAuth.{CardType}.Result"
+    enum="AutofillCvcAuthEvent" expires_after="2023-11-01">
+  <owner>vinnypersky@google.com</owner>
+  <owner>payments-autofill-team@google.com</owner>
+  <summary>
+    Logs the result of the OTP authentication, broken down into sub-histograms
+    by card type. The different possible results can be found in the
+    AutofillCvcAuthEvent enum, with the exception of any retriable failure,
+    which is logged in a separate histogram. This histogram is 1:1 with
+    Autofill.CvcAuth.{CardType}.Attempt
+  </summary>
+  <token key="CardType" variants="Autofill.PaymentsRpcCardType"/>
+</histogram>
+
+<histogram name="Autofill.CvcAuth.{CardType}.RetryableError"
+    enum="AutofillCvcAuthEvent" expires_after="2023-11-01">
+  <owner>vinnypersky@google.com</owner>
+  <owner>payments-autofill-team@google.com</owner>
+  <summary>
+    Logs any retryable failure that occurs during the CVC authentication flow.
+  </summary>
+  <token key="CardType" variants="Autofill.PaymentsRpcCardType"/>
+</histogram>
+
 <histogram name="Autofill.DaysSinceLastUse.CreditCard" units="days"
     expires_after="2023-04-16">
   <owner>battre@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/enterprise/OWNERS b/tools/metrics/histograms/metadata/enterprise/OWNERS
index 9dfc941..019abd5 100644
--- a/tools/metrics/histograms/metadata/enterprise/OWNERS
+++ b/tools/metrics/histograms/metadata/enterprise/OWNERS
@@ -4,3 +4,4 @@
 # Use chromium-metrics-reviews@google.com as a backup.
 poromov@chromium.org
 zmin@chromium.org
+seblalancette@chromium.org
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
index c5a793f..3eea233 100644
--- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -6323,6 +6323,7 @@
   <suffix base="true" name="Renderer"
       label="ThreadPools for renderer processes."/>
   <affected-histogram name="ThreadPool.NumTasksBeforeDetach"/>
+  <affected-histogram name="ThreadPool.UnnecessaryWakeup"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="ThreadPoolWorkerGroup" separator=".">
@@ -6333,6 +6334,10 @@
   <affected-histogram name="ThreadPool.NumTasksBeforeDetach.Browser"/>
   <affected-histogram name="ThreadPool.NumTasksBeforeDetach.ContentChild"/>
   <affected-histogram name="ThreadPool.NumTasksBeforeDetach.Renderer"/>
+  <affected-histogram name="ThreadPool.UnnecessaryWakeup.Browser"/>
+  <affected-histogram name="ThreadPool.UnnecessaryWakeup.ContentChild"/>
+  <affected-histogram name="ThreadPool.UnnecessaryWakeup.GPU"/>
+  <affected-histogram name="ThreadPool.UnnecessaryWakeup.Renderer"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="TileUiSurface" separator="." ordering="prefix,2">
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index a452673..a0f7e37 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -990,7 +990,7 @@
 </histogram>
 
 <histogram name="Navigation.MainFrameSchemeDifferentPageOTR2"
-    enum="NavigationScheme" expires_after="2022-11-01">
+    enum="NavigationScheme" expires_after="2023-05-07">
   <owner>estark@chromium.org</owner>
   <owner>elawrence@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
@@ -1003,7 +1003,7 @@
 </histogram>
 
 <histogram name="Navigation.MainFrameSchemeOTR2" enum="NavigationScheme"
-    expires_after="2022-11-01">
+    expires_after="2023-05-07">
   <owner>elawrence@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index e7d09326..82ff0061 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -9922,26 +9922,6 @@
   </summary>
 </histogram>
 
-<histogram name="OutdatedUpgradeBubble.NumLaterPerEnableAU" units="units"
-    expires_after="M85">
-  <owner>mad@chromium.org</owner>
-  <summary>
-    Counts the number of times the user clicked on the later button of the
-    outdated upgrade bubble, before clicking on the enable updates button in the
-    same Chrome session.
-  </summary>
-</histogram>
-
-<histogram name="OutdatedUpgradeBubble.NumLaterPerReinstall" units="units"
-    expires_after="M85">
-  <owner>mad@chromium.org</owner>
-  <summary>
-    Counts the number of times the user clicked on the later button of the
-    outdated upgrade bubble, before clicking on the reinstall button in the same
-    Chrome session.
-  </summary>
-</histogram>
-
 <histogram name="Overscroll.Cancelled3" enum="OverscrollNavigationType"
     expires_after="2021-02-21">
   <owner>nzolghadr@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 39eab10..60ca7033 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -258,6 +258,16 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.BrowserThrottle.CheckerOnIOLifetime" units="ms"
+    expires_after="2023-02-22">
+  <owner>thefrog@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records how long the BrowserURLLoaderThrottle::CheckerOnIO object exists
+    for. Logged when the object destructs.
+  </summary>
+</histogram>
+
 <histogram
     name="SafeBrowsing.BrowserThrottle.IsCheckCompletedOnProcessResponse"
     enum="BooleanCompleted" expires_after="2023-03-19">
@@ -323,6 +333,17 @@
   </token>
 </histogram>
 
+<histogram name="SafeBrowsing.BrowserThrottle.WillProcessResponseCount"
+    units="times" expires_after="2023-02-22">
+  <owner>thefrog@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records the number of times that WillProcessResponse has been called for a
+    single BrowserUrlLoaderThrottle. Logged each time WillProcessResponse is
+    called.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.CheckBrowseUrl.HasLocalMatch"
     enum="BooleanMatched" expires_after="2023-03-26">
   <owner>vakh@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/scheduler/histograms.xml b/tools/metrics/histograms/metadata/scheduler/histograms.xml
index 00f6bc48..e40c8c2 100644
--- a/tools/metrics/histograms/metadata/scheduler/histograms.xml
+++ b/tools/metrics/histograms/metadata/scheduler/histograms.xml
@@ -293,6 +293,19 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="ThreadPool.UnnecessaryWakeup" enum="BooleanHit"
+    expires_after="2023-02-01">
+  <owner>spvw@chromium.org</owner>
+  <owner>gab@chromium.org</owner>
+  <summary>
+    Records a hit when a thread pool worker thread woke up unnecessarily (when
+    the first GetWork called by a WorkerThread post-wakeup doesn't return a
+    task). This count can be compared between experiments aiming to reduce
+    wakeups. Or, it could be used to derive wakeups/{foo} by using UMA formulas
+    against other time-or-event-based metrics.
+  </summary>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index d01a077..b894725 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "perfetto-luci-artifacts/v31.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "667c12c719b90967bffa203b372a79a8cebb1fd2",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/28b1da6e19569e744e05174357f7be40c8bc22db/trace_processor_shell.exe"
+            "hash": "0e631c819173c90ff71d71dca4cae4bdaef27ca3",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/13e9b7767befb6c9f3f3957278449891f28146cb/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "6373f26144aad58f230d11d6a91efda5a09c9873",
             "full_remote_path": "perfetto-luci-artifacts/v31.0/linux-arm/trace_processor_shell"
         },
         "mac": {
-            "hash": "4be789cf532f44de7498fe501206a55a92c7dd7f",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/28b1da6e19569e744e05174357f7be40c8bc22db/trace_processor_shell"
+            "hash": "941ca66ad7b802091fcd60924eaf83ec7ee5088a",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/cf25b47c405ecca6bc775581f21ba4513e219035/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "5f47ee79e59d00bf3889d30ca52315522c158040",
             "full_remote_path": "perfetto-luci-artifacts/v31.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "b01aca9b22a14a5ed06e17d5125f7d73dd28e813",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/cf25b47c405ecca6bc775581f21ba4513e219035/trace_processor_shell"
+            "hash": "f040895f799f0d28f966722654952c35a010824e",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/13e9b7767befb6c9f3f3957278449891f28146cb/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/style_variable_generator/PRESUBMIT.py b/tools/style_variable_generator/PRESUBMIT.py
index 4d6155dd..2bf6443 100644
--- a/tools/style_variable_generator/PRESUBMIT.py
+++ b/tools/style_variable_generator/PRESUBMIT.py
@@ -12,7 +12,7 @@
 
 TEST_PATTERNS = [r'.+_test.py$']
 STYLE_VAR_GEN_INPUTS = [
-    r'^tools[\\\/]style_variable_generator[\\\/][^\/]+\.json5$'
+    r'^tools[\\\/]style_variable_generator[\\\/].+\.json5$'
 ]
 
 def _CommonChecks(input_api, output_api):
diff --git a/tools/style_variable_generator/presubmit_support.py b/tools/style_variable_generator/presubmit_support.py
index 54e2c721f..20d5dc6d 100644
--- a/tools/style_variable_generator/presubmit_support.py
+++ b/tools/style_variable_generator/presubmit_support.py
@@ -25,6 +25,8 @@
 
 
 def FindDeletedCSSVariables(input_api, output_api, input_file_filter):
+    # TODO(1312192): reenable after fixing presubmit exceptions
+    return []
     files = input_api.AffectedFiles(
         file_filter=lambda f: input_api.FilterSourceFile(
             f, files_to_check=input_file_filter))
diff --git a/tools/translation/upload_screenshots.py b/tools/translation/upload_screenshots.py
index e5659e2f..c325e579 100755
--- a/tools/translation/upload_screenshots.py
+++ b/tools/translation/upload_screenshots.py
@@ -107,6 +107,7 @@
     src_paths.extend(grd.structure_paths)
 
   screenshots = []
+  rename_to_lowercase_png = None
   for grd_path in src_paths:
     # Convert grd_path.grd to grd_path_grd/ directory.
     name, ext = os.path.splitext(os.path.basename(grd_path))
@@ -124,6 +125,20 @@
     for f in os.listdir(screenshots_dir):
       if f in ('OWNERS', 'README.md', 'DIR_METADATA') or f.endswith('.sha1'):
         continue
+
+      # Rename any files ending in .PNG to .png. File extensions on some
+      # platforms are case-sensitive, so renaming to .png ensures that created
+      # .png.sha1 files are the same type on all platforms.
+      if f.endswith('.PNG'):
+        if rename_to_lowercase_png is None:
+          rename_to_lowercase_png = query_yes_no(
+              '.PNG file(s) found, rename to .png for upload?')
+        if rename_to_lowercase_png:
+          f_path = os.path.join(screenshots_dir, f)
+          f = os.path.splitext(f)[0] + '.png'
+          f_path_lowercase_png = os.path.join(screenshots_dir, f)
+          os.rename(f_path, f_path_lowercase_png)
+
       if not f.endswith('.png'):
         print('File with unexpected extension: %s in %s' % (f, screenshots_dir))
         continue
diff --git a/tools/visual_debugger/app.html b/tools/visual_debugger/app.html
index 5be2cad..11d58ba3 100644
--- a/tools/visual_debugger/app.html
+++ b/tools/visual_debugger/app.html
@@ -267,7 +267,7 @@
   </div>
 
   <div class='section'>
-    <!--We need to fix viewer size to avoid scroll position change when 
+    <!--We need to fix viewer size to avoid scroll position change when
       multiple displays are present. crbug.com/1358526-->
     <div style="height:4000px;  border: 1px dotted gray;">
         <canvas id='canvas' style="top :0px"></canvas>
@@ -472,7 +472,7 @@
         canvas.addEventListener('wheel', function(e) {
           e.preventDefault();
           var delta = e.deltaY;
-          
+
           viewer.zoomToMouse(currentMouseX, currentMouseY, delta);
 
         }, {passive:false});
@@ -495,11 +495,11 @@
   }
 
 
-  function showModal(element) {
+  function showModal(element, focusSelector) {
     const container = document.querySelector('.modalContainer');
     container.appendChild(element);
     container.style.display = 'block';
-    element.focus();
+    element.querySelector(focusSelector).focus();
   }
 
   function hideModal() {
@@ -510,4 +510,4 @@
 
 </script>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/tools/visual_debugger/filter-ui.js b/tools/visual_debugger/filter-ui.js
index 3d47fce..a6c898d5 100644
--- a/tools/visual_debugger/filter-ui.js
+++ b/tools/visual_debugger/filter-ui.js
@@ -41,15 +41,15 @@
     <div class='row'>
       <div class='label' title='Filter annotation to match.
       For example frame.root.damage' >Annotation </div>
-        <div class='input'>
-          <input placeholder='Substring to match' id='annotation' size=40>
-          <!-- TODO: A fancy drop-down here would be nice. -->
-        </div>
+      <div class='input'>
+        <input placeholder='Substring to match' id='annotation' size=40>
+        <!-- TODO: A fancy drop-down here would be nice. -->
+      </div>
     </div>
     <div class='row'>
       <div class='label'>File name</div>
       <div class='input'>
-        <input placeholder='Substring to match  (usually empty!)'
+        <input placeholder='Substring to match (usually empty!)'
          id='filename' size=40>
         <!-- TODO: A fancy drop-down here would be nice. -->
       </div>
@@ -105,8 +105,7 @@
   }
 
   setUpButtons_() {
-    const button = this.querySelector('#saveFilter');
-    button.addEventListener('click', () => {
+    const SaveFilter = () => {
       let input = this.querySelector('#filename');
       const filename = input.value || undefined;
       input = this.querySelector('#functionname');
@@ -124,7 +123,11 @@
       this.dispatchEvent(new CustomEvent('saveFilter', {
         detail: { selector: { filename, func, anno }, action }
       }));
-    });
+    };
+
+    this.querySelector('#saveFilter').addEventListener('click', SaveFilter);
+    this.querySelector('#filter-container').addEventListener('keypress',
+      (e) => { if (e.key === 'Enter') SaveFilter(); });
   }
 };
 
@@ -237,8 +240,8 @@
   filterUi.style.top = (anchor.offsetTop + anchor.offsetHeight) + 'px';
   filterUi.style.left = (anchor.offsetLeft + 20) + 'px';
   filterUi.style.zIndex = maxZIndex;
-  
-  showModal(filterUi);
+
+  showModal(filterUi, '#annotation');
 }
 
 // Traverses through all the filter chips and enables/disables
@@ -329,7 +332,7 @@
   filterUi.style.left = (chip.offsetLeft + 20) + 'px';
   filterUi.style.zIndex = maxZIndex;
 
-  showModal(filterUi);
+  showModal(filterUi, '#annotation');
 
   var filter = Filter.getFilter(index);
 
diff --git a/tools/visual_debugger/frame.js b/tools/visual_debugger/frame.js
index fde2e93a..dc15565 100644
--- a/tools/visual_debugger/frame.js
+++ b/tools/visual_debugger/frame.js
@@ -147,6 +147,11 @@
       canvas.width = this.size_.width * scale;
       canvas.height = this.size_.height * scale;
     }
+    // Some text can be drawn past the canvas boundaries, so add some padding on
+    // each side.
+    const padding = 20;
+    canvas.width += padding * 2;
+    canvas.height += padding * 2;
   }
 
   getFilter(source_index) {
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index d3b43a3..833dac6 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -226,12 +226,6 @@
 );
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_ANDROID)
-const char kElasticOverscrollType[] = "type";
-const char kElasticOverscrollTypeFilter[] = "filter";
-const char kElasticOverscrollTypeTransform[] = "transform";
-#endif  // BUILDFLAG(IS_ANDROID)
-
 // Enables focus follow follow cursor (sloppyfocus).
 BASE_FEATURE(kFocusFollowsCursor,
              "FocusFollowsCursor",
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index f4ff60a..85552aa5 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -41,15 +41,6 @@
 COMPONENT_EXPORT(UI_BASE_FEATURES) BASE_DECLARE_FEATURE(kElasticOverscroll);
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_ANDROID)
-COMPONENT_EXPORT(UI_BASE_FEATURES)
-extern const char kElasticOverscrollType[];
-COMPONENT_EXPORT(UI_BASE_FEATURES)
-extern const char kElasticOverscrollTypeFilter[];
-COMPONENT_EXPORT(UI_BASE_FEATURES)
-extern const char kElasticOverscrollTypeTransform[];
-#endif  // BUILDFLAG(IS_ANDROID)
-
 #if BUILDFLAG(IS_WIN)
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 BASE_DECLARE_FEATURE(kApplyNativeOccludedRegionToWindowTracker);
diff --git a/ui/compositor/presentation_time_recorder.cc b/ui/compositor/presentation_time_recorder.cc
index e7c1f18..be83d87 100644
--- a/ui/compositor/presentation_time_recorder.cc
+++ b/ui/compositor/presentation_time_recorder.cc
@@ -11,7 +11,6 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
-#include "ui/gfx/presentation_feedback.h"
 
 namespace ui {
 
@@ -19,21 +18,6 @@
 
 bool report_immediately_for_test = false;
 
-std::string ToFlagString(uint32_t flags) {
-  std::string tmp;
-  if (flags & gfx::PresentationFeedback::kVSync)
-    tmp += "V,";
-  if (flags & gfx::PresentationFeedback::kFailure)
-    tmp += "F,";
-  if (flags & gfx::PresentationFeedback::kHWClock)
-    tmp += "HCL,";
-  if (flags & gfx::PresentationFeedback::kHWCompletion)
-    tmp += "HCO,";
-  if (flags & gfx::PresentationFeedback::kZeroCopy)
-    tmp += "Z";
-  return tmp;
-}
-
 }  // namespace
 
 // PresentationTimeRecorderInternal -------------------------------------------
@@ -53,9 +37,11 @@
       const PresentationTimeRecorderInternal&) = delete;
 
   ~PresentationTimeRecorderInternal() override {
+    const int average_latency_ms =
+        present_count_ ? total_latency_ms_ / present_count_ : 0;
     VLOG(1) << "Finished Recording FrameTime: average latency="
-            << average_latency_ms() << "ms, max latency=" << max_latency_ms()
-            << "ms, failure_ratio=" << failure_ratio();
+            << average_latency_ms << "ms, max latency=" << max_latency_ms_
+            << "ms";
     if (compositor_)
       compositor_->RemoveObserver(this);
   }
@@ -89,7 +75,7 @@
 
  protected:
   int max_latency_ms() const { return max_latency_ms_; }
-  int success_count() const { return success_count_; }
+  int present_count() const { return present_count_; }
 
  private:
   friend class TestApi;
@@ -109,21 +95,11 @@
 
   void OnPresented(int count,
                    base::TimeTicks requested_time,
-                   const gfx::PresentationFeedback& feedback);
-
-  int average_latency_ms() const {
-    return success_count_ ? total_latency_ms_ / success_count_ : 0;
-  }
-  int failure_ratio() const {
-    return failure_count_
-               ? (100 * failure_count_) / (success_count_ + failure_count_)
-               : 0;
-  }
+                   base::TimeTicks presentation_timestamp);
 
   State state_ = PRESENTED;
 
-  int success_count_ = 0;
-  int failure_count_ = 0;
+  int present_count_ = 0;
   int request_count_ = 0;
   int total_latency_ms_ = 0;
   int max_latency_ms_ = 0;
@@ -150,13 +126,11 @@
 
   if (report_immediately_for_test) {
     state_ = COMMITTED;
-    gfx::PresentationFeedback feedback;
-    feedback.timestamp = now;
-    OnPresented(request_count_++, now, feedback);
+    OnPresented(request_count_++, now, now);
     return true;
   }
 
-  compositor_->RequestPresentationTimeForNextFrame(
+  compositor_->RequestSuccessfulPresentationTimeForNextFrame(
       base::BindOnce(&PresentationTimeRecorderInternal::OnPresented,
                      weak_ptr_factory_.GetWeakPtr(), request_count_++, now));
   return true;
@@ -165,7 +139,7 @@
 void PresentationTimeRecorder::PresentationTimeRecorderInternal::OnPresented(
     int count,
     base::TimeTicks requested_time,
-    const gfx::PresentationFeedback& feedback) {
+    base::TimeTicks presentation_timestamp) {
   std::unique_ptr<PresentationTimeRecorderInternal> deleter;
   if (!recording_ && (count == (request_count_ - 1)))
     deleter = base::WrapUnique(this);
@@ -173,34 +147,27 @@
   if (state_ == COMMITTED)
     state_ = PRESENTED;
 
-  if (feedback.flags & gfx::PresentationFeedback::kFailure) {
-    failure_count_++;
-    LOG(WARNING) << "PresentationFailed (" << count << "):"
-                 << ", flags=" << ToFlagString(feedback.flags);
-    return;
-  }
-  if (feedback.timestamp.is_null()) {
+  if (presentation_timestamp.is_null()) {
     // TODO(b/165951963): ideally feedback.timestamp should not be null.
     // Consider replacing this by DCHECK or CHECK.
     LOG(ERROR) << "Invalid feedback timestamp (" << count << "):"
                << " timestamp is not set";
     return;
   }
-  const base::TimeDelta delta = feedback.timestamp - requested_time;
+  const base::TimeDelta delta = presentation_timestamp - requested_time;
   if (delta.InMilliseconds() < 0) {
     LOG(ERROR) << "Invalid timestamp for presentation feedback (" << count
                << "): requested_time=" << requested_time
-               << " feedback.timestamp=" << feedback.timestamp;
+               << " presentation_timestamp=" << presentation_timestamp;
     return;
   }
   if (delta.InMilliseconds() > max_latency_ms_)
     max_latency_ms_ = delta.InMilliseconds();
 
-  success_count_++;
+  present_count_++;
   total_latency_ms_ += delta.InMilliseconds();
   ReportTime(delta);
-  VLOG(1) << "OnPresented (" << count << "):" << delta.InMilliseconds()
-          << ",flags=" << ToFlagString(feedback.flags);
+  VLOG(1) << "OnPresented (" << count << "):" << delta.InMilliseconds();
 }
 
 // PresentationTimeRecorder ---------------------------------------------------
@@ -257,7 +224,7 @@
       const PresentationTimeHistogramRecorder&) = delete;
 
   ~PresentationTimeHistogramRecorder() override {
-    if (success_count() > 0 && !max_latency_histogram_name_.empty()) {
+    if (present_count() > 0 && !max_latency_histogram_name_.empty()) {
       CreateTimesHistogram(max_latency_histogram_name_.c_str())
           ->AddTimeMillisecondsGranularity(
               base::Milliseconds(max_latency_ms()));
@@ -300,20 +267,9 @@
 void PresentationTimeRecorder::TestApi::OnPresented(
     int count,
     base::TimeTicks requested_time,
-    const gfx::PresentationFeedback& feedback) {
-  recorder_->recorder_internal_->OnPresented(count, requested_time, feedback);
-}
-
-int PresentationTimeRecorder::TestApi::GetMaxLatencyMs() const {
-  return recorder_->recorder_internal_->max_latency_ms();
-}
-
-int PresentationTimeRecorder::TestApi::GetSuccessCount() const {
-  return recorder_->recorder_internal_->success_count();
-}
-
-int PresentationTimeRecorder::TestApi::GetFailureRatio() const {
-  return recorder_->recorder_internal_->failure_ratio();
+    base::TimeTicks presentation_timestamp) {
+  recorder_->recorder_internal_->OnPresented(count, requested_time,
+                                             presentation_timestamp);
 }
 
 }  // namespace ui
diff --git a/ui/compositor/presentation_time_recorder.h b/ui/compositor/presentation_time_recorder.h
index 51049e2..3300343 100644
--- a/ui/compositor/presentation_time_recorder.h
+++ b/ui/compositor/presentation_time_recorder.h
@@ -34,11 +34,7 @@
     void OnCompositingDidCommit(ui::Compositor* compositor);
     void OnPresented(int count,
                      base::TimeTicks requested_time,
-                     const gfx::PresentationFeedback& feedback);
-
-    int GetMaxLatencyMs() const;
-    int GetSuccessCount() const;
-    int GetFailureRatio() const;
+                     base::TimeTicks presentation_timestamp);
 
    private:
     raw_ptr<PresentationTimeRecorder> recorder_;
diff --git a/ui/compositor/presentation_time_recorder_unittest.cc b/ui/compositor/presentation_time_recorder_unittest.cc
index 230163a0..73532fda 100644
--- a/ui/compositor/presentation_time_recorder_unittest.cc
+++ b/ui/compositor/presentation_time_recorder_unittest.cc
@@ -93,25 +93,6 @@
   histogram_tester.ExpectTotalCount(kMaxLatencyName, 1);
 }
 
-TEST_F(PresentationTimeRecorderTest, NoSuccessNoHistogram) {
-  base::HistogramTester histogram_tester;
-  auto* compositor = host_->GetCompositor();
-  auto test_recorder = CreatePresentationTimeHistogramRecorder(
-      compositor, kName, kMaxLatencyName);
-  PresentationTimeRecorder::TestApi test_api(test_recorder.get());
-  base::TimeDelta interval_not_used = base::Milliseconds(0);
-  gfx::PresentationFeedback failure(
-      base::TimeTicks() + base::Milliseconds(2000), interval_not_used,
-      gfx::PresentationFeedback::kFailure);
-  base::TimeTicks start = base::TimeTicks() + base::Milliseconds(1000);
-  test_recorder->RequestNext();
-  test_api.OnPresented(0, start, failure);
-
-  test_recorder.reset();
-  histogram_tester.ExpectTotalCount(kName, 0);
-  histogram_tester.ExpectTotalCount(kMaxLatencyName, 0);
-}
-
 TEST_F(PresentationTimeRecorderTest, DelayedHistogram) {
   base::HistogramTester histogram_tester;
   auto* compositor = host_->GetCompositor();
@@ -131,28 +112,4 @@
   histogram_tester.ExpectTotalCount(kMaxLatencyName, 1);
 }
 
-TEST_F(PresentationTimeRecorderTest, Failure) {
-  auto* compositor = host_->GetCompositor();
-  auto test_recorder = CreatePresentationTimeHistogramRecorder(
-      compositor, kName, kMaxLatencyName);
-  PresentationTimeRecorder::TestApi test_api(test_recorder.get());
-  test_recorder->RequestNext();
-  test_api.OnCompositingDidCommit(compositor);
-  base::TimeDelta interval_not_used = base::Milliseconds(0);
-  base::TimeTicks start = base::TimeTicks() + base::Milliseconds(1000);
-  gfx::PresentationFeedback success(
-      base::TimeTicks() + base::Milliseconds(1100), interval_not_used,
-      /*flags=*/0);
-  test_api.OnPresented(0, start, success);
-  EXPECT_EQ(100, test_api.GetMaxLatencyMs());
-  EXPECT_EQ(1, test_api.GetSuccessCount());
-  gfx::PresentationFeedback failure(
-      base::TimeTicks() + base::Milliseconds(2000), interval_not_used,
-      gfx::PresentationFeedback::kFailure);
-  test_api.OnPresented(0, start, failure);
-  // Failure should not be included in max latency.
-  EXPECT_EQ(100, test_api.GetMaxLatencyMs());
-  EXPECT_EQ(50, test_api.GetFailureRatio());
-}
-
 }  // namespace ui
diff --git a/ui/message_center/views/message_popup_collection.cc b/ui/message_center/views/message_popup_collection.cc
index 42d20b7..5e40c08a 100644
--- a/ui/message_center/views/message_popup_collection.cc
+++ b/ui/message_center/views/message_popup_collection.cc
@@ -149,6 +149,11 @@
 MessageView* MessagePopupCollection::GetMessageViewForNotificationId(
     const std::string& notification_id) {
   auto it = base::ranges::find_if(popup_items_, [&](const auto& child) {
+    // Exit early if the popup ptr has been set to nullptr by
+    // `NotifyPopupClosed` but has not been cleared from `popup_items_`.
+    if (!child.popup)
+      return false;
+
     auto* widget = child.popup->GetWidget();
     // Do not return popups that are in the process of closing, but have not
     // yet been removed from `popup_items_`.
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_output.cc b/ui/ozone/platform/wayland/host/wayland_zaura_output.cc
index d07638114..da0ef72c 100644
--- a/ui/ozone/platform/wayland/host/wayland_zaura_output.cc
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_output.cc
@@ -17,8 +17,9 @@
   DCHECK(obj_);
 
   static constexpr zaura_output_listener kZAuraOutputListener = {
-      &OnScale,  &OnConnection,       &OnDeviceScaleFactor,
-      &OnInsets, &OnLogicalTransform, &OnDisplayId};
+      &OnScale,    &OnConnection,       &OnDeviceScaleFactor,
+      &OnInsets,   &OnLogicalTransform, &OnDisplayId,
+      &OnActivated};
   zaura_output_add_listener(obj_.get(), &kZAuraOutputListener, this);
 }
 
@@ -72,4 +73,7 @@
   }
 }
 
+void WaylandZAuraOutput::OnActivated(void* data,
+                                     struct zaura_output* zaura_output) {}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_output.h b/ui/ozone/platform/wayland/host/wayland_zaura_output.h
index 3432fd2..f6a1a7131 100644
--- a/ui/ozone/platform/wayland/host/wayland_zaura_output.h
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_output.h
@@ -63,6 +63,7 @@
                           struct zaura_output* zaura_output,
                           uint32_t display_id_hi,
                           uint32_t display_id_lo);
+  static void OnActivated(void* data, struct zaura_output* zaura_output);
 
   wl::Object<zaura_output> obj_;
   gfx::Insets insets_;
diff --git a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts
index ed8059a..3a5b136 100644
--- a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts
+++ b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from 'chrome://resources/js/assert.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 './cr_a11y_announcer.html.js';
diff --git a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.ts b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.ts
index ae06420..636bf59 100644
--- a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.ts
+++ b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.ts
@@ -6,7 +6,7 @@
 
 import {FlattenedNodesObserver, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {assert} from '../../js/assert.js';
+import {assert} from '../../js/assert_ts.js';
 import {FocusOutlineManager} from '../../js/focus_outline_manager.js';
 import {FocusRow} from '../../js/focus_row.js';
 import {focusWithoutInk} from '../../js/focus_without_ink.js';
@@ -302,7 +302,8 @@
     this.$.dialog.close();
     this.open = false;
     if (this.anchorElement_) {
-      focusWithoutInk(assert(this.anchorElement_));
+      assert(this.anchorElement_);
+      focusWithoutInk(this.anchorElement_);
       this.anchorElement_ = null;
     }
     if (this.lastConfig_) {
diff --git a/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts b/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts
index b4a91a19..a80318f 100644
--- a/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts
+++ b/ui/webui/resources/cr_elements/cr_menu_selector/cr_menu_selector.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from '../../js/assert.js';
+import {assert} from '../../js/assert_ts.js';
 import {FocusOutlineManager} from '../../js/focus_outline_manager.js';
 
 export class CrMenuSelector extends HTMLElement {
diff --git a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.ts b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.ts
index 7eec7f5..f0a3499 100644
--- a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.ts
+++ b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector_grid.ts
@@ -9,7 +9,7 @@
 
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {assert} from '../../js/assert.js';
+import {assert} from '../../js/assert_ts.js';
 import {hasKeyModifiers} from '../../js/util_ts.js';
 
 import {getTemplate} from './cr_profile_avatar_selector_grid.html.js';