diff --git a/DEPS b/DEPS
index 47d8f4d..ff573a24 100644
--- a/DEPS
+++ b/DEPS
@@ -275,15 +275,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': 'fb2d7d7a5975cc8cc74086f1b2ed910cfcd2b309',
+  'skia_revision': 'a3d2d625093786d7b3d906ac13f17341e2a423c8',
   # 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': '2425750cbd27e8ed125d16e4a31cd779fd286aaa',
+  'v8_revision': 'ee936caafb23c6fae7f69d98c579681d9e7bca92',
   # 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': 'ae74bdb3db5154646af1a85bb8a9e102fa954efa',
+  'angle_revision': '213f669d39aa9ce8cfba930549154686f7624fec',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -354,7 +354,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': 'b6a3b2ae8a4c1d5847c2bb1535377e13ee3045be',
+  'devtools_frontend_revision': '94a5f6c5fece0c4d970ce5765410215bc04e5a79',
   # 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.
@@ -390,7 +390,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'd7d55e48592070ebfe8a053dbfa342e8a6b2552d',
+  'dawn_revision': 'fa5cd029d1175d8d30ce3b528c40641be230603a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -457,7 +457,7 @@
   'libcxx_revision':       'b1269813eaf5b8ac78e35e45a0f7cc320bd3e7d6',
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:c547ca1497e3ff0dcbc0b2cb036b3d40380cbeeb',
+  'gn_version': 'git_revision:37baefb026b199605affa7bcb24810d1724ce373',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -920,7 +920,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'rub6aXBz5r6WR9pBd1H-bRnRkT8jX123XYM5pQG_xhwC',
+          'version': 'aaLNF-LbuC3COrA7CxH-p6Kk9DXknyL3CJbRKKLARSoC',
       },
     ],
     'condition': 'checkout_android',
@@ -1113,7 +1113,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3f862120bceef5b6d7b2c0e4750282f8539b24a1',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e2bf3a6f503a80623e848902461b45e64c973bb0',
       'condition': 'checkout_chromeos',
   },
 
@@ -1584,7 +1584,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'ovozeRSDDfERnEFpDo_WS6OYOcEF7oT1JzGxCSf-g0kC',
+              'version': 'L2ZGhyJ-Hc8AnXHo5dhtNdI2F2bC_9yi2lqTbJ98sLcC',
           },
       ],
       'condition': 'checkout_android',
@@ -1705,7 +1705,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'bb289ce3cb15bbabd42fdcb01439367846d9069d',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '70091fdb8d3d037b8f2919eb63e5a2bf6444deb7',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '24c2a787daefb05e27c1689f5ef6cc491eeadb1c',
 
   'src/third_party/webrtc':
     Var('webrtc_git') + '/src.git' + '@' + 'fc2c24ef4406484a2e6bec4d7aa44a2c78e6cdd0',
@@ -1781,7 +1781,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1de44161d87b560d5591e9794365c430a93035b0',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@518383f78e719d4db6fafb3f24542f6f226cb62f',
     'condition': 'checkout_src_internal',
   },
 
@@ -1800,7 +1800,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/eche_app/app',
-        'version': 'PsOW8S_6c5amu3xQkVNmX06QA-OglSYYV0iTBY-a2UsC',
+        'version': 'Q9HVLBI7O4qIUO4vCNkNTRDJlHpQcAA00VUrHKZt6TMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/components/arc/arc_features.cc b/ash/components/arc/arc_features.cc
index 9664552..461778d 100644
--- a/ash/components/arc/arc_features.cc
+++ b/ash/components/arc/arc_features.cc
@@ -140,7 +140,7 @@
 
 // When enabled, unclaimed USB device will be attached to ARCVM by default.
 const base::Feature kUsbDeviceDefaultAttachToArcVm{
-    "UsbDeviceDefaultAttachToArcVm", base::FEATURE_DISABLED_BY_DEFAULT};
+    "UsbDeviceDefaultAttachToArcVm", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls ARC USB Storage UI feature.
 // When enabled, chrome://settings and Files.app will ask if the user wants
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 7abd0bb..f7f7d9f 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -695,12 +695,6 @@
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables mounting various archive formats (in two tiers) in Files App. This
-// flag controls the first tier, whose support is very good.
-// https://crbug.com/1216245
-const base::Feature kFilesArchivemount{"FilesArchivemount",
-                                       base::FEATURE_ENABLED_BY_DEFAULT};
-
-// Enables mounting various archive formats (in two tiers) in Files App. This
 // flag controls the second tier, whose support is more experimental.
 // https://crbug.com/1216245
 const base::Feature kFilesArchivemount2{"FilesArchivemount2",
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index fe738c5..402c13dd 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -286,7 +286,6 @@
 extern const base::Feature kFastPairSubsequentPairingUX;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kFastPairSavedDevices;
-COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesArchivemount;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesArchivemount2;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesExtractArchive;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesSWA;
diff --git a/ash/system/power/adaptive_charging_controller.cc b/ash/system/power/adaptive_charging_controller.cc
index ba58ec55..f24c35a 100644
--- a/ash/system/power/adaptive_charging_controller.cc
+++ b/ash/system/power/adaptive_charging_controller.cc
@@ -71,6 +71,7 @@
   if (!proto.has_adaptive_charging_heuristic_enabled() ||
       !proto.adaptive_charging_heuristic_enabled() ||
       !is_adaptive_delaying_charge_) {
+    notification_controller_->CloseAdaptiveChargingNotification();
     return;
   }
 
diff --git a/ash/system/power/adaptive_charging_notification_controller.cc b/ash/system/power/adaptive_charging_notification_controller.cc
index 20668d2..1c200f4 100644
--- a/ash/system/power/adaptive_charging_notification_controller.cc
+++ b/ash/system/power/adaptive_charging_notification_controller.cc
@@ -78,6 +78,12 @@
       std::move(notification));
 }
 
+void AdaptiveChargingNotificationController::CloseAdaptiveChargingNotification(
+    bool by_user) {
+  message_center::MessageCenter::Get()->RemoveNotification(kInfoNotificationId,
+                                                           by_user);
+}
+
 bool AdaptiveChargingNotificationController::ShouldShowNotification() {
   PrefService* pref_service =
       Shell::Get()->session_controller()->GetActivePrefService();
@@ -93,6 +99,7 @@
     return;
   if (button_index.value() == 0) {
     PowerManagerClient::Get()->ChargeNowForAdaptiveCharging();
+    CloseAdaptiveChargingNotification(/*by_user=*/true);
   } else {
     NOTREACHED() << "Unknown button index value";
   }
diff --git a/ash/system/power/adaptive_charging_notification_controller.h b/ash/system/power/adaptive_charging_notification_controller.h
index 35eb065..641aefa 100644
--- a/ash/system/power/adaptive_charging_notification_controller.h
+++ b/ash/system/power/adaptive_charging_notification_controller.h
@@ -34,6 +34,8 @@
   void ShowAdaptiveChargingNotification(
       absl::optional<int> hours_to_full = absl::nullopt);
 
+  void CloseAdaptiveChargingNotification(bool by_user = false);
+
   bool ShouldShowNotification();
 
   // message_center::NotificationObserver:
diff --git a/ash/system/power/adaptive_charging_notification_controller_unittest.cc b/ash/system/power/adaptive_charging_notification_controller_unittest.cc
index 23cf76c..9277cb6 100644
--- a/ash/system/power/adaptive_charging_notification_controller_unittest.cc
+++ b/ash/system/power/adaptive_charging_notification_controller_unittest.cc
@@ -60,6 +60,13 @@
     return controller_.get();
   }
 
+  void SimulateClick(int button_index) {
+    message_center::Notification* notification =
+        message_center::MessageCenter::Get()->FindVisibleNotificationById(
+            /*id=*/"adaptive-charging-notify-info");
+    notification->delegate()->Click(button_index, absl::nullopt);
+  }
+
  private:
   std::unique_ptr<AdaptiveChargingNotificationController> controller_;
 };
@@ -117,4 +124,15 @@
   EXPECT_NE(notification->message().find(u"5:42 pm"), std::u16string::npos);
 }
 
+TEST_F(AdaptiveChargingNotificationControllerTest,
+       ClickButtonMakesNotificationDisappear) {
+  SetAdaptiveChargingPref(true);
+  GetController()->ShowAdaptiveChargingNotification(5);
+  EXPECT_EQ(VisibleNotificationCount(), 1u);
+
+  // Notification should disappear after click.
+  SimulateClick(/*button_index=*/0);
+  EXPECT_EQ(VisibleNotificationCount(), 0u);
+}
+
 }  // namespace ash
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc
index 69cbbf5..14bfc431 100644
--- a/ash/system/time/calendar_view_unittest.cc
+++ b/ash/system/time/calendar_view_unittest.cc
@@ -1375,7 +1375,8 @@
   EXPECT_EQ(u"2021", header_year()->GetText());
 }
 
-TEST_F(CalendarViewAnimationTest, ResetToTodayWithAnimation) {
+// TODO(https://crbug.com/1329801): Test is flaky.
+TEST_F(CalendarViewAnimationTest, DISABLED_ResetToTodayWithAnimation) {
   ui::ScopedAnimationDurationScaleMode test_duration_mode(
       ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
 
diff --git a/ash/webui/camera_app_ui/resources/js/device/camera3_device_info.ts b/ash/webui/camera_app_ui/resources/js/device/camera3_device_info.ts
index 7e391a18..a111752 100644
--- a/ash/webui/camera_app_ui/resources/js/device/camera3_device_info.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/camera3_device_info.ts
@@ -127,7 +127,7 @@
       Promise<Camera3DeviceInfo> {
     const deviceId = deviceInfo.deviceId;
 
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     if (!deviceOperator) {
       throw new Error('Device operation is not supported');
     }
diff --git a/ash/webui/camera_app_ui/resources/js/device/camera_operation.ts b/ash/webui/camera_app_ui/resources/js/device/camera_operation.ts
index 3311e941..64f77054 100644
--- a/ash/webui/camera_app_ui/resources/js/device/camera_operation.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/camera_operation.ts
@@ -135,7 +135,7 @@
   private async *
       getConfigurationCandidates(cameraInfo: CameraInfo):
           AsyncIterable<ConfigureCandidate> {
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
 
     for (const deviceId of this.getDeviceIdCandidates(cameraInfo)) {
       for (const mode of await this.getModeCandidates(deviceId)) {
@@ -210,7 +210,7 @@
       return false;
     }
 
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     state.set(state.State.USE_FAKE_CAMERA, deviceOperator === null);
 
     for await (const c of this.getConfigurationCandidates(cameraInfo)) {
@@ -363,7 +363,7 @@
         defaultFacing,
     );
     this.capturer = new Capturer(this.modes);
-    this.infoUpdater.addDeviceChangeListener(async (updater) => {
+    this.infoUpdater.addDeviceChangeListener((updater) => {
       const info = new CameraInfo(updater);
       if (this.ongoingOperationType !== null) {
         this.pendingUpdateInfo = info;
diff --git a/ash/webui/camera_app_ui/resources/js/device/device_info_updater.ts b/ash/webui/camera_app_ui/resources/js/device/device_info_updater.ts
index d5a32c3..88611f5 100644
--- a/ash/webui/camera_app_ui/resources/js/device/device_info_updater.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/device_info_updater.ts
@@ -32,16 +32,6 @@
       Array<(updater: DeviceInfoUpdater) => void> = [];
 
   /**
-   * Action locking update of camera information.
-   */
-  private lockingUpdate: Promise<void>|null = null;
-
-  /**
-   * Pending camera information update while update capability is locked.
-   */
-  private pendingUpdate: Promise<void>|null = null;
-
-  /**
    * MediaDeviceInfo of all available video devices.
    */
   private devicesInfo: MediaDeviceInfo[] = [];
@@ -58,48 +48,18 @@
   private pendingDevicesInfo: DeviceInfo[] = [];
 
   constructor() {
-    StreamManager.getInstance().addRealDeviceChangeListener(
-        async (devicesInfo) => {
-          this.pendingDevicesInfo = devicesInfo;
-          await this.update();
-        });
-  }
-
-  /**
-   * Tries to gain lock and initiates update process.
-   */
-  private async update() {
-    if (this.lockingUpdate) {
-      if (this.pendingUpdate) {
-        return;
-      }
-      this.pendingUpdate = (async () => {
-        while (this.lockingUpdate) {
-          try {
-            await this.lockingUpdate;
-          } catch (e) {
-            // Ignore exception from waiting for existing update.
-          }
-        }
-        this.lockingUpdate = this.pendingUpdate;
-        this.pendingUpdate = null;
-        await this.doUpdate();
-        this.lockingUpdate = null;
-      })();
-    } else {
-      this.lockingUpdate = (async () => {
-        await this.doUpdate();
-        this.lockingUpdate = null;
-      })();
-    }
+    StreamManager.getInstance().addRealDeviceChangeListener((devicesInfo) => {
+      this.pendingDevicesInfo = devicesInfo;
+      this.update();
+    });
   }
 
   /**
    * Updates devices information.
    */
-  private async doUpdate() {
+  private update() {
     this.devicesInfo = this.pendingDevicesInfo.map((d) => d.v1Info);
-    if (await DeviceOperator.isSupported()) {
+    if (DeviceOperator.isSupported()) {
       this.camera3DevicesInfo =
           this.pendingDevicesInfo.map((d) => assertExists(d.v3Info));
     } else {
@@ -119,35 +79,6 @@
   }
 
   /**
-   * Requests to lock update of device information. This function is preserved
-   * for device information reader to lock the update capability so as to ensure
-   * getting consistent data between all information providers.
-   *
-   * @param callback Called after update capability is locked. Getting
-   *     information from all providers in callback are guaranteed to be
-   *     consistent.
-   */
-  async lockDeviceInfo(callback: () => Promise<void>): Promise<void> {
-    await StreamManager.getInstance().deviceUpdate();
-    while (this.lockingUpdate || this.pendingUpdate) {
-      try {
-        await this.lockingUpdate;
-        await this.pendingUpdate;
-      } catch (e) {
-        // Ignore exception from waiting for existing update.
-      }
-    }
-    this.lockingUpdate = (async () => {
-      try {
-        await callback();
-      } finally {
-        this.lockingUpdate = null;
-      }
-    })();
-    await this.lockingUpdate;
-  }
-
-  /**
    * Gets MediaDeviceInfo for all available video devices.
    */
   getDevicesInfo(): MediaDeviceInfo[] {
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/index.ts b/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
index 817eed8..5a2a092 100644
--- a/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
@@ -130,7 +130,7 @@
     async function prepareDeviceForPhoto(
         constraints: StreamConstraints, resolution: Resolution,
         captureIntent: CaptureIntent): Promise<void> {
-      const deviceOperator = await DeviceOperator.getInstance();
+      const deviceOperator = DeviceOperator.getInstance();
       if (deviceOperator === null) {
         return;
       }
@@ -150,7 +150,7 @@
         isSupported: async () => true,
         isSupportPTZ: () => true,
         prepareDevice: async (constraints) => {
-          const deviceOperator = await DeviceOperator.getInstance();
+          const deviceOperator = DeviceOperator.getInstance();
           if (deviceOperator === null) {
             return;
           }
@@ -213,7 +213,7 @@
           if (deviceId === null) {
             return false;
           }
-          const deviceOperator = await DeviceOperator.getInstance();
+          const deviceOperator = DeviceOperator.getInstance();
           if (deviceOperator === null) {
             return false;
           }
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/video.ts b/ash/webui/camera_app_ui/resources/js/device/mode/video.ts
index 13476ab..44a5edb7 100644
--- a/ash/webui/camera_app_ui/resources/js/device/mode/video.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/mode/video.ts
@@ -250,7 +250,7 @@
    * @return Returns whether taking video sanpshot via Blob stream is enabled.
    */
   async isBlobVideoSnapshotEnabled(): Promise<boolean> {
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     const {deviceId} = this.video.getVideoSettings();
     return deviceOperator !== null &&
         (await deviceOperator.isBlobVideoSnapshotEnabled(deviceId));
diff --git a/ash/webui/camera_app_ui/resources/js/device/preview.ts b/ash/webui/camera_app_ui/resources/js/device/preview.ts
index f97c086..0ea2ef4 100644
--- a/ash/webui/camera_app_ui/resources/js/device/preview.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/preview.ts
@@ -165,7 +165,7 @@
   }
 
   private async updatePTZ() {
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     const {pan, tilt, zoom} = this.getVideoTrack().getCapabilities();
 
     this.isSupportPTZInternal = await (async () => {
@@ -320,7 +320,7 @@
       this.updateShowMetadata();
       await this.updatePTZ();
 
-      const deviceOperator = await DeviceOperator.getInstance();
+      const deviceOperator = DeviceOperator.getInstance();
       if (deviceOperator !== null) {
         const {deviceId} = getVideoTrackSettings(this.getVideoTrack());
         const isSuccess =
@@ -358,7 +358,7 @@
       const track = this.getVideoTrack();
       const {deviceId} = getVideoTrackSettings(track);
       track.stop();
-      const deviceOperator = await DeviceOperator.getInstance();
+      const deviceOperator = DeviceOperator.getInstance();
       if (deviceOperator !== null) {
         deviceOperator.dropConnection(deviceId);
       }
@@ -550,7 +550,7 @@
       };
     })();
 
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     if (!deviceOperator) {
       return;
     }
@@ -643,11 +643,6 @@
       return;
     }
 
-    const deviceOperator = await DeviceOperator.getInstance();
-    if (!deviceOperator) {
-      return;
-    }
-
     closeEndpoint(this.metadataObserver);
     this.metadataObserver = null;
 
diff --git a/ash/webui/camera_app_ui/resources/js/device/stream_manager.ts b/ash/webui/camera_app_ui/resources/js/device/stream_manager.ts
index 0feb667a..8f113683 100644
--- a/ash/webui/camera_app_ui/resources/js/device/stream_manager.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/stream_manager.ts
@@ -58,8 +58,7 @@
   /**
    * Listeners for real device change event.
    */
-  private readonly realListeners:
-      Array<(devices: DeviceInfo[]) => Promise<void>> = [];
+  private readonly realListeners: Array<(devices: DeviceInfo[]) => void> = [];
 
   /**
    * Latest result of Camera3DeviceInfo of all real video devices.
@@ -115,8 +114,7 @@
    * Registers listener to be called when state of available real devices
    * changes.
    */
-  addRealDeviceChangeListener(
-      listener: (devices: DeviceInfo[]) => Promise<void>): void {
+  addRealDeviceChangeListener(listener: (devices: DeviceInfo[]) => void): void {
     this.realListeners.push(listener);
   }
 
@@ -126,7 +124,7 @@
   async openCaptureStream(constraints: StreamConstraints):
       Promise<MediaStream> {
     const realDeviceId = constraints.deviceId;
-    if (await DeviceOperator.isSupported()) {
+    if (DeviceOperator.isSupported()) {
       try {
         await this.setVirtualDeviceEnabled(realDeviceId, true);
         assert(this.virtualMap !== null);
@@ -146,7 +144,7 @@
    */
   async closeCaptureStream(captureStream: MediaStream): Promise<void> {
     assertExists(captureStream.getVideoTracks()[0]).stop();
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     if (deviceOperator !== null) {
       // We need to cache |virtualId| first since it will be wiped out after
       // disabling multi-stream.
@@ -263,7 +261,7 @@
   private async queryMojoDevicesInfo(): Promise<DeviceInfo[]|null> {
     const deviceInfos = await this.devicesInfo;
     assert(deviceInfos !== null);
-    const isV3Supported = await DeviceOperator.isSupported();
+    const isV3Supported = DeviceOperator.isSupported();
     return Promise.all(deviceInfos.map(
         async (d) => ({
           v1Info: d,
@@ -283,7 +281,7 @@
    */
   async setVirtualDeviceEnabled(deviceId: string, enabled: boolean):
       Promise<void> {
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     assert(deviceOperator !== null);
 
     if (enabled) {
diff --git a/ash/webui/camera_app_ui/resources/js/main.ts b/ash/webui/camera_app_ui/resources/js/main.ts
index edf526b..8429626 100644
--- a/ash/webui/camera_app_ui/resources/js/main.ts
+++ b/ash/webui/camera_app_ui/resources/js/main.ts
@@ -18,6 +18,7 @@
 import * as loadTimeData from './models/load_time_data.js';
 import * as localStorage from './models/local_storage.js';
 import {ChromeHelper} from './mojo/chrome_helper.js';
+import {DeviceOperator} from './mojo/device_operator.js';
 import * as nav from './nav.js';
 import {PerfLogger} from './perf.js';
 import {preloadImagesList} from './preload_images.js';
@@ -215,6 +216,7 @@
    * Starts the app by loading the model and opening the camera-view.
    */
   async start(launchType: metrics.LaunchType): Promise<void> {
+    await DeviceOperator.initializeInstance();
     document.documentElement.dir = loadTimeData.getTextDirection();
     try {
       await filesystem.initialize();
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/device_operator.ts b/ash/webui/camera_app_ui/resources/js/mojo/device_operator.ts
index 1f598551..4d6cd589 100644
--- a/ash/webui/camera_app_ui/resources/js/mojo/device_operator.ts
+++ b/ash/webui/camera_app_ui/resources/js/mojo/device_operator.ts
@@ -111,10 +111,12 @@
 }
 
 /**
- * The singleton instance of DeviceOperator. Initialized by the first
- * invocation of getInstance().
+ * The singleton instance of DeviceOperator. Initialized by calling
+ * initializeInstance().
+ *
+ * Note that undefined means not initialized, and null means not supported.
  */
-let instance: DeviceOperator|null = null;
+let instance: DeviceOperator|null|undefined = undefined;
 
 /**
  * Job queue to sequentialize devices operations.
@@ -132,12 +134,6 @@
       wrapEndpoint(CameraAppDeviceProvider.getRemote());
 
   /**
-   * Flag that indicates if the direct communication between camera app and
-   * video capture devices is supported.
-   */
-  private readonly isSupported: Promise<boolean>;
-
-  /**
    * Map which maps from device id to the remote of devices. We want to have
    * only one remote for each devices to avoid unnecessary wastes of resources
    * and also makes it easier to control the connection.
@@ -149,11 +145,13 @@
    */
   private readonly cameraInfos = new Map<string, Promise<CameraInfo>>();
 
-  constructor() {
-    this.isSupported = (async () => {
-      const {isSupported} = await this.deviceProvider.isSupported();
-      return isSupported;
-    })();
+  /**
+   * Return if the direct communication between camera app and video capture
+   * devices is supported.
+   */
+  async isSupported(): Promise<boolean> {
+    const {isSupported} = await this.deviceProvider.isSupported();
+    return isSupported;
   }
 
   /**
@@ -713,17 +711,17 @@
   }
 
   /**
-   * Creates a new instance of DeviceOperator if it is not set. Returns the
-   *     exist instance.
+   * Initialize the singleton instance.
    *
-   * @return The singleton instance.
+   * This should be called before all invocation of static getInstance() and
+   * static isSupported().
    */
-  static async getInstance(): Promise<DeviceOperator|null> {
-    if (instance === null) {
-      instance = new DeviceOperator();
-    }
-    if (!await instance.isSupported) {
-      return null;
+  static async initializeInstance(): Promise<void> {
+    assert(instance === undefined);
+    const rawInstance = new DeviceOperator();
+    if (!await rawInstance.isSupported()) {
+      instance = null;
+      return;
     }
 
     // Using a wrapper to ensure all the device operations are sequentialized.
@@ -737,7 +735,17 @@
         return val;
       },
     };
-    return new Proxy(instance, deviceOperatorWrapper);
+    instance = new Proxy(rawInstance, deviceOperatorWrapper);
+  }
+
+  /**
+   * Returns the existing singleton instance of DeviceOperator.
+   *
+   * @return The singleton instance.
+   */
+  static getInstance(): DeviceOperator|null {
+    assert(instance !== undefined);
+    return instance;
   }
 
   /**
@@ -745,7 +753,7 @@
    *
    * @return True if the DeviceOperator is supported.
    */
-  static async isSupported(): Promise<boolean> {
-    return await this.getInstance() !== null;
+  static isSupported(): boolean {
+    return this.getInstance() !== null;
   }
 }
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/image_capture.ts b/ash/webui/camera_app_ui/resources/js/mojo/image_capture.ts
index 018bc90c..750b54e5 100644
--- a/ash/webui/camera_app_ui/resources/js/mojo/image_capture.ts
+++ b/ash/webui/camera_app_ui/resources/js/mojo/image_capture.ts
@@ -79,7 +79,7 @@
    */
   async takePhoto(photoSettings: PhotoSettings, photoEffects: Effect[] = []):
       Promise<TakePhotoResult[]> {
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     if (deviceOperator === null) {
       if (photoEffects.length > 0) {
         throw new Error('Applying effects is not supported on this device');
@@ -148,7 +148,7 @@
    * @return Promise for the operation.
    */
   async addMetadataObserver(): Promise<void> {
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     if (deviceOperator === null) {
       return;
     }
@@ -185,19 +185,12 @@
 
   /**
    * Removes the observer that saves metadata.
-   *
-   * @return Promise for the operation.
    */
-  async removeMetadataObserver(): Promise<void> {
+  removeMetadataObserver(): void {
     if (this.metadataObserver === null) {
       return;
     }
 
-    const deviceOperator = await DeviceOperator.getInstance();
-    if (deviceOperator === null) {
-      return;
-    }
-
     closeEndpoint(this.metadataObserver);
     this.metadataObserver = null;
   }
diff --git a/ash/webui/camera_app_ui/resources/js/nav.ts b/ash/webui/camera_app_ui/resources/js/nav.ts
index aa29806..cf8539f1 100644
--- a/ash/webui/camera_app_ui/resources/js/nav.ts
+++ b/ash/webui/camera_app_ui/resources/js/nav.ts
@@ -133,11 +133,14 @@
  * @return Promise for the operation or result.
  */
 export function open(
-    name: ViewName, options?: EnterOptions): Promise<LeaveCondition> {
+    name: ViewName, options?: EnterOptions): {closed: Promise<LeaveCondition>} {
   const index = findIndex(name);
-  return show(index).enter(options).finally(() => {
-    hide(index);
-  });
+  const view = show(index);
+  return {
+    closed: view.enter(options).finally(() => {
+      hide(index);
+    }),
+  };
 }
 
 /**
diff --git a/ash/webui/camera_app_ui/resources/js/new_feature_toast.ts b/ash/webui/camera_app_ui/resources/js/new_feature_toast.ts
index dbef9f3..6a59372 100644
--- a/ash/webui/camera_app_ui/resources/js/new_feature_toast.ts
+++ b/ash/webui/camera_app_ui/resources/js/new_feature_toast.ts
@@ -199,18 +199,15 @@
 /**
  * Shows the toast on the given element.
  */
-export async function show(el: HTMLElement): Promise<void> {
+export function show(el: HTMLElement): void {
   if (showing !== null) {
     hide();
   }
 
-  await new Promise((r) => {
-    const ripple = new RippleEffect(el);
-    const toast = new Toast(el);
-    const timeout = setTimeout(r, SHOWING_TIMEOUT_MS);
-    showing = {ripple, toast, timeout};
-  });
-  hide();
+  const ripple = new RippleEffect(el);
+  const toast = new Toast(el);
+  const timeout = setTimeout(hide, SHOWING_TIMEOUT_MS);
+  showing = {ripple, toast, timeout};
 }
 
 /**
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera.ts b/ash/webui/camera_app_ui/resources/js/views/camera.ts
index 997c0ad7..04e221b 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera.ts
@@ -443,7 +443,7 @@
         // Record and keep the rotation only at the instance the user starts the
         // capture. Users may change the device orientation while taking video.
         const cameraFrameRotation = await (async () => {
-          const deviceOperator = await DeviceOperator.getInstance();
+          const deviceOperator = DeviceOperator.getInstance();
           if (deviceOperator === null) {
             return 0;
           }
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
index e141703..c2400f0 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
@@ -225,7 +225,7 @@
     if (this.observer !== null) {
       return;
     }
-    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceOperator = DeviceOperator.getInstance();
     if (deviceOperator === null) {
       // Skip showing indicator on fake camera.
       return;
diff --git a/ash/webui/camera_app_ui/resources/js/views/review.ts b/ash/webui/camera_app_ui/resources/js/views/review.ts
index 78825a5..4907388 100644
--- a/ash/webui/camera_app_ui/resources/js/views/review.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/review.ts
@@ -194,7 +194,7 @@
       }
       setupI18nElements(btnGroup.el);
     }
-    nav.open(this.viewName).then(() => {
+    nav.open(this.viewName).closed.then(() => {
       onSelected.signal(null);
     });
     const result = await onSelected.wait();
diff --git a/ash/webui/camera_app_ui/resources/js/views/settings/primary.ts b/ash/webui/camera_app_ui/resources/js/views/settings/primary.ts
index bedc93a..79d7fe4 100644
--- a/ash/webui/camera_app_ui/resources/js/views/settings/primary.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/settings/primary.ts
@@ -218,7 +218,7 @@
   private async openSubSettings(name: ViewName): Promise<void> {
     // Dismiss primary-settings if sub-settings was dismissed by background
     // click.
-    const cond = await nav.open(name);
+    const cond = await nav.open(name).closed;
     if (cond.kind === 'BACKGROUND_CLICKED') {
       this.leave(cond);
     }
diff --git a/ash/webui/camera_app_ui/resources/utils/cca.py b/ash/webui/camera_app_ui/resources/utils/cca.py
index bffce35..bac2ff71 100755
--- a/ash/webui/camera_app_ui/resources/utils/cca.py
+++ b/ash/webui/camera_app_ui/resources/utils/cca.py
@@ -15,6 +15,7 @@
 import subprocess
 import sys
 import tempfile
+import time
 import xml.sax
 
 
@@ -34,6 +35,11 @@
     subprocess.check_call(args, cwd=cwd)
 
 
+def check_output(args, cwd=None):
+    logging.debug(f'$ {shell_join(args)}')
+    return subprocess.check_output(args, cwd=cwd, text=True)
+
+
 def run_node(args):
     root = get_chromium_root()
     node = os.path.join(root, 'third_party/node/linux/node-linux-x64/bin/node')
@@ -67,6 +73,41 @@
     return os.stat(util_js).st_ino == os.stat(util_js_in_gen).st_ino
 
 
+CCA_OVERRIDE_PATH = '/etc/camera/cca'
+CCA_OVERRIDE_FEATURE = 'CCALocalOverride'
+CHROME_DEV_CONF_PATH = '/etc/chrome_dev.conf'
+
+
+def local_override_enabled(device):
+    chrome_dev_conf = check_output(
+        ['ssh', device, '--', 'cat', CHROME_DEV_CONF_PATH])
+    # This is a simple heuristic that is not 100% accurate, since this only
+    # matches the feature name which can be in other irrevelant position in the
+    # file. This should be fine though since this is only used for developers
+    # and it's not expected to have the exact string match outside of
+    # --enable-features added by this script.
+    return CCA_OVERRIDE_FEATURE in chrome_dev_conf
+
+
+def ensure_local_override_enabled(device, force):
+    if local_override_enabled(device):
+        return
+    run([
+        'ssh', device, '--',
+        f'echo "--enable-features={CCA_OVERRIDE_FEATURE}"' +
+        f' >> {CHROME_DEV_CONF_PATH}'
+    ])
+    if not force:
+        prompt = input('Need to restart UI for deploy to take effect, ' +
+                       'do it now? (y/N): ').lower()
+        if prompt != 'y':
+            print(
+                'Not restarting UI. ' +
+                '`restart ui` on DUT manually for the change to take effect.')
+            return
+    run(['ssh', device, '--', 'restart', 'ui'])
+
+
 def deploy(args):
     root_dir = get_chromium_root()
     cca_root = os.getcwd()
@@ -74,6 +115,7 @@
 
     src_relative_dir = os.path.relpath(cca_root, root_dir)
     gen_dir = os.path.join(target_dir, 'gen', src_relative_dir)
+    tsc_dir = os.path.join(gen_dir, 'js/tsc/js')
 
     # Since CCA copy source to gen directory and place it together with other
     # generated files for TypeScript compilation, and GN use hard links when
@@ -85,7 +127,7 @@
     # TODO(pihsun): Support this case if there's some common scenario that
     # would cause this.
     assert gen_files_are_hard_links(gen_dir), (
-        'The generated files are not hard linked.')
+        'The generated files are not hard linked, compile Chrome first?')
 
     build_preload_images_js(os.path.join(gen_dir, 'js'))
 
@@ -103,67 +145,42 @@
         'false',
     ])
 
-    build_pak_cmd = [
-        'tools/grit/grit.py',
-        '-i',
-        os.path.join(gen_dir, '../ash_camera_app_resources.grd'),
-        'build',
-        '-o',
-        os.path.join(target_dir, 'gen/ash'),
-        '-f',
-        os.path.join(target_dir,
-                     'gen/tools/gritsettings/default_resource_ids'),
-        '-D',
-        f'SHARED_INTERMEDIATE_DIR={os.path.join(target_dir, "gen")}',
-        '-E',
-        f'root_src_dir={get_chromium_root()}',
-        '-E',
-        f'root_gen_dir={os.path.join(target_dir, "gen")}',
-    ]
-    # Since there is a constraint in grit.py which will replace ${root_gen_dir}
-    # in .grd file only if the script is executed in the parent directory of
-    # ${root_gen_dir}, execute the script in Chromium root as a workaround.
-    run(build_pak_cmd, get_chromium_root())
-
-    with tempfile.TemporaryDirectory() as tmp_dir:
-        pak_util_script = os.path.join(get_chromium_root(),
-                                       'tools/grit/pak_util.py')
-        extract_resources_pak_cmd = [
-            pak_util_script,
-            'extract',
-            '--raw',
-            os.path.join(target_dir, 'resources.pak'),
-            '-o',
-            tmp_dir,
-        ]
-        run(extract_resources_pak_cmd)
-
-        extract_camera_pak_cmd = [
-            pak_util_script,
-            'extract',
-            '--raw',
-            os.path.join(target_dir, 'gen/ash/ash_camera_app_resources.pak'),
-            '-o',
-            tmp_dir,
-        ]
-        run(extract_camera_pak_cmd)
-
-        create_new_resources_pak_cmd = [
-            pak_util_script,
-            'create',
-            '-i',
-            tmp_dir,
-            os.path.join(target_dir, 'resources.pak'),
-        ]
-        run(create_new_resources_pak_cmd)
-
-    deploy_new_resources_pak_cmd = [
+    deploy_new_tsc_files = [
         'rsync',
+        '--recursive',
         '--inplace',
-        os.path.join(target_dir, 'resources.pak'),
-        f'{args.device}:/opt/google/chrome/',
+        '--delete',
+        '--mkpath',
+        f'{tsc_dir}/',
+        f'{args.device}:{CCA_OVERRIDE_PATH}/js/',
     ]
-    run(deploy_new_resources_pak_cmd)
+    run(deploy_new_tsc_files)
+
+    for dir in ['css', 'images', 'views', 'sounds']:
+        deploy_new_assets = [
+            'rsync',
+            '--recursive',
+            '--inplace',
+            '--delete',
+            '--mkpath',
+            f'{os.path.join(cca_root, dir)}/',
+            f'{args.device}:{CCA_OVERRIDE_PATH}/{dir}/',
+        ]
+        run(deploy_new_assets)
+
+    current_time = time.strftime('%F %T%z')
+    run([
+        'ssh',
+        args.device,
+        '--',
+        'printf',
+        '%s',
+        shlex.quote(f'export const VERSION = "cca.py deploy {current_time}";'),
+        '>',
+        f'{CCA_OVERRIDE_PATH}/js/deployed_version.js',
+    ])
+
+    ensure_local_override_enabled(args.device, args.force)
 
 
 def test(args):
@@ -324,6 +341,9 @@
                                           )
     deploy_parser.add_argument('board')
     deploy_parser.add_argument('device')
+    deploy_parser.add_argument('--force',
+                               help="Don't prompt for restarting Chrome.",
+                               action='store_true')
     deploy_parser.set_defaults(func=deploy)
 
     test_parser = subparsers.add_parser('test',
diff --git a/ash/webui/media_app_ui/resources/index_dark_light.html b/ash/webui/media_app_ui/resources/index_dark_light.html
index 877e8f4..36e7aec 100644
--- a/ash/webui/media_app_ui/resources/index_dark_light.html
+++ b/ash/webui/media_app_ui/resources/index_dark_light.html
@@ -28,13 +28,14 @@
   }
 
   /*
-   * This is the <iframe> style set for sandboxed guests that use
-   * guest_view_iframe_container.js.
-   * TODO(crbug/996088): Remove the iframe styles if switched to <webview>.
+   * This is the <iframe> style set for sandboxed chrome-untrusted:// guests.
+   * `user-select: none` is applied here to avoid the iframe obtaining a grey
+   * overlay if it is clicked while loading (b/227678322).
    */
   iframe {
     border: 0;
     height: 100%;
+    user-select: none;
     width: 100%;
   }
 </style>
diff --git a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
index 35b6e4d4..95739c02 100644
--- a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
@@ -386,7 +386,8 @@
   assertFilenamesToBe('1.png,3.png');
 };
 
-MediaAppUIBrowserTest.NotifyCurrentFile = async () => {
+// Test that each file type has an icon in light mode.
+MediaAppUIBrowserTest.NotifyCurrentFileLight = async () => {
   const imageFile = new File([], 'image.png', {type: 'image/png'});
   const audioFile = new File([], 'audio.wav', {type: 'audio/wav'});
   const videoFile = new File([], 'video.mp4', {type: 'video/mp4'});
diff --git a/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
index 36857683..329f4fff 100644
--- a/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
@@ -208,9 +208,11 @@
   runMediaAppTest('MultipleSelectionLaunch');
 });
 
-TEST_F('MediaAppUIGtestBrowserTest', 'NotifyCurrentFile', () => {
-  runMediaAppTest('NotifyCurrentFile');
-});
+TEST_F(
+    'MediaAppUIWithDarkLightModeGtestBrowserTest', 'NotifyCurrentFileLight',
+    () => {
+      runMediaAppTest('NotifyCurrentFileLight');
+    });
 
 TEST_F(
     'MediaAppUIWithDarkLightModeDarkGtestBrowserTest', 'NotifyCurrentFileDark',
diff --git a/base/allocator/partition_allocator/BUILD.gn b/base/allocator/partition_allocator/BUILD.gn
index 4e817e4..a3097d5 100644
--- a/base/allocator/partition_allocator/BUILD.gn
+++ b/base/allocator/partition_allocator/BUILD.gn
@@ -87,8 +87,11 @@
     "partition_alloc_base/immediate_crash.h",
     "partition_alloc_base/logging.cc",
     "partition_alloc_base/logging.h",
+    "partition_alloc_base/mac/scoped_cftyperef.h",
+    "partition_alloc_base/mac/scoped_typeref.h",
     "partition_alloc_base/memory/ref_counted.cc",
     "partition_alloc_base/memory/ref_counted.h",
+    "partition_alloc_base/memory/scoped_policy.h",
     "partition_alloc_base/memory/scoped_refptr.h",
     "partition_alloc_base/migration_adapter.h",
     "partition_alloc_base/no_destructor.h",
diff --git a/base/allocator/partition_allocator/DEPS b/base/allocator/partition_allocator/DEPS
index b7471a04..dae6178d 100644
--- a/base/allocator/partition_allocator/DEPS
+++ b/base/allocator/partition_allocator/DEPS
@@ -9,7 +9,6 @@
     "+base/logging_buildflags.h",
     "+base/mac/foundation_util.h",
     "+base/mac/mac_util.h",
-    "+base/mac/scoped_cftyperef.h",
     "+base/debug/debugging_buildflags.h",
     "+build/build_config.h",
     "+build/buildflag.h",
diff --git a/base/allocator/partition_allocator/page_allocator_internals_posix.h b/base/allocator/partition_allocator/page_allocator_internals_posix.h
index b7dc8bf..07bbe88 100644
--- a/base/allocator/partition_allocator/page_allocator_internals_posix.h
+++ b/base/allocator/partition_allocator/page_allocator_internals_posix.h
@@ -20,9 +20,9 @@
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_APPLE)
+#include "base/allocator/partition_allocator/partition_alloc_base/mac/scoped_cftyperef.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
-#include "base/mac/scoped_cftyperef.h"
 
 #include <Availability.h>
 #include <Security/Security.h>
diff --git a/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_cftyperef.h b/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_cftyperef.h
new file mode 100644
index 0000000..5b56f7fb
--- /dev/null
+++ b/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_cftyperef.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_CFTYPEREF_H_
+#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_CFTYPEREF_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "base/allocator/partition_allocator/partition_alloc_base/mac/scoped_typeref.h"
+
+namespace partition_alloc::internal::base {
+
+// ScopedCFTypeRef<> is patterned after std::unique_ptr<>, but maintains
+// ownership of a CoreFoundation object: any object that can be represented
+// as a CFTypeRef.  Style deviations here are solely for compatibility with
+// std::unique_ptr<>'s interface, with which everyone is already familiar.
+//
+// By default, ScopedCFTypeRef<> takes ownership of an object (in the
+// constructor or in reset()) by taking over the caller's existing ownership
+// claim.  The caller must own the object it gives to ScopedCFTypeRef<>, and
+// relinquishes an ownership claim to that object.  ScopedCFTypeRef<> does not
+// call CFRetain(). This behavior is parameterized by the |OwnershipPolicy|
+// enum. If the value |RETAIN| is passed (in the constructor or in reset()),
+// then ScopedCFTypeRef<> will call CFRetain() on the object, and the initial
+// ownership is not changed.
+
+namespace internal {
+
+template <typename CFT>
+struct ScopedCFTypeRefTraits {
+  static CFT InvalidValue() { return nullptr; }
+  static CFT Retain(CFT object) {
+    CFRetain(object);
+    return object;
+  }
+  static void Release(CFT object) { CFRelease(object); }
+};
+
+}  // namespace internal
+
+template <typename CFT>
+using ScopedCFTypeRef =
+    ScopedTypeRef<CFT, internal::ScopedCFTypeRefTraits<CFT>>;
+
+}  // namespace partition_alloc::internal::base
+
+#endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_CFTYPEREF_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_typeref.h b/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_typeref.h
new file mode 100644
index 0000000..c0488e93
--- /dev/null
+++ b/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_typeref.h
@@ -0,0 +1,145 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_TYPEREF_H_
+#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_TYPEREF_H_
+
+#include "base/allocator/partition_allocator/partition_alloc_base/memory/scoped_policy.h"
+#include "base/allocator/partition_allocator/partition_alloc_check.h"
+
+namespace partition_alloc::internal::base {
+
+// ScopedTypeRef<> is patterned after std::unique_ptr<>, but maintains ownership
+// of a reference to any type that is maintained by Retain and Release methods.
+//
+// The Traits structure must provide the Retain and Release methods for type T.
+// A default ScopedTypeRefTraits is used but not defined, and should be defined
+// for each type to use this interface. For example, an appropriate definition
+// of ScopedTypeRefTraits for CGLContextObj would be:
+//
+//   template<>
+//   struct ScopedTypeRefTraits<CGLContextObj> {
+//     static CGLContextObj InvalidValue() { return nullptr; }
+//     static CGLContextObj Retain(CGLContextObj object) {
+//       CGLContextRetain(object);
+//       return object;
+//     }
+//     static void Release(CGLContextObj object) { CGLContextRelease(object); }
+//   };
+//
+// For the many types that have pass-by-pointer create functions, the function
+// InitializeInto() is provided to allow direct initialization and assumption
+// of ownership of the object. For example, continuing to use the above
+// CGLContextObj specialization:
+//
+//   base::ScopedTypeRef<CGLContextObj> context;
+//   CGLCreateContext(pixel_format, share_group, context.InitializeInto());
+//
+// For initialization with an existing object, the caller may specify whether
+// the ScopedTypeRef<> being initialized is assuming the caller's existing
+// ownership of the object (and should not call Retain in initialization) or if
+// it should not assume this ownership and must create its own (by calling
+// Retain in initialization). This behavior is based on the |policy| parameter,
+// with |ASSUME| for the former and |RETAIN| for the latter. The default policy
+// is to |ASSUME|.
+
+template <typename T>
+struct ScopedTypeRefTraits;
+
+template <typename T, typename Traits = ScopedTypeRefTraits<T>>
+class ScopedTypeRef {
+ public:
+  using element_type = T;
+
+  explicit constexpr ScopedTypeRef(
+      element_type object = Traits::InvalidValue(),
+      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
+      : object_(object) {
+    if (object_ && policy == base::scoped_policy::RETAIN)
+      object_ = Traits::Retain(object_);
+  }
+
+  ScopedTypeRef(const ScopedTypeRef<T, Traits>& that) : object_(that.object_) {
+    if (object_)
+      object_ = Traits::Retain(object_);
+  }
+
+  // This allows passing an object to a function that takes its superclass.
+  template <typename R, typename RTraits>
+  explicit ScopedTypeRef(const ScopedTypeRef<R, RTraits>& that_as_subclass)
+      : object_(that_as_subclass.get()) {
+    if (object_)
+      object_ = Traits::Retain(object_);
+  }
+
+  ScopedTypeRef(ScopedTypeRef<T, Traits>&& that) : object_(that.object_) {
+    that.object_ = Traits::InvalidValue();
+  }
+
+  ~ScopedTypeRef() {
+    if (object_)
+      Traits::Release(object_);
+  }
+
+  ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) {
+    reset(that.get(), base::scoped_policy::RETAIN);
+    return *this;
+  }
+
+  // This is to be used only to take ownership of objects that are created
+  // by pass-by-pointer create functions. To enforce this, require that the
+  // object be reset to NULL before this may be used.
+  [[nodiscard]] element_type* InitializeInto() {
+    PA_DCHECK(!object_);
+    return &object_;
+  }
+
+  void reset(const ScopedTypeRef<T, Traits>& that) {
+    reset(that.get(), base::scoped_policy::RETAIN);
+  }
+
+  void reset(element_type object = Traits::InvalidValue(),
+             base::scoped_policy::OwnershipPolicy policy =
+                 base::scoped_policy::ASSUME) {
+    if (object && policy == base::scoped_policy::RETAIN)
+      object = Traits::Retain(object);
+    if (object_)
+      Traits::Release(object_);
+    object_ = object;
+  }
+
+  bool operator==(const ScopedTypeRef& that) const {
+    return object_ == that.object_;
+  }
+
+  bool operator!=(const ScopedTypeRef& that) const {
+    return object_ != that.object_;
+  }
+
+  operator element_type() const { return object_; }
+
+  element_type get() const { return object_; }
+
+  void swap(ScopedTypeRef& that) {
+    element_type temp = that.object_;
+    that.object_ = object_;
+    object_ = temp;
+  }
+
+  // ScopedTypeRef<>::release() is like std::unique_ptr<>::release.  It is NOT
+  // a wrapper for Release().  To force a ScopedTypeRef<> object to call
+  // Release(), use ScopedTypeRef<>::reset().
+  [[nodiscard]] element_type release() {
+    element_type temp = object_;
+    object_ = Traits::InvalidValue();
+    return temp;
+  }
+
+ private:
+  element_type object_;
+};
+
+}  // namespace partition_alloc::internal::base
+
+#endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_TYPEREF_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/memory/scoped_policy.h b/base/allocator/partition_allocator/partition_alloc_base/memory/scoped_policy.h
new file mode 100644
index 0000000..53c5ad96
--- /dev/null
+++ b/base/allocator/partition_allocator/partition_alloc_base/memory/scoped_policy.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_SCOPED_POLICY_H_
+#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_SCOPED_POLICY_H_
+
+namespace partition_alloc::internal::base::scoped_policy {
+
+// Defines the ownership policy for a scoped object.
+enum OwnershipPolicy {
+  // The scoped object takes ownership of an object by taking over an existing
+  // ownership claim.
+  ASSUME,
+
+  // The scoped object will retain the object and any initial ownership is
+  // not changed.
+  RETAIN
+};
+
+}  // namespace partition_alloc::internal::base::scoped_policy
+
+#endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_SCOPED_POLICY_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/migration_adapter.h b/base/allocator/partition_allocator/partition_alloc_base/migration_adapter.h
index 0b60deff..971a92e5 100644
--- a/base/allocator/partition_allocator/partition_alloc_base/migration_adapter.h
+++ b/base/allocator/partition_allocator/partition_alloc_base/migration_adapter.h
@@ -33,16 +33,6 @@
 
 #if BUILDFLAG(IS_MAC)
 
-namespace internal {
-
-template <typename CFT>
-struct ScopedCFTypeRefTraits;
-
-}  // namespace internal
-
-template <typename T, typename Traits>
-class ScopedTypeRef;
-
 namespace mac {
 
 template <typename T>
@@ -67,12 +57,6 @@
 using ::base::LazyInstanceTraitsBase;
 
 #if BUILDFLAG(IS_MAC)
-template <typename CFT>
-using ScopedCFTypeRef =
-    ::base::ScopedTypeRef<CFT, ::base::internal::ScopedCFTypeRefTraits<CFT>>;
-#endif
-
-#if BUILDFLAG(IS_MAC)
 namespace mac {
 
 using ::base::mac::CFCast;
diff --git a/buildtools/reclient_cfgs/rewrapper_chroot_compile.cfg b/buildtools/reclient_cfgs/rewrapper_chroot_compile.cfg
index ca99b0a..fe4de50 100644
--- a/buildtools/reclient_cfgs/rewrapper_chroot_compile.cfg
+++ b/buildtools/reclient_cfgs/rewrapper_chroot_compile.cfg
@@ -1,5 +1,5 @@
 service=remotebuildexecution.googleapis.com:443
-instance=projects/goma-foundry-experiments/instances/default_instance
+instance=projects/rbe-chromium-trusted-test/instances/default_instance
 use_application_default_credentials=true
 platform=container-image=docker://gcr.io/cloud-marketplace/google/rbe-ubuntu16-04@sha256:f6568d8168b14aafd1b707019927a63c2d37113a03bcee188218f99bd0327ea1,dockerChrootPath=.,dockerRuntime=runsc
 server_address=unix:///tmp/reproxy.sock
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 05c6016..438dabcd 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -1225,9 +1225,6 @@
 
 bool PictureLayerImpl::CanRecreateHighResTilingForLCDTextAndRasterTransform(
     const PictureLayerTiling& high_res) const {
-  // This is for the sync tree only to avoid flickering.
-  if (!layer_tree_impl()->IsSyncTree())
-    return false;
   // We can recreate the tiling if we would invalidate all of its tiles.
   if (high_res.may_contain_low_resolution_tiles())
     return true;
@@ -1247,7 +1244,7 @@
   // If ReadyToActivate() is already scheduled, recreating tiling should be
   // delayed until the activation is executed. Otherwise the tiles in viewport
   // will be deleted.
-  if (layer_tree_impl()->IsReadyToActivate())
+  if (layer_tree_impl()->IsSyncTree() && layer_tree_impl()->IsReadyToActivate())
     return false;
   return true;
 }
@@ -1267,12 +1264,28 @@
         high_res->raster_transform().translation() != raster_translation;
     bool can_use_lcd_text_changed =
         high_res->can_use_lcd_text() != can_use_lcd_text();
+    bool can_recreate_highres_tiling =
+        CanRecreateHighResTilingForLCDTextAndRasterTransform(*high_res);
+    // Only for the sync tree to avoid flickering.
     bool should_recreate_high_res =
         (raster_transform_is_not_ideal || can_use_lcd_text_changed) &&
-        CanRecreateHighResTilingForLCDTextAndRasterTransform(*high_res);
+        layer_tree_impl()->IsSyncTree() && can_recreate_highres_tiling;
+    // Only request an invalidation if we don't already have a pending tree.
+    bool can_request_invalidation_for_high_res =
+        (raster_transform_is_not_ideal || can_use_lcd_text_changed) &&
+        !layer_tree_impl()->settings().commit_to_active_tree &&
+        layer_tree_impl()->IsActiveTree() && can_recreate_highres_tiling &&
+        !layer_tree_impl()->HasPendingTree();
+
     if (should_recreate_high_res) {
       tilings_->Remove(high_res);
       high_res = nullptr;
+    } else if (can_request_invalidation_for_high_res) {
+      // Anytime a condition which flips whether we can recreate the tiling
+      // changes, we'll get a call to UpdateDrawProperties. We check whether we
+      // could recreate the tiling when this runs on the active tree to trigger
+      // an impl-side invalidation (if needed).
+      layer_tree_impl()->RequestImplSideInvalidationForRerasterTiling();
     } else if (!has_adjusted_raster_scale) {
       // Nothing changed, no need to update tilings.
       DCHECK_EQ(HIGH_RESOLUTION, high_res->resolution());
diff --git a/cc/metrics/event_latency_tracing_recorder.cc b/cc/metrics/event_latency_tracing_recorder.cc
index 96586cf..3c7cbc7 100644
--- a/cc/metrics/event_latency_tracing_recorder.cc
+++ b/cc/metrics/event_latency_tracing_recorder.cc
@@ -227,34 +227,31 @@
   }
   if (stage_history) {
     DCHECK(viz_breakdown);
-    // Find the first compositor stage that happens after the final dispatch
-    // stage.
+    // Find the first compositor stage that starts at the same time or after the
+    // end of the final event dispatch stage. At least,
+    // SubmitCompositorFrameToPresentationCompositorFrame stage should match
+    // this criteria.
     auto stage_it = std::find_if(
         stage_history->begin(), stage_history->end(),
         [dispatch_timestamp](const CompositorFrameReporter::StageData& stage) {
-          return stage.start_time > dispatch_timestamp;
+          return stage.start_time >= dispatch_timestamp;
         });
-    // TODO(crbug.com/1079116): Ideally, at least the start time of
-    // SubmitCompositorFrameToPresentationCompositorFrame stage should be
-    // greater than the final event dispatch timestamp, but apparently, this is
-    // not always the case (see crbug.com/1093698). For now, skip to the next
-    // event in such cases. Hopefully, the work to reduce discrepancies between
-    // the new EventLatency and the old Event.Latency metrics would fix this
-    // issue. If not, we need to reconsider investigating this issue.
-    if (stage_it == stage_history->end())
-      return;
+    DCHECK(stage_it != stage_history->end());
 
     DCHECK(dispatch_stage ==
                EventMetrics::DispatchStage::kRendererCompositorFinished ||
            dispatch_stage ==
                EventMetrics::DispatchStage::kRendererMainFinished);
 
-    const char* d2c_breakdown_name = GetDispatchToCompositorBreakdownName(
-        dispatch_stage, stage_it->stage_type);
-    TRACE_EVENT_BEGIN(kTracingCategory,
-                      perfetto::StaticString{d2c_breakdown_name}, trace_track,
-                      dispatch_timestamp);
-    TRACE_EVENT_END(kTracingCategory, trace_track, stage_it->start_time);
+    // Record dispatch-to-compositor stage only if it has non-zero duration.
+    if (dispatch_timestamp < stage_it->start_time) {
+      const char* d2c_breakdown_name = GetDispatchToCompositorBreakdownName(
+          dispatch_stage, stage_it->stage_type);
+      TRACE_EVENT_BEGIN(kTracingCategory,
+                        perfetto::StaticString{d2c_breakdown_name}, trace_track,
+                        dispatch_timestamp);
+      TRACE_EVENT_END(kTracingCategory, trace_track, stage_it->start_time);
+    }
 
     // Compositor stages.
     for (; stage_it != stage_history->end(); ++stage_it) {
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 13b2649..889af5d 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -466,6 +466,44 @@
   return uploaded_image;
 }
 
+// We use this below, instead of just a std::unique_ptr, so that we can run
+// a Finch experiment to check the impact of not using discardable memory on the
+// GPU decode path.
+class HeapDiscardableMemory : public base::DiscardableMemory {
+ public:
+  explicit HeapDiscardableMemory(size_t size)
+      : memory_(new char[size]), size_(size) {}
+  ~HeapDiscardableMemory() override = default;
+  [[nodiscard]] bool Lock() override {
+    // Locking only succeeds when we have not yet discarded the memory (i.e. if
+    // we have never called |Unlock()|.)
+    return memory_ != nullptr;
+  }
+  void Unlock() override { Discard(); }
+  void* data() const override {
+    DCHECK(memory_);
+    return static_cast<void*>(memory_.get());
+  }
+  void DiscardForTesting() override { Discard(); }
+  base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
+      const char* name,
+      base::trace_event::ProcessMemoryDump* pmd) const override {
+    auto* dump = pmd->CreateAllocatorDump(name);
+    dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                    base::trace_event::MemoryAllocatorDump::kUnitsBytes, size_);
+    return dump;
+  }
+
+ private:
+  void Discard() {
+    memory_.reset();
+    size_ = 0;
+  }
+
+  std::unique_ptr<char[]> memory_;
+  size_t size_;
+};
+
 }  // namespace
 
 // Extract the information to uniquely identify a DrawImage for the purposes of
@@ -1986,10 +2024,17 @@
   sk_sp<SkImage> image_v;
   {
     base::AutoUnlock unlock(lock_);
-    auto* allocator = base::DiscardableMemoryAllocator::GetInstance();
-    backing_memory = allocator->AllocateLockedDiscardableMemoryWithRetryOrDie(
-        image_data->size, base::BindOnce(&GpuImageDecodeCache::ClearCache,
-                                         base::Unretained(this)));
+    if (base::FeatureList::IsEnabled(
+            features::kNoDiscardableMemoryForGpuDecodePath)) {
+      backing_memory =
+          std::make_unique<HeapDiscardableMemory>(image_data->size);
+    } else {
+      auto* allocator = base::DiscardableMemoryAllocator::GetInstance();
+      backing_memory = allocator->AllocateLockedDiscardableMemoryWithRetryOrDie(
+          image_data->size, base::BindOnce(&GpuImageDecodeCache::ClearCache,
+                                           base::Unretained(this)));
+    }
+
     sk_sp<SkColorSpace> color_space =
         ColorSpaceForImageDecode(draw_image, image_data->mode);
     auto release_proc = [](const void*, void*) {};
diff --git a/cc/tiles/gpu_image_decode_cache_unittest.cc b/cc/tiles/gpu_image_decode_cache_unittest.cc
index ea16477..cef073a6 100644
--- a/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -381,7 +381,8 @@
                      bool /* allow_accelerated_jpeg_decoding */,
                      bool /* allow_accelerated_webp_decoding */,
                      bool /* advertise_accelerated_decoding */,
-                     bool /* enable_clipped_image_scaling */>> {
+                     bool /* enable_clipped_image_scaling */,
+                     bool /* no_discardable_memory */>> {
  public:
   void SetUp() override {
     std::vector<base::Feature> enabled_features;
@@ -391,6 +392,10 @@
     allow_accelerated_webp_decoding_ = std::get<4>(GetParam());
     if (allow_accelerated_webp_decoding_)
       enabled_features.push_back(features::kVaapiWebPImageDecodeAcceleration);
+    no_discardable_memory_ = std::get<7>(GetParam());
+    if (no_discardable_memory_)
+      enabled_features.push_back(
+          features::kNoDiscardableMemoryForGpuDecodePath);
     feature_list_.InitWithFeatures(enabled_features,
                                    {} /* disabled_features */);
     advertise_accelerated_decoding_ = std::get<5>(GetParam());
@@ -692,6 +697,7 @@
   bool allow_accelerated_webp_decoding_;
   bool advertise_accelerated_decoding_;
   bool enable_clipped_image_scaling_;
+  bool no_discardable_memory_;
   int max_texture_size_ = 0;
 };
 
@@ -3629,7 +3635,8 @@
         testing::Values(false) /* allow_accelerated_jpeg_decoding */,
         testing::Values(false) /* allow_accelerated_webp_decoding */,
         testing::Values(false) /* advertise_accelerated_decoding */,
-        testing::Bool() /* enable_clipped_image_scaling */));
+        testing::Bool() /* enable_clipped_image_scaling */,
+        testing::Values(false) /* no_discardable_memory */));
 
 INSTANTIATE_TEST_SUITE_P(
     GpuImageDecodeCacheTestsOOPR,
@@ -3641,7 +3648,8 @@
         testing::Values(false) /* allow_accelerated_jpeg_decoding */,
         testing::Values(false) /* allow_accelerated_webp_decoding */,
         testing::Values(false) /* advertise_accelerated_decoding */,
-        testing::Values(false) /* enable_clipped_image_scaling */));
+        testing::Values(false) /* enable_clipped_image_scaling */,
+        testing::Values(false) /* no_discardable_memory */));
 
 class GpuImageDecodeCacheWithAcceleratedDecodesTest
     : public GpuImageDecodeCacheTest {
@@ -3957,7 +3965,8 @@
         testing::Values(true) /* allow_accelerated_jpeg_decoding */,
         testing::Values(true) /* allow_accelerated_webp_decoding */,
         testing::Values(true) /* advertise_accelerated_decoding */,
-        testing::Values(false) /* enable_clipped_image_scaling */));
+        testing::Values(false) /* enable_clipped_image_scaling */,
+        testing::Bool() /* no_discardable_memory */));
 
 class GpuImageDecodeCacheWithAcceleratedDecodesFlagsTest
     : public GpuImageDecodeCacheWithAcceleratedDecodesTest {};
@@ -4093,14 +4102,14 @@
 INSTANTIATE_TEST_SUITE_P(
     GpuImageDecodeCacheTestsOOPR,
     GpuImageDecodeCacheWithAcceleratedDecodesFlagsTest,
-    testing::Combine(
-        testing::Values(kN32_SkColorType),
-        testing::Values(true) /* use_transfer_cache */,
-        testing::Bool() /* do_yuv_decode */,
-        testing::Bool() /* allow_accelerated_jpeg_decoding */,
-        testing::Bool() /* allow_accelerated_webp_decoding */,
-        testing::Bool() /* advertise_accelerated_decoding */,
-        testing::Values(false) /* enable_clipped_image_scaling */));
+    testing::Combine(testing::Values(kN32_SkColorType),
+                     testing::Values(true) /* use_transfer_cache */,
+                     testing::Bool() /* do_yuv_decode */,
+                     testing::Bool() /* allow_accelerated_jpeg_decoding */,
+                     testing::Bool() /* allow_accelerated_webp_decoding */,
+                     testing::Bool() /* advertise_accelerated_decoding */,
+                     testing::Values(false) /* enable_clipped_image_scaling */,
+                     testing::Bool() /* no_discardable_memory */));
 
 #undef EXPECT_TRUE_IF_NOT_USING_TRANSFER_CACHE
 #undef EXPECT_FALSE_IF_NOT_USING_TRANSFER_CACHE
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index a1386c19..eef870d3 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -5112,6 +5112,11 @@
   return client_->IsReadyToActivate();
 }
 
+void LayerTreeHostImpl::RequestImplSideInvalidationForRerasterTiling() {
+  bool needs_first_draw_on_activation = true;
+  client_->NeedsImplSideInvalidation(needs_first_draw_on_activation);
+}
+
 base::WeakPtr<LayerTreeHostImpl> LayerTreeHostImpl::AsWeakPtr() {
   return weak_factory_.GetWeakPtr();
 }
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 982117d..44a93c13 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -896,6 +896,8 @@
 
   bool IsReadyToActivate() const;
 
+  void RequestImplSideInvalidationForRerasterTiling();
+
  protected:
   LayerTreeHostImpl(
       const LayerTreeSettings& settings,
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 97dc7ad5..c66f51e04c 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -10102,11 +10102,6 @@
                                            transform);
         node->has_potential_animation = true;
         break;
-      case 3:
-        // animation finished
-        node->has_potential_animation = false;
-        transform_tree.set_needs_update(true);
-        break;
     }
   }
 
@@ -10169,17 +10164,165 @@
         PostSetNeedsCommitToMainThread();
         break;
       case 2:
-        // translation is changed in frame2, but recreating tiling should not
-        // happen because ReadyToActivate is true
-        ASSERT_EQ(tiling_transform.scale(), gfx::Vector2dF(2.0f, 1.0f));
-        ASSERT_EQ(tiling_transform.translation(), gfx::Vector2dF(0, 0));
-        PostSetNeedsCommitToMainThread();
+        if (should_delay_recreating_tiling_) {
+          // translation is changed in frame2, but recreating tiling should not
+          // happen because ReadyToActivate is true
+          ASSERT_EQ(tiling_transform.scale(), gfx::Vector2dF(2.0f, 1.0f));
+          ASSERT_EQ(tiling_transform.translation(), gfx::Vector2dF(0, 0));
+          should_delay_recreating_tiling_ = false;
+        } else {
+          // Invalidating implside will trigger recreating tiling without next
+          // commit
+          ASSERT_EQ(tiling_transform.scale(), gfx::Vector2dF(2.0f, 1.0f));
+          ASSERT_EQ(tiling_transform.translation(), gfx::Vector2dF(0, 0.8f));
+          EndTest();
+        }
         break;
       case 3:
-        // in next commit, the new tiling with new translation is generated
-        ASSERT_EQ(tiling_transform.scale(), gfx::Vector2dF(2.0f, 1.0f));
-        ASSERT_EQ(tiling_transform.translation(), gfx::Vector2dF(0, 0.8f));
-        EndTest();
+        NOTREACHED() << "We shouldn't see another commit in this test";
+        break;
+    }
+  }
+
+ protected:
+  FakeContentLayerClient client_;
+  // to access layer information in main and impl
+  int layer_id_;
+  // to access layer information in main's WillCommit()
+  scoped_refptr<FakePictureLayer> layer_on_main_;
+  bool should_delay_recreating_tiling_ = true;
+};
+MULTI_THREAD_TEST_F(LayerTreeHostTestDelayRecreateTiling);
+
+// This test validate that recreating tiling is delayed by veto conditions and
+// the delayed tiling is created again when the veto conditions are reset.
+class LayerTreeHostTestInvalidateImplSideForRerasterTiling
+    : public LayerTreeHostTestWithHelper {
+ public:
+  void SetupTree() override {
+    client_.set_fill_with_nonsolid_color(true);
+    scoped_refptr<FakePictureLayer> root_layer =
+        FakePictureLayer::Create(&client_);
+    root_layer->SetBounds(gfx::Size(150, 150));
+    root_layer->SetIsDrawable(true);
+
+    layer_on_main_ =
+        CreateAndAddFakePictureLayer(gfx::Size(30, 30), root_layer.get());
+
+    // initial transform to force transform node
+    gfx::Transform transform;
+    transform.Scale(2.0f, 1.0f);
+    layer_on_main_->SetTransform(transform);
+
+    layer_tree_host()->SetRootLayer(root_layer);
+
+    LayerTreeHostTest::SetupTree();
+    client_.set_bounds(root_layer->bounds());
+
+    layer_id_ = layer_on_main_->id();
+  }
+
+  void WillCommit(const CommitState&) override {
+    TransformTree& transform_tree =
+        layer_tree_host()->property_trees()->transform_tree_mutable();
+    TransformNode* node =
+        transform_tree.Node(layer_on_main_->transform_tree_index());
+
+    gfx::Transform transform;
+    transform.Scale(2.0f, 1.0f);
+    transform.Translate(0.0f, 0.8f);
+
+    switch (layer_tree_host()->SourceFrameNumber()) {
+      case 1:
+        // in frame1, translation changed and animation start
+        transform_tree.OnTransformAnimated(layer_on_main_->element_id(),
+                                           transform);
+        node->has_potential_animation = true;
+        break;
+    }
+  }
+
+  void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+  void ClearAnimationForLayer(LayerTreeImpl* tree_impl,
+                              FakePictureLayerImpl* layer_impl) {
+    if (!tree_impl)
+      return;
+
+    TransformTree& transform_tree =
+        tree_impl->property_trees()->transform_tree_mutable();
+    TransformNode* node =
+        transform_tree.Node(layer_impl->transform_tree_index());
+
+    node->has_potential_animation = false;
+    transform_tree.set_needs_update(true);
+    tree_impl->set_needs_update_draw_properties();
+  }
+
+  TransformNode* TransformNodeForLayer(LayerTreeImpl* tree_impl,
+                                       FakePictureLayerImpl* layer_impl) {
+    TransformTree& transform_tree =
+        tree_impl->property_trees()->transform_tree_mutable();
+    TransformNode* node =
+        transform_tree.Node(layer_impl->transform_tree_index());
+    return node;
+  }
+
+  void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
+    if (host_impl->active_tree()->source_frame_number() == 2) {
+      FakePictureLayerImpl* target_layer = static_cast<FakePictureLayerImpl*>(
+          host_impl->active_tree()->LayerById(layer_id_));
+      gfx::AxisTransform2d tiling_transform =
+          target_layer->HighResTiling()->raster_transform();
+      TransformNode* node =
+          TransformNodeForLayer(host_impl->active_tree(), target_layer);
+      if (node->has_potential_animation) {
+        // in frame 2, active tree still have old tiling because animation is
+        // still active.
+        EXPECT_EQ(tiling_transform.scale(), gfx::Vector2dF(2.0f, 1.0f));
+        EXPECT_EQ(tiling_transform.translation(), gfx::Vector2dF(0, 0));
+
+        // now clear animation and trigger a new draw to check if invalidation
+        // implside will be requested for rerastering tiling.
+        ClearAnimationForLayer(host_impl->active_tree(), target_layer);
+        ClearAnimationForLayer(host_impl->recycle_tree(), target_layer);
+        host_impl->SetNeedsRedraw();
+      }
+    }
+  }
+
+  void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
+    FakePictureLayerImpl* target_layer = static_cast<FakePictureLayerImpl*>(
+        host_impl->active_tree()->LayerById(layer_id_));
+    gfx::AxisTransform2d tiling_transform =
+        target_layer->HighResTiling()->raster_transform();
+    TransformNode* node =
+        TransformNodeForLayer(host_impl->active_tree(), target_layer);
+    switch (host_impl->active_tree()->source_frame_number()) {
+      case 0:
+        PostSetNeedsCommitToMainThread();
+        break;
+      case 1:
+        PostSetNeedsCommitToMainThread();
+        break;
+      case 2:
+        if (node->has_potential_animation) {
+          // translation is changed in frame2, but recreating tiling should not
+          // happen because animation is still active.
+          EXPECT_EQ(tiling_transform.scale(), gfx::Vector2dF(2.0f, 1.0f));
+          EXPECT_EQ(tiling_transform.translation(), gfx::Vector2dF(0, 0));
+
+          // trigger draw to check if invalidating implside is not triggered
+          // if animation is still active.
+          host_impl->SetNeedsRedraw();
+        } else {
+          // check if invalidation implside was requested successfully.
+          // new tiling should be created.
+          EXPECT_EQ(tiling_transform.scale(), gfx::Vector2dF(2.0f, 1.0f));
+          EXPECT_EQ(tiling_transform.translation(), gfx::Vector2dF(0, 0.8f));
+          EndTest();
+        }
+        break;
     }
   }
 
@@ -10190,7 +10333,6 @@
   // to access layer information in main's WillCommit()
   scoped_refptr<FakePictureLayer> layer_on_main_;
 };
-MULTI_THREAD_TEST_F(LayerTreeHostTestDelayRecreateTiling);
-
+MULTI_THREAD_TEST_F(LayerTreeHostTestInvalidateImplSideForRerasterTiling);
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index d347a91c..d0f0aa8 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1871,6 +1871,10 @@
   return host_impl_->sync_tree() == this;
 }
 
+bool LayerTreeImpl::HasPendingTree() const {
+  return host_impl_->pending_tree() != nullptr;
+}
+
 LayerImpl* LayerTreeImpl::FindActiveTreeLayerById(int id) {
   LayerTreeImpl* tree = host_impl_->active_tree();
   if (!tree)
@@ -2934,4 +2938,8 @@
   visual_update_duration_ = visual_update_duration;
 }
 
+void LayerTreeImpl::RequestImplSideInvalidationForRerasterTiling() {
+  host_impl_->RequestImplSideInvalidationForRerasterTiling();
+}
+
 }  // namespace cc
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index d3f2bbf..4c455a4 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -139,6 +139,7 @@
   bool IsPendingTree() const;
   bool IsRecycleTree() const;
   bool IsSyncTree() const;
+  bool HasPendingTree() const;
   LayerImpl* FindActiveTreeLayerById(int id);
   LayerImpl* FindPendingTreeLayerById(int id);
   // TODO(bokan): PinchGestureActive is a layering violation, it's not related
@@ -166,6 +167,7 @@
   TargetColorParams GetTargetColorParams(
       gfx::ContentColorUsage content_color_usage) const;
   bool IsReadyToActivate() const;
+  void RequestImplSideInvalidationForRerasterTiling();
 
   // Tree specific methods exposed to layer-impl tree.
   // ---------------------------------------------------------------------------
diff --git a/cc/trees/ukm_manager.cc b/cc/trees/ukm_manager.cc
index f0cc2f4..4bb9182a 100644
--- a/cc/trees/ukm_manager.cc
+++ b/cc/trees/ukm_manager.cc
@@ -328,17 +328,9 @@
     auto stage_it = std::find_if(
         stage_history.begin(), stage_history.end(),
         [dispatch_timestamp](const CompositorFrameReporter::StageData& stage) {
-          return stage.start_time > dispatch_timestamp;
+          return stage.start_time >= dispatch_timestamp;
         });
-    // TODO(crbug.com/1079116): Ideally, at least the start time of
-    // SubmitCompositorFrameToPresentationCompositorFrame stage should be
-    // greater than the final event dispatch timestamp, but apparently, this is
-    // not always the case (see crbug.com/1093698). For now, skip to the next
-    // event in such cases. Hopefully, the work to reduce discrepancies between
-    // the new EventLatency and the old Event.Latency metrics would fix this
-    // issue. If not, we need to reconsider investigating this issue.
-    if (stage_it == stage_history.end())
-      continue;
+    DCHECK(stage_it != stage_history.end());
 
     switch (dispatch_stage) {
       case EventMetrics::DispatchStage::kRendererCompositorFinished:
diff --git a/chrome/VERSION b/chrome/VERSION
index 40e610c..1be7dd97 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=104
 MINOR=0
-BUILD=5094
+BUILD=5095
 PATCH=0
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandler.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandler.java
index 479fba3..4e262b6 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandler.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandler.java
@@ -194,6 +194,10 @@
                 booleanCallback.onResult(false);
                 return;
             }
+            if (delegate.isSupervisedUser()) {
+                booleanCallback.onResult(false);
+                return;
+            }
             if (ONBOARDING_ACTION.equals(actionId)) {
                 delegate.performOnboarding(experimentIds, arguments, booleanCallback);
                 return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionRecorder.java
index 0d8a7c6..5a9dd62 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionRecorder.java
@@ -86,13 +86,6 @@
     void setupLoggingForPage(@Nullable WebContents basePageWebContents);
 
     /**
-     * Logs a particular feature at inference time as a key/value pair.
-     * @param feature The feature to log.
-     * @param value The value to log, which is associated with the given key.
-     */
-    void logFeature(@Feature int feature, Object value);
-
-    /**
      * Returns whether or not AssistRanker query is enabled.
      */
     boolean isQueryEnabled();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 8ff9b1e..caec0d8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -1651,18 +1651,6 @@
         mInternalStateController.enter(InternalState.SELECTION_CLEARED_RECOGNIZED);
     }
 
-    @Override
-    public void logNonHeuristicFeatures(ContextualSearchInteractionRecorder rankerLogger) {
-        boolean didOptIn = mPolicy.isContextualSearchFullyEnabled();
-        rankerLogger.logFeature(ContextualSearchInteractionRecorder.Feature.DID_OPT_IN, didOptIn);
-        boolean isHttp = mPolicy.isBasePageHTTP(getBasePageURL());
-        rankerLogger.logFeature(ContextualSearchInteractionRecorder.Feature.IS_HTTP, isHttp);
-        String contentLanguage = mContext.getDetectedLanguage();
-        boolean isLanguageMismatch = mTranslateController.needsTranslation(contentLanguage);
-        rankerLogger.logFeature(ContextualSearchInteractionRecorder.Feature.IS_LANGUAGE_MISMATCH,
-                isLanguageMismatch);
-    }
-
     /** Shows the given selection as the Search Term in the Bar. */
     private void showSelectionAsSearchInBar(String selection) {
         if (isSearchPanelShowing()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
index ecda925..8eed3c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
@@ -39,7 +39,6 @@
     private Map<Integer /* @Feature */, Object> mFeaturesToLog;
 
     // A for-testing copy of all the features to log setup so that it will survive a {@link #reset}.
-    private Map<Integer /* @Feature */, Object> mFeaturesLoggedForTesting;
     private Map<Integer /* @Feature */, Object> mOutcomesLoggedForTesting;
 
     private ContextualSearchInteractionPersister mInteractionPersister;
@@ -92,79 +91,6 @@
     }
 
     /**
-     * Gets the name of the given feature.
-     * @param feature An outcome that might have been expected to be logged.
-     * @return The name of the outcome if it's expected to be logged, or {@code null} if it's not
-     *         expected to be logged.
-     */
-    @VisibleForTesting
-    protected static final String featureName(@Feature int feature) {
-        // NOTE: this list needs to be kept in sync with the white list in
-        // predictor_config_definitions.cc and with ukm.xml!
-        switch (feature) {
-            case Feature.DURATION_AFTER_SCROLL_MS:
-                return "DurationAfterScrollMs";
-            case Feature.SCREEN_TOP_DPS:
-                return "ScreenTopDps";
-            case Feature.WAS_SCREEN_BOTTOM:
-                return "WasScreenBottom";
-            case Feature.PREVIOUS_WEEK_IMPRESSIONS_COUNT:
-                return "PreviousWeekImpressionsCount";
-            case Feature.PREVIOUS_WEEK_CTR_PERCENT:
-                return "PreviousWeekCtrPercent";
-            case Feature.PREVIOUS_28DAY_IMPRESSIONS_COUNT:
-                return "Previous28DayImpressionsCount";
-            case Feature.PREVIOUS_28DAY_CTR_PERCENT:
-                return "Previous28DayCtrPercent";
-            // UKM CS v2 features.
-            case Feature.DID_OPT_IN:
-                return "DidOptIn";
-            case Feature.IS_SHORT_WORD:
-                return "IsShortWord";
-            case Feature.IS_LONG_WORD:
-                return "IsLongWord";
-            case Feature.IS_WORD_EDGE:
-                return "IsWordEdge";
-            case Feature.IS_ENTITY:
-                return "IsEntity";
-            case Feature.TAP_DURATION_MS:
-                return "TapDurationMs";
-            // UKM CS v3 features.
-            case Feature.FONT_SIZE:
-                return "FontSize";
-            case Feature.IS_HTTP:
-                return "IsHttp";
-            case Feature.IS_SECOND_TAP_OVERRIDE:
-                return "IsSecondTapOverride";
-            case Feature.IS_ENTITY_ELIGIBLE:
-                return "IsEntityEligible";
-            case Feature.IS_LANGUAGE_MISMATCH:
-                return "IsLanguageMismatch";
-            case Feature.PORTION_OF_ELEMENT:
-                return "PortionOfElement";
-            // UKM CS v4 features.
-            case Feature.TAP_COUNT:
-                return "TapCount";
-            case Feature.OPEN_COUNT:
-                return "OpenCount";
-            case Feature.QUICK_ANSWER_COUNT:
-                return "QuickAnswerCount";
-            case Feature.ENTITY_IMPRESSIONS_COUNT:
-                return "EntityImpressionsCount";
-            case Feature.ENTITY_OPENS_COUNT:
-                return "EntityOpensCount";
-            case Feature.QUICK_ACTION_IMPRESSIONS_COUNT:
-                return "QuickActionImpressionsCount";
-            case Feature.QUICK_ACTIONS_TAKEN_COUNT:
-                return "QuickActionsTaken";
-            case Feature.QUICK_ACTIONS_IGNORED_COUNT:
-                return "QuickActionsIgnored";
-            default:
-                return null;
-        }
-    }
-
-    /**
      * This method should be called to clean up storage when an instance of this class is
      * no longer in use.  The ContextualSearchRankerLoggerImplJni.get().destroy will call the
      * destructor on the native instance.
@@ -197,15 +123,6 @@
     }
 
     @Override
-    public void logFeature(@Feature int feature, Object value) {
-        assert mIsLoggingReadyForPage : "mIsLoggingReadyForPage false.";
-        assert !mHasInferenceOccurred;
-        if (!isEnabled()) return;
-
-        logInternal(feature, value);
-    }
-
-    @Override
     public void logOutcome(@Feature int feature, Object value) {
         assert mIsLoggingReadyForPage;
         assert mHasInferenceOccurred;
@@ -219,13 +136,7 @@
         assert mIsLoggingReadyForPage;
         assert !mHasInferenceOccurred;
         mHasInferenceOccurred = true;
-        if (isEnabled() && mBasePageWebContents != null && mFeaturesToLog != null
-                && !mFeaturesToLog.isEmpty()) {
-            for (Map.Entry<Integer, Object> entry : mFeaturesToLog.entrySet()) {
-                logObject(entry.getKey(), entry.getValue());
-            }
-            mFeaturesLoggedForTesting = mFeaturesToLog;
-            mFeaturesToLog = new HashMap<Integer, Object>();
+        if (isEnabled() && mBasePageWebContents != null) {
             mAssistRankerPrediction = ContextualSearchRankerLoggerImplJni.get().runInference(
                     mNativePointer, ContextualSearchRankerLoggerImpl.this);
             ContextualSearchUma.logRecordedFeaturesToRanker();
@@ -254,11 +165,7 @@
                     && !mFeaturesToLog.isEmpty()) {
                 assert mIsLoggingReadyForPage;
                 assert mHasInferenceOccurred;
-                // Only the outcomes will be present, since we logged inference features at
-                // inference time.
-                for (Map.Entry<Integer, Object> entry : mFeaturesToLog.entrySet()) {
-                    logObject(entry.getKey(), entry.getValue());
-                }
+
                 mOutcomesLoggedForTesting = mFeaturesToLog;
                 ContextualSearchUma.logRecordedOutcomesToRanker();
                 // Also persist the outcomes if we are persisting this interaction.
@@ -301,50 +208,6 @@
     }
 
     /**
-     * Logs the given {@link ContextualSearchInteractionRecorder.Feature} with the given value
-     * {@link Object}.
-     * @param feature The feature to log.
-     * @param value An {@link Object} value to log (must be convertible to a {@code long}).
-     */
-    private void logObject(@Feature int feature, Object value) {
-        if (value instanceof Boolean) {
-            logToNative(feature, ((boolean) value ? 1 : 0));
-        } else if (value instanceof Integer) {
-            logToNative(feature, (int) value);
-        } else if (value instanceof Character) {
-            logToNative(feature, Character.getNumericValue((char) value));
-        } else {
-            assert false : "Could not log feature to Ranker: " + String.valueOf(feature)
-                           + " of class "
-                           + value.getClass();
-        }
-    }
-
-    /**
-     * Logs to the native instance.  All native logging must go through this bottleneck.
-     * @param feature The feature to log.
-     * @param value The value to log.
-     */
-    private void logToNative(@Feature int feature, int value) {
-        String featureName =
-                outcomeName(feature) == null ? featureName(feature) : outcomeName(feature);
-        assert featureName != null : "No Name for feature " + feature;
-        ContextualSearchRankerLoggerImplJni.get().logInt32(
-                mNativePointer, ContextualSearchRankerLoggerImpl.this, featureName, value);
-    }
-
-    /**
-     * Gets the current set of features that have been logged.  Should only be used for testing
-     * purposes!
-     * @return The current set of features that have been logged, or {@code null}.
-     */
-    @VisibleForTesting
-    @Nullable
-    Map<Integer, Object> getFeaturesLogged() {
-        return mFeaturesLoggedForTesting;
-    }
-
-    /**
      * Gets the current set of outcomes that have been logged.  Should only be used for
      * testing purposes!
      * @return The current set of outcomes that have been logged, or {@code null}.
@@ -365,8 +228,6 @@
 
         void destroy(long nativeContextualSearchRankerLoggerImpl,
                 ContextualSearchRankerLoggerImpl caller);
-        void logInt32(long nativeContextualSearchRankerLoggerImpl,
-                ContextualSearchRankerLoggerImpl caller, String featureString, int value);
         void setupLoggingAndRanker(long nativeContextualSearchRankerLoggerImpl,
                 ContextualSearchRankerLoggerImpl caller, WebContents basePageWebContents);
         // Returns an AssistRankerPrediction integer value.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
index bff7be46d..1687e76 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
@@ -480,7 +480,6 @@
         int tapPrediction = AssistRankerPrediction.UNDETERMINED;
         if (!shouldSuppressTapBasedOnHeuristics) {
             tapHeuristics.logRankerTapSuppression(interactionRecorder);
-            mHandler.logNonHeuristicFeatures(interactionRecorder);
             tapPrediction = interactionRecorder.runPredictionForTapSuppression();
             ContextualSearchUma.logRankerPrediction(tapPrediction);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java
index 36065542..08009500 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionHandler.java
@@ -76,14 +76,6 @@
     public void handleMetricsForWouldSuppressTap(ContextualSearchHeuristics tapHeuristics);
 
     /**
-     * Logs all the features that we can obtain without accessing heuristics, i.e. from global
-     * state.
-     * @param interactionRecorder The {@link ContextualSearchInteractionRecorder} to log the
-     * features to.
-     */
-    public void logNonHeuristicFeatures(ContextualSearchInteractionRecorder interactionRecorder);
-
-    /**
      * Handles a long-press gesture that may make a server Resolve request to determine the search.
      */
     void handleValidResolvingLongpress();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
index 7bc4878..9654617 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
@@ -575,28 +575,6 @@
         }
     }
 
-    /**
-     * Gets the name of the given feature when it's expected to be logged.
-     *
-     * @param feature An outcome that might have been expected to be logged.
-     * @return The name of the outcome if it's expected to be logged, or {@code null} if it's not
-     * expected to be logged.
-     */
-    private static final String expectedFeatureName(
-            @ContextualSearchInteractionRecorder.Feature int feature) {
-        switch (feature) {
-            // We don't log previous user impressions and CTR if not available for the
-            // current user.
-            case ContextualSearchInteractionRecorder.Feature.PREVIOUS_WEEK_CTR_PERCENT:
-            case ContextualSearchInteractionRecorder.Feature.PREVIOUS_WEEK_IMPRESSIONS_COUNT:
-            case ContextualSearchInteractionRecorder.Feature.PREVIOUS_28DAY_CTR_PERCENT:
-            case ContextualSearchInteractionRecorder.Feature.PREVIOUS_28DAY_IMPRESSIONS_COUNT:
-                return null;
-            default:
-                return ContextualSearchRankerLoggerImpl.featureName(feature);
-        }
-    }
-
     protected interface ThrowingRunnable {
         void run() throws TimeoutException;
     }
@@ -1524,21 +1502,6 @@
         return rankerLogger;
     }
 
-    /**
-     * @return The value of the given logged feature, or {@code null} if not logged.
-     */
-    protected Object loggedToRanker(@ContextualSearchInteractionRecorder.Feature int feature) {
-        return getRankerLogger().getFeaturesLogged().get(feature);
-    }
-
-    /** Asserts that all the expected features have been logged to Ranker. **/
-    protected void assertLoggedAllExpectedFeaturesToRanker() {
-        for (int feature = 0; feature < ContextualSearchInteractionRecorder.Feature.NUM_ENTRIES;
-                feature++) {
-            if (expectedFeatureName(feature) != null) Assert.assertNotNull(loggedToRanker(feature));
-        }
-    }
-
     /** Asserts that all the expected outcomes have been logged to Ranker. **/
     protected void assertLoggedAllExpectedOutcomesToRanker() {
         for (int feature = 0; feature < ContextualSearchInteractionRecorder.Feature.NUM_ENTRIES;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java
index 46c45a1..a1ad36a5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java
@@ -230,7 +230,6 @@
         clickWordNode("states");
         Assert.assertEquals("States", getSelectedText());
         waitForPanelToPeek();
-        assertLoggedAllExpectedFeaturesToRanker();
         // Avoid issues with double-tap detection by ensuring sequential taps
         // aren't treated as such. Double-tapping can also select words much as
         // longpress, in turn showing the pins and preventing contextual tap
@@ -243,7 +242,6 @@
         clickNode("states-near");
         waitForSelectionToBe("StatesNear");
         assertLoggedAllExpectedOutcomesToRanker();
-        assertLoggedAllExpectedFeaturesToRanker();
         Thread.sleep(ViewConfiguration.getDoubleTapTimeout());
         clickNode("states");
         waitForSelectionToBe("States");
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index df6a635..663df8b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4978,9 +4978,6 @@
      flag_descriptions::kDriveFsChromeNetworkingName,
      flag_descriptions::kDriveFsChromeNetworkingDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kDriveFsChromeNetworking)},
-    {"files-archivemount", flag_descriptions::kFilesArchivemountName,
-     flag_descriptions::kFilesArchivemountDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::features::kFilesArchivemount)},
     {"files-archivemount2", flag_descriptions::kFilesArchivemount2Name,
      flag_descriptions::kFilesArchivemount2Description, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kFilesArchivemount2)},
diff --git a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc
index 352e99f..01beecb 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc
@@ -25,12 +25,8 @@
 
 namespace {
 
-const char kContextualSearchRankerDidPredict[] = "OutcomeRankerDidPredict";
-const char kContextualSearchRankerPrediction[] = "OutcomeRankerPrediction";
 const char kContextualSearchImportantFeature[] = "DidOptIn";
 const char kContextualSearchImportantOutcome[] = "OutcomeWasPanelOpened";
-const char kContextualSearchRankerPredictionScore[] =
-    "OutcomeRankerPredictionScore";
 
 }  // namespace
 
@@ -73,22 +69,6 @@
   }
 }
 
-void ContextualSearchRankerLoggerImpl::LogFeature(
-    const std::string& feature_name,
-    int value) {
-  auto& features = *ranker_example_->mutable_features();
-  features[feature_name].set_int32_value(value);
-}
-
-void ContextualSearchRankerLoggerImpl::LogInt32(
-    JNIEnv* env,
-    jobject obj,
-    const base::android::JavaParamRef<jstring>& j_feature,
-    jint j_int) {
-  std::string feature = base::android::ConvertJavaStringToUTF8(env, j_feature);
-  LogFeature(feature, j_int);
-}
-
 AssistRankerPrediction ContextualSearchRankerLoggerImpl::RunInference(
     JNIEnv* env,
     jobject obj) {
@@ -100,20 +80,6 @@
     // Log to UMA whether we were able to predict or not.
     base::UmaHistogramBoolean("Search.ContextualSearch.Ranker.WasAbleToPredict",
                               was_able_to_predict);
-    // TODO(chrome-ranker-team): this should be logged internally by Ranker.
-    LogFeature(kContextualSearchRankerDidPredict,
-               static_cast<int>(was_able_to_predict));
-    if (was_able_to_predict) {
-      LogFeature(kContextualSearchRankerPrediction,
-                 static_cast<int>(prediction));
-      // For offline validation also log the prediction score.
-      // TODO(donnd): remove when https://crbug.com/914179 is resolved.
-      float score;
-      bool was_able_to_predict_score =
-          predictor_->PredictScore(*ranker_example_, &score);
-      if (was_able_to_predict_score)
-        LogFeature(kContextualSearchRankerPredictionScore, score);
-    }
   }
   AssistRankerPrediction prediction_enum;
   if (was_able_to_predict) {
diff --git a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h
index f409f33..d5dab2b4 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h
+++ b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h
@@ -57,12 +57,6 @@
       jobject obj,
       const base::android::JavaParamRef<jobject>& java_web_contents);
 
-  // Logs an int32 value with the given feature name.
-  void LogInt32(JNIEnv* env,
-                jobject obj,
-                const base::android::JavaParamRef<jstring>& j_feature,
-                jint j_int);
-
   // Runs the model and returns the inference result as an
   // AssistRankerPrediction enum.
   AssistRankerPrediction RunInference(JNIEnv* env, jobject obj);
@@ -78,9 +72,6 @@
   // Returns whether or not AssistRanker query is enabled.
   bool IsQueryEnabledInternal();
 
-  // Adds feature to the RankerExample.
-  void LogFeature(const std::string& feature_name, int value);
-
   // Sets up the Ranker Predictor for the given |web_contents|.
   void SetupRankerPredictor(content::WebContents& web_contents);
 
diff --git a/chrome/browser/apps/app_service/web_contents_app_id_utils.cc b/chrome/browser/apps/app_service/web_contents_app_id_utils.cc
index f35a05ce..4ecc726 100644
--- a/chrome/browser/apps/app_service/web_contents_app_id_utils.cc
+++ b/chrome/browser/apps/app_service/web_contents_app_id_utils.cc
@@ -119,25 +119,16 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 std::string GetAppIdForWebContents(content::WebContents* web_contents) {
-  std::string app_id;
+  const web_app::AppId* app_id =
+      web_app::WebAppTabHelper::GetAppId(web_contents);
+  if (app_id)
+    return *app_id;
 
-  web_app::WebAppTabHelper* web_app_tab_helper =
-      web_app::WebAppTabHelper::FromWebContents(web_contents);
-  // web_app_tab_helper is nullptr in some unit tests.
-  if (web_app_tab_helper) {
-    app_id = web_app_tab_helper->GetAppId();
-  }
-
-  if (app_id.empty()) {
-    extensions::TabHelper* extensions_tab_helper =
-        extensions::TabHelper::FromWebContents(web_contents);
-    // extensions_tab_helper is nullptr in some tests.
-    if (extensions_tab_helper) {
-      app_id = extensions_tab_helper->GetExtensionAppId();
-    }
-  }
-
-  return app_id;
+  extensions::TabHelper* extensions_tab_helper =
+      extensions::TabHelper::FromWebContents(web_contents);
+  // extensions_tab_helper is nullptr in some tests.
+  return extensions_tab_helper ? extensions_tab_helper->GetExtensionAppId()
+                               : std::string();
 }
 
 void SetAppIdForWebContents(Profile* profile,
@@ -154,13 +145,14 @@
   if (extension) {
     DCHECK(extension->is_app());
     web_app::WebAppTabHelper::FromWebContents(web_contents)
-        ->SetAppId(std::string());
+        ->SetAppId(absl::nullopt);
     extensions::TabHelper::FromWebContents(web_contents)
         ->SetExtensionAppById(app_id);
   } else {
     bool app_installed = IsAppReady(profile, app_id);
     web_app::WebAppTabHelper::FromWebContents(web_contents)
-        ->SetAppId(app_installed ? app_id : std::string());
+        ->SetAppId(app_installed ? absl::optional<web_app::AppId>(app_id)
+                                 : absl::nullopt);
     extensions::TabHelper::FromWebContents(web_contents)
         ->SetExtensionAppById(std::string());
   }
diff --git a/chrome/browser/apps/digital_goods/digital_goods_lacros.cc b/chrome/browser/apps/digital_goods/digital_goods_lacros.cc
index 2ac80842..bf7dcac 100644
--- a/chrome/browser/apps/digital_goods/digital_goods_lacros.cc
+++ b/chrome/browser/apps/digital_goods/digital_goods_lacros.cc
@@ -141,12 +141,6 @@
     return;
   }
 
-  pending_callbacks_.push_back(std::move(callback));
-  if (pending_callbacks_.size() > 1) {
-    // A crosapi call is already in flight, just wait for it to return.
-    return;
-  }
-
   auto id_and_scope = GetWebAppIdAndScopeForDocument(render_frame_host());
   if (!id_and_scope) {
     std::move(callback).Run(
@@ -165,6 +159,12 @@
     return;
   }
 
+  pending_callbacks_.push_back(std::move(callback));
+  if (pending_callbacks_.size() > 1) {
+    // A crosapi call is already in flight, just wait for it to return.
+    return;
+  }
+
   lacros_service->GetRemote<crosapi::mojom::DigitalGoodsFactory>()
       ->CreateDigitalGoods(
           payment_method, app_id,
diff --git a/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
index dfa306e3..d73adf8 100644
--- a/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
@@ -10,6 +10,7 @@
 
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "base/containers/contains.h"
+#include "base/memory/values_equivalent.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
@@ -227,10 +228,10 @@
   }
 
   // Don't capture if already inside the target app scope.
-  if (app_type == AppType::kWeb) {
-    auto* tab_helper = web_app::WebAppTabHelper::FromWebContents(web_contents);
-    if (tab_helper && tab_helper->GetAppId() == preferred_app_id.value())
-      return false;
+  if (app_type == AppType::kWeb &&
+      base::ValuesEquivalent(web_app::WebAppTabHelper::GetAppId(web_contents),
+                             &preferred_app_id.value())) {
+    return false;
   }
 
   // If this is a prerender navigation that would otherwise launch an app, we
diff --git a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
index 2342030..367478b 100644
--- a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
+++ b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
@@ -25,6 +25,7 @@
 #include "components/feature_engagement/public/tracker.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
+#include "ui/gfx/favicon_size.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.h"
@@ -196,4 +197,12 @@
 #endif
 }
 
+int GetIntentPickerBubbleIconSize() {
+  constexpr int kIntentPickerUiUpdateIconSize = 40;
+
+  return features::LinkCapturingUiUpdateEnabled()
+             ? kIntentPickerUiUpdateIconSize
+             : gfx::kFaviconSize;
+}
+
 }  // namespace apps
diff --git a/chrome/browser/apps/intent_helper/intent_picker_helpers.h b/chrome/browser/apps/intent_helper/intent_picker_helpers.h
index 67b691f..a1671030 100644
--- a/chrome/browser/apps/intent_helper/intent_picker_helpers.h
+++ b/chrome/browser/apps/intent_helper/intent_picker_helpers.h
@@ -29,6 +29,9 @@
 // Returns true if persistence for PWA entries in the Intent Picker is enabled.
 bool IntentPickerPwaPersistenceEnabled();
 
+// Returns the size, in dp, of app icons shown in the intent picker bubble.
+int GetIntentPickerBubbleIconSize();
+
 }  // namespace apps
 
 #endif  // CHROME_BROWSER_APPS_INTENT_HELPER_INTENT_PICKER_HELPERS_H_
diff --git a/chrome/browser/ash/OWNERS b/chrome/browser/ash/OWNERS
index f4e4a5b..88b063b6 100644
--- a/chrome/browser/ash/OWNERS
+++ b/chrome/browser/ash/OWNERS
@@ -1,5 +1,16 @@
-# //chrome/browser/chromeos is moving to this directory, so share OWNERS.
-file://chrome/browser/chromeos/OWNERS
+achuith@chromium.org
+afakhry@chromium.org
+alemate@chromium.org
+hidehiko@chromium.org
+jamescook@chromium.org
+khorimoto@chromium.org
+oshima@chromium.org
+xiyuan@chromium.org
 
+# This is for the common case of adding or renaming files. If you're doing
+# structural changes, use usual OWNERS rules.
+per-file BUILD.gn=*
+
+per-file *active_directory*=file://chrome/browser/ash/authpolicy/OWNERS
 per-file note_taking*=glenrob@chromium.org
 per-file note_taking*=ericwilligers@chromium.org
diff --git a/chrome/browser/ash/app_mode/app_session_ash.h b/chrome/browser/ash/app_mode/app_session_ash.h
index 9027abdc..d9ee12d2 100644
--- a/chrome/browser/ash/app_mode/app_session_ash.h
+++ b/chrome/browser/ash/app_mode/app_session_ash.h
@@ -21,6 +21,7 @@
   void Init(Profile* profile, const std::string& app_id) override;
   void InitForWebKiosk(Browser* browser) override;
 
+  // Initializes an app session for Web kiosk with lacros.
   void InitForWebKioskWithLacros(Profile* profile);
 
  private:
diff --git a/chrome/browser/ash/borealis/borealis_features.cc b/chrome/browser/ash/borealis/borealis_features.cc
index 3321f07..be6ce7e3 100644
--- a/chrome/browser/ash/borealis/borealis_features.cc
+++ b/chrome/browser/ash/borealis/borealis_features.cc
@@ -114,6 +114,8 @@
         return AllowStatus::kAllowed;
       }
       return AllowStatus::kIncorrectToken;
+    } else if (IsBoard("draco")) {
+      return AllowStatus::kAllowed;
     }
     return AllowStatus::kIncorrectToken;
   }
diff --git a/chrome/browser/ash/child_accounts/time_limits/web_time_limit_navigation_throttle.cc b/chrome/browser/ash/child_accounts/time_limits/web_time_limit_navigation_throttle.cc
index cc62576d..8511d70d 100644
--- a/chrome/browser/ash/child_accounts/time_limits/web_time_limit_navigation_throttle.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/web_time_limit_navigation_throttle.cc
@@ -164,15 +164,13 @@
                   (type == Browser::Type::TYPE_POPUP);
   }
 
-  web_app::WebAppTabHelper* web_app_helper =
-      web_app::WebAppTabHelper::FromWebContents(web_contents);
-
-  bool is_app = web_app_helper && !web_app_helper->GetAppId().empty();
+  const web_app::AppId* app_id =
+      web_app::WebAppTabHelper::GetAppId(web_contents);
 
   base::TimeDelta time_limit = GetWebTimeLimit(browser_context);
   const std::string& app_locale = g_browser_process->GetApplicationLocale();
 
-  if (!is_app) {
+  if (!app_id) {
     const GURL& url = navigation_handle()->GetURL();
 
     std::string domain = net::registry_controlled_domains::GetDomainAndRegistry(
@@ -199,17 +197,16 @@
     return PROCEED;
 
   //  Don't throttle allowlisted applications.
-  if (IsWebAppAllowlisted(web_app_helper->GetAppId(), browser_context))
+  if (IsWebAppAllowlisted(*app_id, browser_context))
     return PROCEED;
 
   Profile* profile = Profile::FromBrowserContext(browser_context);
   std::string app_name;
   apps::AppServiceProxyFactory::GetForProfile(profile)
       ->AppRegistryCache()
-      .ForOneApp(web_app_helper->GetAppId(),
-                 [&app_name](const apps::AppUpdate& update) {
-                   app_name = update.ShortName();
-                 });
+      .ForOneApp(*app_id, [&app_name](const apps::AppUpdate& update) {
+        app_name = update.ShortName();
+      });
   return NavigationThrottle::ThrottleCheckResult(
       CANCEL, net::ERR_BLOCKED_BY_CLIENT,
       GetWebTimeLimitAppErrorPage(time_limit, app_locale, app_name));
diff --git a/chrome/browser/ash/child_accounts/time_limits/web_time_navigation_observer.cc b/chrome/browser/ash/child_accounts/time_limits/web_time_navigation_observer.cc
index beb2ad29..bbf0777 100644
--- a/chrome/browser/ash/child_accounts/time_limits/web_time_navigation_observer.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/web_time_navigation_observer.cc
@@ -48,9 +48,7 @@
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   if (!web_app::AreWebAppsEnabled(profile))
     return false;
-  const web_app::WebAppTabHelper* web_app_helper =
-      web_app::WebAppTabHelper::FromWebContents(web_contents());
-  return !web_app_helper->GetAppId().empty();
+  return web_app::WebAppTabHelper::GetAppId(web_contents()) != nullptr;
 }
 
 void WebTimeNavigationObserver::PrimaryPageChanged(content::Page& page) {
diff --git a/chrome/browser/ash/file_manager/extract_io_task.cc b/chrome/browser/ash/file_manager/extract_io_task.cc
index 245b5ee5..aba6cd1 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.cc
+++ b/chrome/browser/ash/file_manager/extract_io_task.cc
@@ -55,6 +55,19 @@
 
 ExtractIOTask::~ExtractIOTask() {}
 
+void ExtractIOTask::ZipListenerCallback(uint64_t bytes) {
+  progress_.bytes_transferred += bytes;
+  speedometer_.Update(progress_.bytes_transferred);
+  const double remaining_seconds = speedometer_.GetRemainingSeconds();
+
+  // Speedometer can produce infinite result which can't be serialized to JSON
+  // when sending the status via private API.
+  if (std::isfinite(remaining_seconds)) {
+    progress_.remaining_seconds = remaining_seconds;
+  }
+  progress_callback_.Run(progress_);
+}
+
 void ExtractIOTask::FinishedExtraction(bool success) {
   progress_.state = success ? State::kSuccess : State::kError;
   DCHECK_GT(extractCount_, 0);
@@ -102,6 +115,8 @@
     unzip::Unzip(
         unzip::LaunchUnzipper(), source_file, destination_directory,
         std::move(options),
+        base::BindRepeating(&ExtractIOTask::ZipListenerCallback,
+                            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&ExtractIOTask::ZipExtractCallback,
                        weak_ptr_factory_.GetWeakPtr(), destination_directory));
   } else {
@@ -174,6 +189,7 @@
     return;
   }
 
+  speedometer_.SetTotalBytes(progress_.total_bytes);
   ExtractAllSources();
 }
 
diff --git a/chrome/browser/ash/file_manager/extract_io_task.h b/chrome/browser/ash/file_manager/extract_io_task.h
index 3e41c83..db7f3b9 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.h
+++ b/chrome/browser/ash/file_manager/extract_io_task.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/ash/drive/file_system_util.h"
 #include "chrome/browser/ash/file_manager/io_task.h"
+#include "chrome/browser/ash/file_manager/speedometer.h"
 #include "components/services/unzip/public/cpp/unzip.h"
 #include "storage/browser/file_system/file_system_context.h"
 #include "storage/browser/file_system/file_system_url.h"
@@ -60,6 +61,8 @@
 
   void ZipExtractCallback(base::FilePath destination_directory, bool success);
 
+  void ZipListenerCallback(uint64_t bytes);
+
   void ExtractIntoNewDirectory(base::FilePath destination_directory,
                                base::FilePath source_file,
                                bool created_ok);
@@ -88,6 +91,9 @@
   Profile* profile_;
   const scoped_refptr<storage::FileSystemContext> file_system_context_;
 
+  // Speedometer used to calculate the remaining time to finish the operation.
+  Speedometer speedometer_;
+
   ProgressCallback progress_callback_;
   CompleteCallback complete_callback_;
 
diff --git a/chrome/browser/ash/file_manager/file_tasks.cc b/chrome/browser/ash/file_manager/file_tasks.cc
index 32bb4eb..c072164 100644
--- a/chrome/browser/ash/file_manager/file_tasks.cc
+++ b/chrome/browser/ash/file_manager/file_tasks.cc
@@ -522,45 +522,17 @@
 
   std::set<std::string> disabled_actions;
 
-  // kFilesArchivemount and kFilesArchivemount2 controls what subset of
-  // filename extensions listed in ui/file_manager/file_manager/manifest.json
-  // allows the "mount-archive" action.
-  //
-  // If kFilesArchivemount is disabled then only ".rar" and ".zip" are allowed.
-  // This corresponds to the status quo as of milestone M92.
-  //
-  // If kFilesArchivemount is enabled but kFilesArchivemount2 is disabled then
-  // more extensions are allowed, including ".7z" and uncompressed tar (".tar")
-  // but not compressed tar (".tar.bz", ".tar.bz2", ".tar.gz", ".tar.lzma",
-  // ".tar.xz", ".tbz", ".tbz2", ".tgz", ".tlzma", and ".txz") or compressed
-  // general files (".bz2", ".gz", ".lzma", and ".xz").
-  //
-  // If both are enabled then everything listed in manifest.json is allowed.
-  //
-  // TODO(crbug.com/1295892): some time after M98, remove these feature flags
-  // (scheduled to expire in M112) by hard-coding them to true, so that these
-  // if-blocks are never taken and can be deleted.
-  if (!base::FeatureList::IsEnabled(ash::features::kFilesArchivemount)) {
-    for (const auto& entry : entries) {
-      // Allow-list: .rar and .zip.
-      if (!entry.path.MatchesExtension(".rar") &&
-          !entry.path.MatchesExtension(".zip")) {
-        disabled_actions.emplace("mount-archive");
-        break;
-      }
-    }
-  } else if (!base::FeatureList::IsEnabled(
-                 ash::features::kFilesArchivemount2)) {
+  // kFilesArchivemount2 controls what subset of filename extensions listed in
+  // ui/file_manager/file_manager/manifest.json allows the "mount-archive"
+  // action. If kFilesArchivemount2 is enabled, everything listed in
+  // manifest.json is allowed.
+  if (!base::FeatureList::IsEnabled(ash::features::kFilesArchivemount2)) {
     for (const auto& entry : entries) {
       // Deny-list: various compressed formats.
-      if (entry.path.MatchesFinalExtension(".7z") ||
-          entry.path.MatchesFinalExtension(".bz") ||
+      if (entry.path.MatchesFinalExtension(".bz") ||
           entry.path.MatchesFinalExtension(".bz2") ||
-          entry.path.MatchesFinalExtension(".crx") ||
           entry.path.MatchesFinalExtension(".gz") ||
-          entry.path.MatchesFinalExtension(".iso") ||
           entry.path.MatchesFinalExtension(".lzma") ||
-          entry.path.MatchesFinalExtension(".tar") ||
           entry.path.MatchesFinalExtension(".taz") ||
           entry.path.MatchesFinalExtension(".tb2") ||
           entry.path.MatchesFinalExtension(".tbz") ||
diff --git a/chrome/browser/ash/input_method/ui/completion_suggestion_label_view.cc b/chrome/browser/ash/input_method/ui/completion_suggestion_label_view.cc
new file mode 100644
index 0000000..046adf96
--- /dev/null
+++ b/chrome/browser/ash/input_method/ui/completion_suggestion_label_view.cc
@@ -0,0 +1,65 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/input_method/ui/completion_suggestion_label_view.h"
+
+#include "chrome/browser/ash/input_method/ui/colors.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+
+namespace ui {
+namespace ime {
+namespace {
+constexpr char kSuggestionFontStyle[] = "Roboto";
+constexpr int kSuggestionFontSize = 13;
+}  // namespace
+
+CompletionSuggestionLabelView::CompletionSuggestionLabelView() {
+  SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  SetAutoColorReadabilityEnabled(false);
+  // StyledLabel eats event, probably because it has to handle links.
+  // Explicitly sets can_process_events_within_subtree to false for
+  // hover to work correctly.
+  SetCanProcessEventsWithinSubtree(false);
+}
+
+void CompletionSuggestionLabelView::SetPrefixAndPrediction(
+    const std::u16string& prefix,
+    const std::u16string& prediction) {
+  const gfx::FontList kSuggestionFont({kSuggestionFontStyle}, gfx::Font::NORMAL,
+                                      kSuggestionFontSize,
+                                      gfx::Font::Weight::NORMAL);
+  // SetText clears the existing style only if the text to set is different from
+  // the previous one.
+  SetText(u"");
+  SetText(prefix + prediction);
+
+  // Create style range for prefix if it's not empty.
+  if (!prefix.empty()) {
+    views::StyledLabel::RangeStyleInfo prefix_style;
+    prefix_style.custom_font = kSuggestionFont;
+    prefix_style.override_color =
+        ResolveSemanticColor(cros_styles::ColorName::kTextColorPrimary);
+    AddStyleRange(gfx::Range(0, prefix.length()), prefix_style);
+  }
+
+  // Create style range for the prediction.
+  views::StyledLabel::RangeStyleInfo prediction_style;
+  prediction_style.custom_font = kSuggestionFont;
+  prediction_style.override_color =
+      ResolveSemanticColor(cros_styles::ColorName::kTextColorSecondary);
+  AddStyleRange(
+      gfx::Range(prefix.length(), prefix.length() + prediction.length()),
+      prediction_style);
+
+  // TODO(crbug/1099146): Add tests to check view's height and width with
+  // a non-empty prefix.
+  // Maximum width for suggestion.
+  SizeToFit(448);
+}
+
+BEGIN_METADATA(CompletionSuggestionLabelView, views::StyledLabel)
+END_METADATA
+
+}  // namespace ime
+}  // namespace ui
diff --git a/chrome/browser/ash/input_method/ui/completion_suggestion_label_view.h b/chrome/browser/ash/input_method/ui/completion_suggestion_label_view.h
new file mode 100644
index 0000000..699cc361
--- /dev/null
+++ b/chrome/browser/ash/input_method/ui/completion_suggestion_label_view.h
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_INPUT_METHOD_UI_COMPLETION_SUGGESTION_LABEL_VIEW_H_
+#define CHROME_BROWSER_ASH_INPUT_METHOD_UI_COMPLETION_SUGGESTION_LABEL_VIEW_H_
+
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/chromeos/ui_chromeos_export.h"
+#include "ui/views/controls/styled_label.h"
+
+namespace ui {
+namespace ime {
+
+// A CompletionSuggestionLabelView renders the text of a completion suggestion.
+// A completion suggestion label has two parts:
+// - Prefix: The prefix in the suggestion that matches what the user has typed
+//   so far. This may be empty for next-word predictions.
+// - Prediction: The remaining part of the suggestion that is predicted by the
+//   input method.
+//
+// Examples:
+// - User types "how a". The input method suggests "how are you".
+//   The prefix is "how a" and the prediction is "re you".
+// - User types a space to begin a new word. The input method suggests "how".
+//   The prefix is "" and the prediction is "how".
+//
+// CompletionSuggestionLabelView renders the prefix differently from the
+// prediction to distinguish the two.
+class UI_CHROMEOS_EXPORT CompletionSuggestionLabelView
+    : public views::StyledLabel {
+ public:
+  METADATA_HEADER(CompletionSuggestionLabelView);
+
+  CompletionSuggestionLabelView();
+
+  // Set the prefix and prediction parts of the label.
+  void SetPrefixAndPrediction(const std::u16string& prefix,
+                              const std::u16string& prediction);
+};
+
+BEGIN_VIEW_BUILDER(UI_CHROMEOS_EXPORT,
+                   CompletionSuggestionLabelView,
+                   views::StyledLabel)
+END_VIEW_BUILDER
+
+}  // namespace ime
+}  // namespace ui
+
+DEFINE_VIEW_BUILDER(UI_CHROMEOS_EXPORT, ui::ime::CompletionSuggestionLabelView)
+
+#endif  // CHROME_BROWSER_ASH_INPUT_METHOD_UI_COMPLETION_SUGGESTION_LABEL_VIEW_H_
diff --git a/chrome/browser/ash/input_method/ui/completion_suggestion_label_view_unittest.cc b/chrome/browser/ash/input_method/ui/completion_suggestion_label_view_unittest.cc
new file mode 100644
index 0000000..664a6c34
--- /dev/null
+++ b/chrome/browser/ash/input_method/ui/completion_suggestion_label_view_unittest.cc
@@ -0,0 +1,95 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/input_method/ui/completion_suggestion_label_view.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "chrome/browser/ash/input_method/ui/colors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/test/views_test_base.h"
+
+namespace ui {
+namespace ime {
+namespace {
+
+// Returns the child of `view` at `index` as a views::Label.
+views::Label* LabelAt(const views::View& view, size_t index) {
+  views::View* const child = view.children()[index];
+  EXPECT_EQ(child->GetClassName(), views::Label::kViewClassName);
+  return static_cast<views::Label*>(child);
+}
+
+class CompletionSuggestionLabelViewTest : public views::ViewsTestBase {
+ public:
+  CompletionSuggestionLabelViewTest() = default;
+};
+
+TEST_F(CompletionSuggestionLabelViewTest, EmptyPrefixHasCorrectText) {
+  CompletionSuggestionLabelView label;
+
+  label.SetPrefixAndPrediction(u"", u"good");
+
+  EXPECT_EQ(label.GetText(), u"good");
+}
+
+TEST_F(CompletionSuggestionLabelViewTest, NonEmptyPrefixHasCorrectText) {
+  CompletionSuggestionLabelView label;
+
+  label.SetPrefixAndPrediction(u"how a", u"re you");
+
+  EXPECT_EQ(label.GetText(), u"how are you");
+}
+
+TEST_F(CompletionSuggestionLabelViewTest,
+       ChildHasCorrectTextWhenPrefixIsEmpty) {
+  CompletionSuggestionLabelView label;
+
+  label.SetPrefixAndPrediction(u"", u"good");
+
+  ASSERT_EQ(label.children().size(), 1u);
+  EXPECT_EQ(LabelAt(label, 0)->GetText(), u"good");
+}
+
+TEST_F(CompletionSuggestionLabelViewTest,
+       ChildUsesSecondaryColorWhenPrefixIsEmpty) {
+  CompletionSuggestionLabelView label;
+
+  label.SetPrefixAndPrediction(u"", u"good");
+
+  ASSERT_EQ(label.children().size(), 1u);
+  EXPECT_EQ(LabelAt(label, 0)->GetEnabledColor(),
+            ResolveSemanticColor(cros_styles::ColorName::kTextColorSecondary));
+}
+
+TEST_F(CompletionSuggestionLabelViewTest,
+       ChildrenHaveCorrectTextWhenPrefixIsNotEmpty) {
+  CompletionSuggestionLabelView label;
+
+  label.SetPrefixAndPrediction(u"how a", u"re you");
+
+  ASSERT_EQ(label.children().size(), 2u);
+  EXPECT_EQ(LabelAt(label, 0)->GetText(), u"how a");
+  EXPECT_EQ(LabelAt(label, 1)->GetText(), u"re you");
+}
+
+TEST_F(CompletionSuggestionLabelViewTest,
+       ChildrenUsePrimaryAndSecondaryColorsWhenPrefixIsNotEmpty) {
+  CompletionSuggestionLabelView label;
+
+  label.SetPrefixAndPrediction(u"how a", u"re you");
+
+  ASSERT_EQ(label.children().size(), 2u);
+  EXPECT_EQ(LabelAt(label, 0)->GetEnabledColor(),
+            ResolveSemanticColor(cros_styles::ColorName::kTextColorPrimary));
+  EXPECT_EQ(LabelAt(label, 1)->GetEnabledColor(),
+            ResolveSemanticColor(cros_styles::ColorName::kTextColorSecondary));
+}
+
+}  // namespace
+}  // namespace ime
+}  // namespace ui
diff --git a/chrome/browser/ash/input_method/ui/suggestion_view.cc b/chrome/browser/ash/input_method/ui/suggestion_view.cc
index 5d6e42b..77d3229 100644
--- a/chrome/browser/ash/input_method/ui/suggestion_view.cc
+++ b/chrome/browser/ash/input_method/ui/suggestion_view.cc
@@ -7,6 +7,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ash/input_method/ui/colors.h"
+#include "chrome/browser/ash/input_method/ui/completion_suggestion_label_view.h"
 #include "chrome/browser/ash/input_method/ui/suggestion_details.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -49,21 +50,6 @@
   return index_label;
 }
 
-// Creates the suggestion label, and returns it (never returns nullptr).
-// The label text is not set in this function.
-std::unique_ptr<views::StyledLabel> CreateSuggestionLabel() {
-  auto suggestion_label = std::make_unique<views::StyledLabel>();
-  suggestion_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  suggestion_label->SetBorder(
-      views::CreateEmptyBorder(gfx::Insets::VH(kPadding / 2, 0)));
-  suggestion_label->SetAutoColorReadabilityEnabled(false);
-  // StyledLabel eats event, probably because it has to handle links.
-  // Explicitly sets can_process_events_within_subtree to false for
-  // SuggestionView's hover to work correctly.
-  suggestion_label->SetCanProcessEventsWithinSubtree(false);
-  return suggestion_label;
-}
-
 std::unique_ptr<views::ImageView> CreateDownIcon() {
   auto icon = std::make_unique<views::ImageView>();
   icon->SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
@@ -115,7 +101,10 @@
     : views::Button(std::move(callback)) {
   index_label_ = AddChildView(CreateIndexLabel());
   index_label_->SetVisible(false);
-  suggestion_label_ = AddChildView(CreateSuggestionLabel());
+  suggestion_label_ =
+      AddChildView(std::make_unique<CompletionSuggestionLabelView>());
+  suggestion_label_->SetBorder(
+      views::CreateEmptyBorder(gfx::Insets::VH(kPadding / 2, 0)));
 
   annotation_container_ = AddChildView(CreateAnnotationContainer());
   down_and_enter_annotation_label_ =
@@ -187,39 +176,14 @@
   index_label_->SetText(index);
   index_label_->SetVisible(true);
   index_width_ = index_label_->GetPreferredSize().width();
-  suggestion_label_->SetText(text);
+  suggestion_label_->SetPrefixAndPrediction(u"", text);
   suggestion_width_ = suggestion_label_->GetPreferredSize().width();
 }
 
 void SuggestionView::SetSuggestionText(const std::u16string& text,
                                        const size_t confirmed_length) {
-  // SetText clears the existing style only if the text to set is different from
-  // the previous one.
-  suggestion_label_->SetText(base::EmptyString16());
-  suggestion_label_->SetText(text);
-  gfx::FontList kSuggestionFont({kFontStyle}, gfx::Font::NORMAL,
-                                kSuggestionFontSize, gfx::Font::Weight::NORMAL);
-
-  if (confirmed_length != 0) {
-    views::StyledLabel::RangeStyleInfo confirmed_style;
-    confirmed_style.custom_font = kSuggestionFont;
-    confirmed_style.override_color =
-        ResolveSemanticColor(cros_styles::ColorName::kTextColorPrimary);
-    suggestion_label_->AddStyleRange(gfx::Range(0, confirmed_length),
-                                     confirmed_style);
-  }
-
-  views::StyledLabel::RangeStyleInfo suggestion_style;
-  suggestion_style.custom_font = kSuggestionFont;
-  suggestion_style.override_color =
-      ResolveSemanticColor(cros_styles::ColorName::kTextColorSecondary);
-  suggestion_label_->AddStyleRange(gfx::Range(confirmed_length, text.length()),
-                                   suggestion_style);
-
-  // TODO(crbug/1099146): Add tests to check view's height and width with
-  // confirmed length.
-  // Maximum width for suggestion.
-  suggestion_label_->SizeToFit(448);
+  suggestion_label_->SetPrefixAndPrediction(text.substr(0, confirmed_length),
+                                            text.substr(confirmed_length));
 }
 
 void SuggestionView::SetHighlighted(bool highlighted) {
diff --git a/chrome/browser/ash/input_method/ui/suggestion_view.h b/chrome/browser/ash/input_method/ui/suggestion_view.h
index 64f26d1..42c8cbd 100644
--- a/chrome/browser/ash/input_method/ui/suggestion_view.h
+++ b/chrome/browser/ash/input_method/ui/suggestion_view.h
@@ -23,10 +23,10 @@
 namespace ime {
 
 struct SuggestionDetails;
+class CompletionSuggestionLabelView;
 
 // Font-related constants
 constexpr char kFontStyle[] = "Roboto";
-constexpr int kSuggestionFontSize = 13;
 constexpr int kAnnotationFontSize = 10;
 constexpr int kIndexFontSize = 10;
 
@@ -79,8 +79,8 @@
                          const size_t confirmed_length);
 
   views::Label* index_label_ = nullptr;
-  // The suggestion label renders suggestions.
-  views::StyledLabel* suggestion_label_ = nullptr;
+  // The suggestion label renders the suggestion text.
+  CompletionSuggestionLabelView* suggestion_label_ = nullptr;
   // The annotation view renders annotations.
   views::View* annotation_container_ = nullptr;
   views::View* down_and_enter_annotation_label_ = nullptr;
diff --git a/chrome/browser/ash/usb/cros_usb_detector_unittest.cc b/chrome/browser/ash/usb/cros_usb_detector_unittest.cc
index 7728cc8..bbf637c 100644
--- a/chrome/browser/ash/usb/cros_usb_detector_unittest.cc
+++ b/chrome/browser/ash/usb/cros_usb_detector_unittest.cc
@@ -380,27 +380,25 @@
   device_manager_.RemoveDevice(device);
   base::RunLoop().RunUntilIdle();
 
-  // Should have 2 buttions when ARCVM is enabled and user enables ARC but the
+  // Should have 3 buttions when ARCVM is enabled and user enables ARC but the
   // feature is disabled.
-  // Update this test when the kUsbDeviceDefaultAttachToArcVm is enabled
-  // by default or removed.
   ASSERT_TRUE(arc::SetArcPlayStoreEnabledForProfile(profile(), true));
   device_manager_.AddDevice(device);
   base::RunLoop().RunUntilIdle();
   notification = display_service_->GetNotification(notification_id);
   ASSERT_TRUE(notification);
-  EXPECT_EQ(notification->buttons().size(), 2u);
+  EXPECT_EQ(notification->buttons().size(), 3u);
   device_manager_.RemoveDevice(device);
   base::RunLoop().RunUntilIdle();
 
-  // Now should have 3 buttons when Bruschetta is enabled.
+  // Now should have 4 buttons when Bruschetta is enabled.
   bruschetta::FakeBruschettaFeatures bruschetta_features;
   bruschetta_features.set_enabled(true);
   device_manager_.AddDevice(device);
   base::RunLoop().RunUntilIdle();
   notification = display_service_->GetNotification(notification_id);
   ASSERT_TRUE(notification);
-  EXPECT_EQ(notification->buttons().size(), 3u);
+  EXPECT_EQ(notification->buttons().size(), 4u);
   device_manager_.RemoveDevice(device);
   base::RunLoop().RunUntilIdle();
 }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index c434d2e..f21347f 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -990,6 +990,8 @@
     "../ash/input_method/ui/candidate_window_view.h",
     "../ash/input_method/ui/colors.cc",
     "../ash/input_method/ui/colors.h",
+    "../ash/input_method/ui/completion_suggestion_label_view.cc",
+    "../ash/input_method/ui/completion_suggestion_label_view.h",
     "../ash/input_method/ui/grammar_suggestion_window.cc",
     "../ash/input_method/ui/grammar_suggestion_window.h",
     "../ash/input_method/ui/infolist_window.cc",
@@ -3597,6 +3599,7 @@
     "../ash/input_method/ui/assistive_accessibility_view_unittest.cc",
     "../ash/input_method/ui/candidate_view_unittest.cc",
     "../ash/input_method/ui/candidate_window_view_unittest.cc",
+    "../ash/input_method/ui/completion_suggestion_label_view_unittest.cc",
     "../ash/input_method/ui/grammar_suggestion_window_unittest.cc",
     "../ash/input_method/ui/input_method_menu_item_unittest.cc",
     "../ash/input_method/ui/input_method_menu_manager_unittest.cc",
diff --git a/chrome/browser/chromeos/OWNERS b/chrome/browser/chromeos/OWNERS
index 4230c5f7..f5eb1de 100644
--- a/chrome/browser/chromeos/OWNERS
+++ b/chrome/browser/chromeos/OWNERS
@@ -1,13 +1,7 @@
+# This directory is for shared code between Ash and Lacros. Most of the
+# ChromeOS code lives in chrome/browser/ash/, hence, share OWNERS.
+file://chrome/browser/ash/OWNERS
+
 # This is for the common case of adding or renaming files. If you're doing
 # structural changes, use usual OWNERS rules.
 per-file BUILD.gn=*
-
-achuith@chromium.org
-afakhry@chromium.org
-alemate@chromium.org
-hidehiko@chromium.org
-jamescook@chromium.org
-khorimoto@chromium.org
-oshima@chromium.org
-xiyuan@chromium.org
-per-file *active_directory*=file://chrome/browser/ash/authpolicy/OWNERS
diff --git a/chrome/browser/chromeos/app_mode/app_session.h b/chrome/browser/chromeos/app_mode/app_session.h
index e49890cd..155e571 100644
--- a/chrome/browser/chromeos/app_mode/app_session.h
+++ b/chrome/browser/chromeos/app_mode/app_session.h
@@ -54,7 +54,8 @@
   kStopped = 3,
   kPluginCrashed = 4,
   kPluginHung = 5,
-  kWebWithLacrosStarted = 6,
+  // No longer used, use kWebStarted for lacros platform.
+  // kWebWithLacrosStarted = 6, 
   kRestored = 7,
   kMaxValue = kRestored,
 };
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 71aeef07..8f1e7a572 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -256,7 +256,7 @@
     "name": "arc-usb-device-attach-to-vm-experiment",
     "owners": [ "lgcheng" ],
     // Used on ChromeOS to temperorily attach unclaimed USB device to ARCVM
-    "expiry_milestone": 104
+    "expiry_milestone": 106
   },
   {
     "name": "arc-use-high-memory-dalvik-profile",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index b4a6e18..8274a68 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4923,10 +4923,6 @@
     "Enable feature which adds ability for user to grab and resize divider of "
     "Docked Magnifier.";
 
-const char kFilesArchivemountName[] = "Archivemount in Files App (1st Tier)";
-const char kFilesArchivemountDescription[] =
-    "Enable mounting various archive formats in File Manager.";
-
 const char kFilesArchivemount2Name[] = "Archivemount in Files App (2nd Tier)";
 const char kFilesArchivemount2Description[] =
     "Enable mounting additional archive formats in File Manager. This has no "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 481e92f..95f75a8 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2805,9 +2805,6 @@
 extern const char kDockedMagnifierResizingName[];
 extern const char kDockedMagnifierResizingDescription[];
 
-extern const char kFilesArchivemountName[];
-extern const char kFilesArchivemountDescription[];
-
 extern const char kFilesArchivemount2Name[];
 extern const char kFilesArchivemount2Description[];
 
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
index c8230bc..c846bba6 100644
--- a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
@@ -23,12 +23,17 @@
 
 MediaPageLoadMetricsObserver::~MediaPageLoadMetricsObserver() = default;
 
-// TODO(https://crbug.com/1317494): Audit and use appropriate policy.
+const char* MediaPageLoadMetricsObserver::GetObserverName() const {
+  static const char kName[] = "MediaPageLoadMetricsObserver";
+  return kName;
+}
+
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 MediaPageLoadMetricsObserver::OnFencedFramesStart(
     content::NavigationHandle* navigation_handle,
     const GURL& currently_committed_url) {
-  return STOP_OBSERVING;
+  // This class needs forwarding for the events MediaStartedPlaying.
+  return FORWARD_OBSERVING;
 }
 
 void MediaPageLoadMetricsObserver::OnResourceDataUseObserved(
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
index 2f74d79..9229399c 100644
--- a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
@@ -24,6 +24,7 @@
   ~MediaPageLoadMetricsObserver() override;
 
   // page_load_metrics::PageLoadMetricsObserver:
+  const char* GetObserverName() const override;
   ObservePolicy OnFencedFramesStart(
       content::NavigationHandle* navigation_handle,
       const GURL& currently_committed_url) override;
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc
index 81809d7..f13ac9e 100644
--- a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/page_load_metrics/common/page_load_metrics.mojom.h"
 #include "components/page_load_metrics/common/page_load_timing.h"
 #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
+#include "content/public/test/navigation_simulator.h"
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
 #include "url/gurl.h"
 
@@ -50,12 +51,11 @@
     cache_bytes_ = 0;
   }
 
-  void SimulatePageLoad(bool simulate_play_media,
-                        bool simulate_app_background) {
-    NavigateAndCommit(GURL(kDefaultTestUrl));
-
+  void SimulateEvents(content::RenderFrameHost* rfh,
+                      bool simulate_play_media,
+                      bool simulate_app_background) {
     if (simulate_play_media)
-      tester()->SimulateMediaPlayed();
+      tester()->SimulateMediaPlayed(rfh);
 
     tester()->SimulateTimingUpdate(timing_);
 
@@ -93,10 +93,31 @@
   page_load_metrics::mojom::PageLoadTiming timing_;
 };
 
+TEST_F(MediaPageLoadMetricsObserverTest, MediaNotPlayed) {
+  ResetTest();
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  content::RenderFrameHost* mainframe = web_contents()->GetMainFrame();
+
+  SimulateEvents(mainframe, false /* simulate_play_media */,
+                 false /* simulate_app_background */);
+
+  tester()->histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network", 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache", 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total", 0);
+}
+
 TEST_F(MediaPageLoadMetricsObserverTest, MediaPlayed) {
   ResetTest();
-  SimulatePageLoad(true /* simulate_play_media */,
-                   false /* simulate_app_background */);
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  content::RenderFrameHost* mainframe = web_contents()->GetMainFrame();
+
+  SimulateEvents(mainframe, true /* simulate_play_media */,
+                 false /* simulate_app_background */);
 
   tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network",
@@ -111,8 +132,12 @@
 
 TEST_F(MediaPageLoadMetricsObserverTest, MediaPlayedAppBackground) {
   ResetTest();
-  SimulatePageLoad(true /* simulate_play_media */,
-                   true /* simulate_app_background */);
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  content::RenderFrameHost* mainframe = web_contents()->GetMainFrame();
+
+  SimulateEvents(mainframe, true /* simulate_play_media */,
+                 true /* simulate_app_background */);
 
   tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network",
@@ -125,15 +150,54 @@
       static_cast<int>((network_bytes_ + cache_bytes_) / 1024), 1);
 }
 
-TEST_F(MediaPageLoadMetricsObserverTest, MediaNotPlayed) {
+TEST_F(MediaPageLoadMetricsObserverTest, MediaPlayedInSubframe) {
   ResetTest();
-  SimulatePageLoad(false /* simulate_play_media */,
-                   false /* simulate_app_background */);
 
-  tester()->histogram_tester().ExpectTotalCount(
-      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network", 0);
-  tester()->histogram_tester().ExpectTotalCount(
-      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache", 0);
-  tester()->histogram_tester().ExpectTotalCount(
-      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total", 0);
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  content::RenderFrameHost* mainframe = web_contents()->GetMainFrame();
+  content::RenderFrameHost* subframe =
+      content::RenderFrameHostTester::For(mainframe)->AppendChild("subframe");
+  std::unique_ptr<content::NavigationSimulator> simulator =
+      content::NavigationSimulator::CreateRendererInitiated(
+          GURL(kDefaultTestUrl), subframe);
+  simulator->Commit();
+
+  SimulateEvents(subframe, true /* simulate_play_media */,
+                 false /* simulate_app_background */);
+
+  tester()->histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network",
+      static_cast<int>(network_bytes_ / 1024), 1);
+  tester()->histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache",
+      static_cast<int>(cache_bytes_ / 1024), 1);
+  tester()->histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total",
+      static_cast<int>((network_bytes_ + cache_bytes_) / 1024), 1);
+}
+
+TEST_F(MediaPageLoadMetricsObserverTest, MediaPlayedInFencedFrame) {
+  ResetTest();
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  content::RenderFrameHost* mainframe = web_contents()->GetMainFrame();
+  content::RenderFrameHost* subframe =
+      content::RenderFrameHostTester::For(mainframe)->AppendFencedFrame();
+  std::unique_ptr<content::NavigationSimulator> simulator =
+      content::NavigationSimulator::CreateRendererInitiated(
+          GURL(kDefaultTestUrl), subframe);
+  simulator->Commit();
+
+  SimulateEvents(subframe, true /* simulate_play_media */,
+                 false /* simulate_app_background */);
+
+  tester()->histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network",
+      static_cast<int>(network_bytes_ / 1024), 1);
+  tester()->histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache",
+      static_cast<int>(cache_bytes_ / 1024), 1);
+  tester()->histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total",
+      static_cast<int>((network_bytes_ + cache_bytes_) / 1024), 1);
 }
diff --git a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc
index 436a4da9..a8df5cd 100644
--- a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc
@@ -39,11 +39,14 @@
   return IsTabRestore(navigation_handle) ? CONTINUE_OBSERVING : STOP_OBSERVING;
 }
 
-// TODO(https://crbug.com/1317494): Audit and use appropriate policy.
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 TabRestorePageLoadMetricsObserver::OnFencedFramesStart(
     content::NavigationHandle* navigation_handle,
     const GURL& currently_committed_url) {
+  // This class is interested only in the primary page's performance to
+  // report at OnComplete or FlushMetricsOnAppEnterBackground. Events for
+  // OnResourceDataUseObserved are forwarded at PageLoadTracker and observer
+  // doesn't need to forward it.
   return STOP_OBSERVING;
 }
 
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
index 8e93f84..2e9acab 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
@@ -156,7 +156,7 @@
 <template is = "dom-if" if="[[showClearRecents]]">
   <button id="clear-recents" on-click="onClearRecentsClick">
     <div id="clear-recents-hover">
-      Clear recently used [[category]]
+      Clear recently used [[category]]s
     </div>
   </button>
 </template>
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 4b79461..8115fbc 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -444,6 +444,7 @@
     "chromeos/crostini_page/crostini_arc_adb.js",
     "chromeos/crostini_page/crostini_arc_adb_confirmation_dialog.js",
     "chromeos/crostini_page/crostini_confirmation_dialog.js",
+    "chromeos/crostini_page/crostini_container_select.js",
     "chromeos/crostini_page/crostini_disk_resize_confirmation_dialog.js",
     "chromeos/crostini_page/crostini_disk_resize_dialog.js",
     "chromeos/crostini_page/crostini_export_import.js",
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn
index ff38163..2ac2c91 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn
@@ -15,6 +15,7 @@
     ":crostini_arc_adb_confirmation_dialog",
     ":crostini_browser_proxy",
     ":crostini_confirmation_dialog",
+    ":crostini_container_select",
     ":crostini_disk_resize_confirmation_dialog",
     ":crostini_disk_resize_dialog",
     ":crostini_export_import",
@@ -67,9 +68,16 @@
 
 js_library("crostini_confirmation_dialog") {
   deps = [
-    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
-    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
-    "//ui/webui/resources/js:load_time_data.m",
+    ":crostini_browser_proxy",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
+js_library("crostini_container_select") {
+  deps = [
+    ":crostini_browser_proxy",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:cr.m",
   ]
 }
 
@@ -146,6 +154,7 @@
 js_library("crostini_port_forwarding") {
   deps = [
     ":crostini_browser_proxy",
+    ":crostini_container_select",
     ":crostini_port_forwarding_add_port_dialog",
     "..:metrics_recorder",
     "..:prefs_behavior",
@@ -166,6 +175,7 @@
 js_library("crostini_port_forwarding_add_port_dialog") {
   deps = [
     ":crostini_browser_proxy",
+    ":crostini_container_select",
     "..:metrics_recorder",
     "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
     "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
@@ -251,6 +261,7 @@
     "crostini_arc_adb_confirmation_dialog.js",
     "crostini_arc_adb.js",
     "crostini_confirmation_dialog.js",
+    "crostini_container_select.js",
     "crostini_disk_resize_confirmation_dialog.js",
     "crostini_disk_resize_dialog.js",
     "crostini_export_import.js",
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
index 5528a2f1..475ec9e 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
@@ -19,6 +19,11 @@
  */
 export let ContainerId;
 
+/** @type {!ContainerId} */ export const DEFAULT_CONTAINER_ID = {
+  vm_name: DEFAULT_CROSTINI_VM,
+  container_name: DEFAULT_CROSTINI_CONTAINER,
+};
+
 /**
  * These values should remain consistent with their C++ counterpart
  * (chrome/browser/ash/crostini/crostini_port_forwarder.h).
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_container_select.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_container_select.html
new file mode 100644
index 0000000..dda4253
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_container_select.html
@@ -0,0 +1,11 @@
+<style include="settings-shared md-select"></style>
+<select id="selectContainer"
+    class="md-select"
+    value="containerLabel_(containerId)"
+    on-change="onSelectContainer_">
+    <template is="dom-repeat" items="[[containers]]">
+      <option value="[[item.id]]">
+        [[containerLabel_(item.id)]]
+      </option>
+    </template>
+</select>
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_container_select.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_container_select.js
new file mode 100644
index 0000000..aa36663
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_container_select.js
@@ -0,0 +1,93 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview 'settings-crostini-container-select' is a component enabling a
+ * user to select a target container from a list stored in prefs.
+ */
+import '//resources/cr_elements/md_select_css.m.js';
+import '../../settings_shared_css.js';
+
+import {html, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {ContainerId, ContainerInfo, DEFAULT_CONTAINER_ID, DEFAULT_CROSTINI_VM} from './crostini_browser_proxy.js';
+
+/**
+ * @param {!ContainerId} first
+ * @param {!ContainerId} second
+ * @return boolean
+ */
+export function equalContainerId(first, second) {
+  return first.vm_name === second.vm_name &&
+      first.container_name === second.container_name;
+}
+
+/**
+ * @param {!ContainerId} id
+ * @return string
+ */
+export function containerLabel(id) {
+  if (id.vm_name === DEFAULT_CROSTINI_VM) {
+    return id.container_name;
+  }
+  return id.vm_name + ':' + id.container_name;
+}
+
+
+/** @polymer */
+class ContainerSelectElement extends PolymerElement {
+  static get is() {
+    return 'settings-crostini-container-select';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      /**
+       * @type {!ContainerId}
+       */
+      selectedContainerId: {
+        type: Object,
+        notify: true,
+      },
+
+      /**
+       * List of containers that are already stored in the settings.
+       * @type {!Array<!ContainerInfo>}
+       */
+      containers: {
+        type: Array,
+        value() {
+          return [];
+        },
+      },
+    };
+  }
+
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onSelectContainer_(e) {
+    const index = e.target.selectedIndex;
+    if (index >= 0 && index < this.containers.length) {
+      this.selectedContainerId = this.containers[index].id;
+    }
+  }
+
+  /**
+   * @param {!ContainerId} id
+   * @return string
+   * @private
+   */
+  containerLabel_(id) {
+    return containerLabel(id);
+  }
+}
+
+customElements.define(ContainerSelectElement.is, ContainerSelectElement);
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.html
index 6d398e04..2443208 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.html
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.html
@@ -43,6 +43,18 @@
     margin-inline-end: calc(var(--cr-toggle-margin-inline-start) +
                             var(--cr-toggle-width));
   }
+
+  #portForwardingListContainerId {
+    color: var(--cros-text-color-disabled);
+    margin-inline-start: 8px;
+  }
+
+  #portForwardingListCard {
+    background-color: var(--cr-card-background-color);
+    border-radius: var(--cr-card-border-radius);
+    box-shadow: var(--cr-card-shadow);
+  }
+
 </style>
 <div class="settings-box first"
     id="portForwardingDescription">
@@ -58,6 +70,14 @@
       aria-describedby="i18n{portForwardingDescription}">
     $i18n{crostiniPortForwardingAddPortButton}
   </cr-button>
+  <template is="dom-if" if="[[allPorts_.length]]" restamp>
+    <cr-icon-button id="showRemoveAllPortsMenu"
+      class="icon-more-vert"
+      title="$i18n{moreActions}"
+      on-click="onShowRemoveAllPortsMenuClick_"
+      aria-label="$i18n{moreActions}">
+    </cr-icon-button>
+  </template>
 </div>
 <template is="dom-if" if="[[!allPorts_.length]]" restamp>
   <div id="no-ports-text"
@@ -66,75 +86,85 @@
   </div>
 </template>
 <template is="dom-if" if="[[allPorts_.length]]" restamp>
-  <div class="list-frame vertical-list">
-    <div class="list-item">
-      <div id="portForwardingListPortNumber"
-          class="start column-title"
-          aria-hidden="true">
-        $i18n{crostiniPortForwardingListPortNumber}
+  <template is="dom-repeat" items="[[allContainers_]]"
+      as="containerInfo" index-as="cidx" mutable-data>
+    <template is="dom-if" 
+        if="[[hasContainerPorts_(allPorts_, containerInfo.id)]]" restamp>
+      <div id="portForwardingListContainerId" 
+          hidden="[[!showContainerId_(allPorts_, containerInfo.id)]]"
+          class="settings-box first">
+          [[containerLabel_(containerInfo.id)]]
       </div>
-      <div id="portForwardingListPortLabel"
-          class="column-title label-column"
-          aria-hidden="true">
-        $i18n{crostiniPortForwardingListLabel}
-      </div>
-      <cr-icon-button id="showRemoveAllPortsMenu"
-          class="icon-more-vert"
-          title="$i18n{moreActions}"
-          on-click="onShowRemoveAllPortsMenuClick_"
-          aria-label="$i18n{moreActions}">
-      </cr-icon-button>
-    </div>
-    <template is="dom-repeat" items="[[allPorts_]]">
-      <div class="list-item">
-        <div id="crostiniPort[[index]]" class="start" aria-hidden="true">
-          [[item.port_number]]
-          <span id="protocolText">
-            <template is="dom-if" if="[[!item.protocol_type]]" restamp>
-              $i18n{crostiniPortForwardingTCP}
-            </template>
-            <template is="dom-if" if="[[item.protocol_type]]" restamp>
-              $i18n{crostiniPortForwardingUDP}
-            </template>
-          </span>
+      <div class="list-frame vertical-list" id="portForwardingListCard">
+        <div class="list-item">
+          <div id="portForwardingListPortNumber"
+              class="start column-title"
+              aria-hidden="true">
+            $i18n{crostiniPortForwardingListPortNumber}
+          </div>
+          <div id="portForwardingListPortLabel"
+              class="column-title label-column"
+              aria-hidden="true">
+            $i18n{crostiniPortForwardingListLabel}
+          </div>
         </div>
-        <div id="crostiniPortLabel[[index]]"
-            class="label-column"
-            aria-hidden="true">
-          [[item.label]]
-        </div>
-        <cr-toggle
-            id="toggleActivationButton[[index]]"
-            checked="[[item.is_active]]"
-            data-port-number$="[[item.port_number]]"
-            data-protocol-type$="[[item.protocol_type]]"
-            data-container-id$="[[item.container_id]]"
-            on-change="onPortActivationChange_"
-            aria-label="$i18n{crostiniPortForwardingToggleAriaLabel}"
-            aria-describedby$=
-                    "crostiniPort[[index]] crostiniPortLabel[[index]]"
-            disabled="[[!crostiniRunning_]]">
-        </cr-toggle>
-        <cr-icon-button id="showRemoveSinglePortMenu[[index]]"
-            class="icon-more-vert"
-            title="$i18n{moreActions}"
-            on-click="onShowRemoveSinglePortMenuClick_"
-            data-port-number$="[[item.port_number]]"
-            data-protocol-type$="[[item.protocol_type]]"
-            data-container-id$="[[item.container_id]]"
-            aria-label=
+        <template is="dom-repeat" items="[[allPorts_]]"
+            filter="[[byContainerId_(containerInfo.id)]]">
+          <div class="list-item">
+            <div id="crostiniPort[[cidx]]-[[index]]"
+                class="start"
+                aria-hidden="true">
+              [[item.port_number]]
+              <span id="protocolText">
+                <template is="dom-if" if="[[!item.protocol_type]]" restamp>
+                  $i18n{crostiniPortForwardingTCP}
+                </template>
+                <template is="dom-if" if="[[item.protocol_type]]" restamp>
+                  $i18n{crostiniPortForwardingUDP}
+                </template>
+              </span>
+            </div>
+            <div id="crostiniPortLabel[[cidx]]-[[index]]"
+                class="label-column"
+                aria-hidden="true">
+              [[item.label]]
+            </div>
+            <cr-toggle
+                id="toggleActivationButton[[cidx]]-[[index]]"
+                checked="[[item.is_active]]"
+                data-port-number$="[[item.port_number]]"
+                data-protocol-type$="[[item.protocol_type]]"
+                data-container-id="[[item.container_id]]"
+                on-change="onPortActivationChange_"
+                aria-label="$i18n{crostiniPortForwardingToggleAriaLabel}"
+                aria-describedby$="crostiniPort[[cidx]]-[[index]] 
+                    crostiniPortLabel[[cidx]]-[[index]]"
+                disabled="[[!crostiniRunning_]]">
+            </cr-toggle>
+            <cr-icon-button 
+                id="showRemoveSinglePortMenu[[cidx]]-[[index]]"
+                class="icon-more-vert"
+                title="$i18n{moreActions}"
+                on-click="onShowRemoveSinglePortMenuClick_"
+                data-port-number$="[[item.port_number]]"
+                data-protocol-type$="[[item.protocol_type]]"
+                data-container-id="[[item.container_id]]"
+                aria-label=
                     "$i18n{crostiniPortForwardingShowMoreActionsAriaLabel}"
-            aria-describedby$=
-                    "crostiniPort[[index]] crostiniPortLabel[[index]]">
-        </cr-icon-button>
+                aria-describedby$="crostiniPort[[cidx]]-[[index]] 
+                    crostiniPortLabel[[cidx]]-[[index]]">
+            </cr-icon-button>
+          </div>
+        </template>
       </div>
     </template>
-  </div>
+  </template>
 </template>
 <template is="dom-if" if="[[showAddPortDialog_]]" restamp>
   <settings-crostini-add-port-dialog
       on-close="onAddPortDialogClose_"
-      all-ports="[[allPorts_]]">
+      all-ports="[[allPorts_]]"
+      all-containers="[[allContainers_]]">
   </settings-crostini-add-port-dialog>
 </template>
 <cr-lazy-render id="removeAllPortsMenu">
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js
index af84c6c..44cf996 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.js
@@ -14,88 +14,129 @@
 import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './crostini_port_forwarding_add_port_dialog.js';
 import '../../controls/settings_toggle_button.js';
+import '../../settings_page/settings_section.js';
+import '../../settings_page_styles.css.js';
 import '../../settings_shared_css.js';
 import '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
 
 import {assert, assertNotReached} from '//resources/js/assert.m.js';
 import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
 import {loadTimeData} from '//resources/js/load_time_data.m.js';
-import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from '//resources/js/web_ui_listener_behavior.m.js';
+import {afterNextRender, flush, html, mixinBehaviors, PolymerElement, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {recordSettingChange} from '../metrics_recorder.js';
-import {PrefsBehavior} from '../prefs_behavior.js';
+import {PrefsBehavior, PrefsBehaviorInterface} from '../prefs_behavior.js';
 
-import {ContainerId, CrostiniBrowserProxy, CrostiniBrowserProxyImpl, CrostiniDiskInfo, CrostiniPortActiveSetting, CrostiniPortProtocol, CrostiniPortSetting, DEFAULT_CROSTINI_CONTAINER, DEFAULT_CROSTINI_VM, MAX_VALID_PORT_NUMBER, MIN_VALID_PORT_NUMBER, PortState} from './crostini_browser_proxy.js';
+import {ContainerId, ContainerInfo, CrostiniBrowserProxy, CrostiniBrowserProxyImpl, CrostiniDiskInfo, CrostiniPortActiveSetting, CrostiniPortProtocol, CrostiniPortSetting, DEFAULT_CONTAINER_ID, DEFAULT_CROSTINI_CONTAINER, DEFAULT_CROSTINI_VM, MAX_VALID_PORT_NUMBER, MIN_VALID_PORT_NUMBER, PortState} from './crostini_browser_proxy.js';
+import {containerLabel, equalContainerId} from './crostini_container_select.js';
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-crostini-port-forwarding',
 
-  behaviors: [PrefsBehavior, WebUIListenerBehavior],
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {PrefsBehaviorInterface}
+ * @implements {WebUIListenerBehaviorInterface}
+ */
+const CrostiniPortForwardingBase =
+    mixinBehaviors([PrefsBehavior, WebUIListenerBehavior], PolymerElement);
 
-  properties: {
-    /** Preferences state. */
-    prefs: {
-      type: Object,
-      notify: true,
-    },
+/** @polymer */
+class CrostiniPortForwardingElement extends CrostiniPortForwardingBase {
+  static get is() {
+    return 'settings-crostini-port-forwarding';
+  }
 
-    /**
-     * Whether Crostini is running.
-     * @private {boolean}
-     */
-    crostiniRunning_: {
-      type: Boolean,
-      value: false,
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /** @private */
-    showAddPortDialog_: {
-      type: Boolean,
-      value: false,
-    },
-
-    /**
-     * The forwarded ports for display in the UI.
-     * @private {!Array<!CrostiniPortSetting>}
-     */
-    allPorts_: {
-      type: Array,
-      value() {
-        return [];
+  static get properties() {
+    return {
+      /** Preferences state. */
+      prefs: {
+        type: Object,
+        notify: true,
       },
-    },
-  },
 
-  /**
-   * List of ports are currently being forwarded.
-   * @private {!Array<?CrostiniPortActiveSetting>}
-   */
-  activePorts_: new Array(),
+      /**
+       * Whether Crostini is running.
+       * @private {boolean}
+       */
+      crostiniRunning_: {
+        type: Boolean,
+        value: false,
+      },
 
-  /**
-   * Tracks the last port that was selected for removal.
-   * @private {?CrostiniPortActiveSetting}
-   */
-  lastMenuOpenedPort_: null,
+      /** @private */
+      showAddPortDialog_: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * The forwarded ports for display in the UI.
+       * @private {!Array<!CrostiniPortSetting>}
+       */
+      allPorts_: {
+        type: Array,
+        notify: true,
+        value() {
+          return [];
+        },
+      },
+
+      /**
+       * The known ContainerIds for display in the UI.
+       * @private {!Array<!ContainerInfo>}
+       */
+      allContainers_: {
+        type: Array,
+        notify: true,
+        value: [],
+      },
+
+    };
+  }
+
+  static get observers() {
+    return [
+      'onCrostiniPortsChanged_(prefs.crostini.port_forwarding.ports.value)'
+    ];
+  }
+
+  constructor() {
+    super();
+    /**
+     * List of ports are currently being forwarded.
+     * @private {!Array<?CrostiniPortActiveSetting>}
+     */
+    this.activePorts_ = new Array();
+
+    /**
+     * Tracks the last port that was selected for removal.
+     * @private {?CrostiniPortActiveSetting}
+     */
+    this.lastMenuOpenedPort_ = null;
+  }
 
   /** @override */
-  attached() {
+  connectedCallback() {
+    super.connectedCallback();
     this.addWebUIListener(
         'crostini-port-forwarder-active-ports-changed',
         (ports) => this.onCrostiniPortsActiveStateChanged_(ports));
     this.addWebUIListener(
         'crostini-status-changed',
         (isRunning) => this.onCrostiniIsRunningStateChanged_(isRunning));
+    this.addWebUIListener(
+        'crostini-container-info', (infos) => this.onContainerInfo_(infos));
     CrostiniBrowserProxyImpl.getInstance().getCrostiniActivePorts().then(
         (ports) => this.onCrostiniPortsActiveStateChanged_(ports));
     CrostiniBrowserProxyImpl.getInstance().checkCrostiniIsRunning().then(
         (isRunning) => this.onCrostiniIsRunningStateChanged_(isRunning));
-  },
-
-  observers:
-      ['onCrostiniPortsChanged_(prefs.crostini.port_forwarding.ports.value)'],
+    CrostiniBrowserProxyImpl.getInstance().requestContainerInfo();
+  }
 
   /**
    * @param {!CrostiniPortProtocol} protocol
@@ -104,36 +145,48 @@
   getProtocolName(protocol) {
     return Object.keys(CrostiniPortProtocol)
         .find(k => CrostiniPortProtocol[k] === protocol);
-  },
+  }
 
   /**
    * @param {boolean} isRunning boolean indicating if Crostini is running.
    * @private
    */
-  onCrostiniIsRunningStateChanged_: function(isRunning) {
+  onCrostiniIsRunningStateChanged_(isRunning) {
     this.crostiniRunning_ = isRunning;
-  },
+  }
+
+  /**
+   * @param {!Array<!ContainerInfo>} containerInfos
+   */
+  onContainerInfo_(containerInfos) {
+    this.allContainers_ = containerInfos;
+  }
 
   /**
    * @param {!Array<!CrostiniPortSetting>} ports List of ports.
    * @private
    */
-  onCrostiniPortsChanged_: function(ports) {
+  onCrostiniPortsChanged_(ports) {
     this.splice('allPorts_', 0, this.allPorts_.length);
     for (const port of ports) {
       port.is_active = this.activePorts_.some(
           activePort => activePort.port_number === port.port_number &&
               activePort.protocol_type === port.protocol_type);
+      port.container_id = port.container_id || {
+        vm_name: port['vm_name'] || DEFAULT_CROSTINI_VM,
+        container_name: port['container_name'] || DEFAULT_CROSTINI_CONTAINER
+      };
       this.push('allPorts_', port);
     }
-  },
+    this.notifyPath('allContainers_');
+  }
 
   /**
    * @param {!Array<!CrostiniPortActiveSetting>} ports List of ports that are
    *     active.
    * @private
    */
-  onCrostiniPortsActiveStateChanged_: function(ports) {
+  onCrostiniPortsActiveStateChanged_(ports) {
     this.activePorts_ = ports;
     for (let i = 0; i < this.allPorts_.length; i++) {
       this.set(
@@ -144,60 +197,59 @@
                   activePort.protocol_type ===
                       this.allPorts_[i].protocol_type));
     }
-  },
+  }
 
   /**
    * @param {!Event} event
    * @private
    */
-  onAddPortClick_: function(event) {
+  onAddPortClick_(event) {
     this.showAddPortDialog_ = true;
-  },
+  }
 
   /**
    * @param {!Event} event
    * @private
    */
-  onAddPortDialogClose_: function(event) {
+  onAddPortDialogClose_(event) {
     this.showAddPortDialog_ = false;
-  },
+  }
 
   /**
    * @param {!Event} event
    * @private
    */
-  onShowRemoveAllPortsMenuClick_: function(event) {
+  onShowRemoveAllPortsMenuClick_(event) {
     const menu = /** @type {!CrActionMenuElement} */
         (this.$.removeAllPortsMenu.get());
     menu.showAt(/** @type {!HTMLElement} */ (event.target));
-  },
+  }
 
   /**
    * @param {!Event} event
    * @private
    */
-  onShowRemoveSinglePortMenuClick_: function(event) {
-    const dataSet = /**
-                       @type {{portNumber: string, protocolType: string,
-                               containerId: !ContainerId}}
-                     */
+  onShowRemoveSinglePortMenuClick_(event) {
+    const dataSet = /** @type {{portNumber: string, protocolType: string}} */
         (event.currentTarget.dataset);
+    const id = /** @type {ContainerId} */
+        (event.currentTarget['dataContainerId']);
     this.lastMenuOpenedPort_ = {
       port_number: Number(dataSet.portNumber),
       protocol_type: /** @type {!CrostiniPortProtocol} */
           (Number(dataSet.protocolType)),
-      container_id: dataSet.containerId
+      container_id: id,
     };
     const menu = /** @type {!CrActionMenuElement} */
         (this.$.removeSinglePortMenu.get());
     menu.showAt(/** @type {!HTMLElement} */ (event.target));
-  },
+  }
 
   /**
    * @param {!Event} event
    * @private
    */
-  onRemoveSinglePortClick_: function(event) {
+  onRemoveSinglePortClick_(event) {
     const menu = /** @type {!CrActionMenuElement} */
         (this.$.removeSinglePortMenu.get());
     assert(
@@ -214,44 +266,40 @@
           menu.close();
         });
     this.lastMenuOpenedPort_ = null;
-  },
+  }
 
   /**
    * @param {!Event} event
    * @private
    */
-  onRemoveAllPortsClick_: function(event) {
+  onRemoveAllPortsClick_(event) {
     const menu = /** @type {!CrActionMenuElement} */
         (this.$.removeAllPortsMenu.get());
     assert(menu.open);
-    const containerId = /**@type {!ContainerId} */ ({
-      vm_name: DEFAULT_CROSTINI_VM,
-      container_name: DEFAULT_CROSTINI_CONTAINER
-    });
-    CrostiniBrowserProxyImpl.getInstance().removeAllCrostiniPortForwards(
-        containerId);
+    for (const container of this.allContainers_) {
+      CrostiniBrowserProxyImpl.getInstance().removeAllCrostiniPortForwards(
+          container.id);
+    }
     recordSettingChange();
     menu.close();
-  },
+  }
 
   /**
    * @param {!Event} event
    * @private
    */
-  onPortActivationChange_: function(event) {
-    const dataSet = /**
-                       @type {{portNumber: string, protocolType: string,
-                           containerId: !ContainerId}}
-                     */
+  onPortActivationChange_(event) {
+    const dataSet = /** @type {{portNumber: string, protocolType: string}} */
         (event.currentTarget.dataset);
+    const id = /** @type {ContainerId} */
+        (event.currentTarget['dataContainerId']);
     const portNumber = Number(dataSet.portNumber);
     const protocolType = /** @type {!CrostiniPortProtocol} */
         (Number(dataSet.protocolType));
     if (event.target.checked) {
       event.target.checked = false;
       CrostiniBrowserProxyImpl.getInstance()
-          .activateCrostiniPortForward(
-              dataSet.containerId, portNumber, protocolType)
+          .activateCrostiniPortForward(id, portNumber, protocolType)
           .then(result => {
             if (!result) {
               this.$.errorToast.show();
@@ -260,12 +308,54 @@
           });
     } else {
       CrostiniBrowserProxyImpl.getInstance()
-          .deactivateCrostiniPortForward(
-              dataSet.containerId, portNumber, protocolType)
+          .deactivateCrostiniPortForward(id, portNumber, protocolType)
           .then(
               result => {
                   // TODO(crbug.com/848127): Error handling for result
               });
     }
-  },
-});
+  }
+
+  /**
+   * @param {!Array<!CrostiniPortSetting>} allPorts
+   * @param {!ContainerId} id
+   * @return boolean
+   * @private
+   */
+  showContainerId_(allPorts, id) {
+    return allPorts.some(port => equalContainerId(port.container_id, id)) &&
+        allPorts.some(
+            port => !equalContainerId(port.container_id, DEFAULT_CONTAINER_ID));
+  }
+
+  /**
+   * @param {!ContainerId} id
+   * @return string
+   * @private
+   */
+  containerLabel_(id) {
+    return this.showContainerId_(this.allPorts_, id) ? containerLabel(id) : '';
+  }
+
+  /**
+   * @param {!Array<!CrostiniPortSetting>} allPorts
+   * @param {!ContainerId} id
+   * @return boolean
+   * @private
+   */
+  hasContainerPorts_(allPorts, id) {
+    return allPorts.some(port => equalContainerId(port.container_id, id));
+  }
+
+  /**
+   * @param {!ContainerId} id
+   * @return function
+   * @private
+   */
+  byContainerId_(id) {
+    return port => equalContainerId(port.container_id, id);
+  }
+}
+
+customElements.define(
+    CrostiniPortForwardingElement.is, CrostiniPortForwardingElement);
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.html
index 6775d1d..9ecd87e 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.html
@@ -62,6 +62,12 @@
         </select>
       </div>
     </div>
+    <template is="dom-if" if="[[showContainerSelect_(allContainers)]]" restamp>
+      <settings-crostini-container-select
+          containers="[[allContainers]]"
+          selected-container-id="{{containerId_}}">
+      </settings-crostini-container-select>
+    </template>
     <div slot="body">
       <cr-input id="portLabelInput"
           label="$i18n{crostiniPortForwardingAddPortDialogLabelLabel}"
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js
index 4bce7d6..316297f 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js
@@ -13,121 +13,142 @@
 import '//resources/cr_elements/cr_input/cr_input.m.js';
 import '//resources/cr_elements/md_select_css.m.js';
 import '../../settings_shared_css.js';
+import './crostini_container_select.js';
 
 import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
-import {loadTimeData} from '//resources/js/load_time_data.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, microTask, PolymerElement, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {ContainerId, CrostiniBrowserProxy, CrostiniBrowserProxyImpl, CrostiniDiskInfo, CrostiniPortActiveSetting, CrostiniPortProtocol, CrostiniPortSetting, DEFAULT_CROSTINI_CONTAINER, DEFAULT_CROSTINI_VM, MAX_VALID_PORT_NUMBER, MIN_VALID_PORT_NUMBER, PortState} from './crostini_browser_proxy.js';
+import {ContainerId, ContainerInfo, CrostiniBrowserProxy, CrostiniBrowserProxyImpl, CrostiniPortActiveSetting, CrostiniPortProtocol, CrostiniPortSetting, DEFAULT_CONTAINER_ID, MAX_VALID_PORT_NUMBER, MIN_VALID_PORT_NUMBER, PortState} from './crostini_browser_proxy.js';
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-crostini-add-port-dialog',
+/** @polymer */
+class CrostiniPortForwardingAddPortDialog extends PolymerElement {
+  static get is() {
+    return 'settings-crostini-add-port-dialog';
+  }
 
-  properties: {
-    /**
-     * @private {?number}
-     */
-    inputPortNumber_: {
-      type: Number,
-      value: null,
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /**
-     * @private {string}
-     */
-    inputPortLabel_: {
-      type: String,
-      value: '',
-    },
 
-    /**
-     * @private {number}
-     */
-    inputProtocolIndex_: {
-      type: Number,
-      value: 0,  // Default: TCP
-    },
-
-    /**
-     * @private {!PortState}
-     */
-    portState_: {
-      type: String,
-      value: PortState.VALID,
-    },
-
-    /**
-     * @private {!ContainerId}
-     */
-    containerId_: {
-      type: Object,
-      value: {
-        vm_name: DEFAULT_CROSTINI_VM,
-        container_name: DEFAULT_CROSTINI_CONTAINER,
+  static get properties() {
+    return {
+      /**
+       * @private {?number}
+       */
+      inputPortNumber_: {
+        type: Number,
+        value: null,
       },
-    },
 
-    /**
-     * List of ports that are already stored in the settings.
-     * @type {!Array<!CrostiniPortSetting>}
-     */
-    allPorts: {
-      type: Array,
-      value() {
-        return [];
+      /**
+       * @private {string}
+       */
+      inputPortLabel_: {
+        type: String,
+        value: '',
       },
-    },
-  },
 
-  observers: [
-    'onPortStateChanged_(portState_)',
-  ],
+      /**
+       * @private {number}
+       */
+      inputProtocolIndex_: {
+        type: Number,
+        value: 0,  // Default: TCP
+      },
+
+      /**
+       * @private {!PortState}
+       */
+      portState_: {
+        type: String,
+        value: PortState.VALID,
+      },
+
+      /**
+       * @type {!ContainerId}
+       */
+      containerId_: {
+        type: Object,
+        value() {
+          return DEFAULT_CONTAINER_ID;
+        },
+      },
+
+      /**
+       * List of ports that are already stored in the settings.
+       * @type {!Array<!CrostiniPortSetting>}
+       */
+      allPorts: {
+        type: Array,
+        value() {
+          return [];
+        },
+      },
+
+      /**
+       * List of containers that are already stored in the settings.
+       * @type {!Array<!ContainerInfo>}
+       */
+      allContainers: {
+        type: Array,
+        value: [],
+      },
+
+    };
+  }
+
+  static get observers() {
+    return [
+      'onPortStateChanged_(portState_)',
+    ];
+  }
 
   /** @override */
-  attached: function() {
+  connectedCallback() {
+    super.connectedCallback();
     this.$.dialog.showModal();
-    this.async(() => {
+    microTask.run(() => {
       this.$.portNumberInput.focus();
-    }, 1);
-  },
+    });
+  }
 
-  resetInputs_: function() {
+  resetInputs_() {
     this.inputPortLabel_ = '';
     this.inputPortNumber_ = null;
     this.inputProtocolIndex_ = 0;
     this.portState_ = PortState.VALID;
-  },
+  }
 
   /**
    * @return {!CrInputElement} input for the port number.
    */
   get portNumberInput() {
     return /** @type{!CrInputElement} */ (this.$.portNumberInput);
-  },
+  }
 
   /**
    * @return {!CrInputElement} input for the optional port label.
    */
   get portLabelInput() {
     return /** @type{!CrInputElement} */ (this.$.portLabelInput);
-  },
+  }
 
   /**
    * @param {string} input The port input to verify.
    * @return {?boolean} if the input string is a valid port number.
    */
-  isValidPortNumber: function(input) {
+  isValidPortNumber(input) {
     const numberRegex = /^[0-9]+$/;
     return input.match(numberRegex) && Number(input) >= MIN_VALID_PORT_NUMBER &&
         Number(input) <= MAX_VALID_PORT_NUMBER;
-  },
+  }
 
   /**
    * @return {!PortState}
    * @private
    */
-  computePortState_: function() {
+  computePortState_() {
     if (!this.isValidPortNumber(this.$.portNumberInput.value)) {
       return PortState.INVALID;
     }
@@ -138,25 +159,25 @@
       return PortState.DUPLICATE;
     }
     return PortState.VALID;
-  },
+  }
 
   /**
    * @param {!Event} e
    * @private
    */
-  onSelectProtocol_: function(e) {
+  onSelectProtocol_(e) {
     this.inputProtocolIndex_ = e.target.selectedIndex;
     this.portState_ = this.computePortState_();
-  },
+  }
 
   /** @private */
-  onCancelTap_: function() {
+  onCancelTap_() {
     this.$.dialog.close();
     this.resetInputs_();
-  },
+  }
 
   /** @private */
-  onAddTap_: function() {
+  onAddTap_() {
     this.portState_ = this.computePortState_();
     if (this.portState_ !== PortState.VALID) {
       return;
@@ -173,15 +194,15 @@
           this.$.dialog.close();
         });
     this.resetInputs_();
-  },
+  }
 
   /** @private */
-  onBlur_: function() {
+  onBlur_() {
     this.portState_ = this.computePortState_();
-  },
+  }
 
   /** @private */
-  onPortStateChanged_: function() {
+  onPortStateChanged_() {
     if (this.portState_ === PortState.VALID) {
       this.$.portNumberInput.invalid = false;
       this.$.continue.disabled = false;
@@ -190,4 +211,17 @@
     this.$.portNumberInput.invalid = true;
     this.$.continue.disabled = true;
   }
-});
+
+  /**
+   * @param {!Array<!ContainerInfo>} allContainers
+   * @return boolean
+   * @private
+   */
+  showContainerSelect_(allContainers) {
+    return allContainers.length > 1;
+  }
+}
+
+customElements.define(
+    CrostiniPortForwardingAddPortDialog.is,
+    CrostiniPortForwardingAddPortDialog);
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.js b/chrome/browser/resources/settings/chromeos/lazy_load.js
index 353d6f6..e4599c0a 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.js
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.js
@@ -7,6 +7,7 @@
 import './crostini_page/crostini_arc_adb.js';
 import './crostini_page/crostini_arc_adb_confirmation_dialog.js';
 import './crostini_page/crostini_confirmation_dialog.js';
+import './crostini_page/crostini_container_select.js';
 import './crostini_page/crostini_disk_resize_confirmation_dialog.js';
 import './crostini_page/crostini_disk_resize_dialog.js';
 import './crostini_page/crostini_export_import.js';
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 7ea4ae80..3642c25 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -1513,8 +1513,15 @@
   SetTitle(kSingleProfileIndex, bookmark, new_title);
 }
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+// Flaky on most desktop platforms. See https://crbug.com/1330378.
+#define MAYBE_ShouldUploadUnsyncedEntityAfterRestart DISABLED_ShouldUploadUnsyncedEntityAfterRestart
+#else
+#define MAYBE_ShouldUploadUnsyncedEntityAfterRestart ShouldUploadUnsyncedEntityAfterRestart
+#endif
+
 IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
-                       ShouldUploadUnsyncedEntityAfterRestart) {
+                       MAYBE_ShouldUploadUnsyncedEntityAfterRestart) {
   ASSERT_TRUE(SetupClients());
 
   const std::string title = "Title";
diff --git a/chrome/browser/ui/app_list/search/mixer_unittest.cc b/chrome/browser/ui/app_list/search/mixer_unittest.cc
index 4722e1c..398c0d33 100644
--- a/chrome/browser/ui/app_list/search/mixer_unittest.cc
+++ b/chrome/browser/ui/app_list/search/mixer_unittest.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/search_controller_impl.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -37,37 +38,6 @@
 const size_t kMaxOmniboxResults = 4;
 const size_t kMaxPlaystoreResults = 2;
 
-class TestSearchResult : public ChromeSearchResult {
- public:
-  TestSearchResult(const std::string& id, double relevance)
-      : instance_id_(instantiation_count++) {
-    set_id(id);
-    SetTitle(base::UTF8ToUTF16(id));
-    set_relevance(relevance);
-  }
-
-  TestSearchResult(const TestSearchResult&) = delete;
-  TestSearchResult& operator=(const TestSearchResult&) = delete;
-
-  ~TestSearchResult() override {}
-
-  // ChromeSearchResult overrides:
-  void Open(int event_flags) override {}
-
-  // For reference equality testing. (Addresses cannot be used to test reference
-  // equality because it is possible that an object will be allocated at the
-  // same address as a previously deleted one.)
-  static int GetInstanceId(ChromeSearchResult* result) {
-    return static_cast<const TestSearchResult*>(result)->instance_id_;
-  }
-
- private:
-  static int instantiation_count;
-
-  int instance_id_;
-};
-int TestSearchResult::instantiation_count = 0;
-
 class TestSearchProvider : public SearchProvider {
  public:
   TestSearchProvider(const std::string& prefix, SearchResultType result_type)
@@ -99,7 +69,7 @@
       // make the differences very small: 0.5, 0.499, 0.498, ...
       if (small_relevance_range_)
         relevance = 0.5 - i / 100.0;
-      TestSearchResult* result = new TestSearchResult(id, relevance);
+      TestResult* result = new TestResult(id, relevance);
       result->SetDisplayType(display_type_);
       result->SetResultType(result_type_);
 
diff --git a/chrome/browser/ui/app_list/search/ranking/answer_ranker_unittest.cc b/chrome/browser/ui/app_list/search/ranking/answer_ranker_unittest.cc
index 7eb7ebc..5736c33 100644
--- a/chrome/browser/ui/app_list/search/ranking/answer_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/ranking/answer_ranker_unittest.cc
@@ -6,30 +6,21 @@
 
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/ranking/types.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace app_list {
 namespace {
 
-class TestResult : public ChromeSearchResult {
- public:
-  TestResult(double relevance, DisplayType display_type, bool best_match) {
-    set_relevance(relevance);
-    SetDisplayType(display_type);
-    SetBestMatch(best_match);
-  }
-  ~TestResult() override {}
-
-  // ChromeSearchResult overrides:
-  void Open(int event_flags) override {}
-};
-
 Results make_omnibox_candidates(std::vector<double> relevances) {
   Results results;
   for (const double relevance : relevances) {
+    // |id| and |normalized_relevance| must be set but are not used.
     results.push_back(std::make_unique<TestResult>(
-        relevance, ash::SearchResultDisplayType::kAnswerCard, false));
+        /*id=*/"", relevance,
+        /*normalized_relevance=*/0.0, ash::SearchResultDisplayType::kAnswerCard,
+        false));
   }
   return results;
 }
@@ -37,8 +28,11 @@
 Results make_shortcut_candidates(std::vector<bool> best_matches) {
   Results results;
   for (const double best_match : best_matches) {
+    // |id| and |normalized_relevance| must be set but are not used.
     results.push_back(std::make_unique<TestResult>(
-        1, ash::SearchResultDisplayType::kList, best_match));
+        /*id=*/"",
+        /*relevance=*/1, /*normalized_relevance=*/0.0,
+        ash::SearchResultDisplayType::kList, best_match));
   }
   return results;
 }
diff --git a/chrome/browser/ui/app_list/search/ranking/best_match_ranker_unittest.cc b/chrome/browser/ui/app_list/search/ranking/best_match_ranker_unittest.cc
index 9cbcf56..303880b1 100644
--- a/chrome/browser/ui/app_list/search/ranking/best_match_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/ranking/best_match_ranker_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/ranking/types.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -15,21 +16,11 @@
 
 using testing::ElementsAreArray;
 
-class TestResult : public ChromeSearchResult {
- public:
-  explicit TestResult(const std::string& id, double relevance) {
-    set_id(id);
-    scoring().normalized_relevance = relevance;
-  }
-  ~TestResult() override {}
-
-  // ChromeSearchResult:
-  void Open(int event_flags) override {}
-};
-
 std::unique_ptr<TestResult> MakeResult(const std::string& id,
-                                       double relevance) {
-  return std::make_unique<TestResult>(id, relevance);
+                                       double normalized_relevance) {
+  // |relevance| must be set but is unused.
+  return std::make_unique<TestResult>(id, /*relevance=*/0.0,
+                                      normalized_relevance);
 }
 
 Results MakeAnswers(
@@ -161,13 +152,13 @@
   ranker_.UpdateResultRanks(results_map, ProviderType::kAssistantText);
 
   // kAssistantText is a low-intent provider and should be ignored from best
-  // match. The other results should be sorted by relevance.
+  // match. The other results should be sorted by (normalized) relevance.
   ExpectBestMatchOrderAndRanks({{"omni_2", 0}, {"omni_1", 1}});
 }
 
 // During the post-burn-in phase, the highest-ranked best match should remain
 // stabilized in this position, and any remaining best matches should be sorted
-// by relevance score.
+// by (normalized) relevance score.
 TEST_F(BestMatchRankerTest, PostBurnIn_HighestBestMatchIsStabilized) {
   ResultsMap results_map;
 
diff --git a/chrome/browser/ui/app_list/search/ranking/filtering_ranker_unittest.cc b/chrome/browser/ui/app_list/search/ranking/filtering_ranker_unittest.cc
index c387fa2..b622e79 100644
--- a/chrome/browser/ui/app_list/search/ranking/filtering_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/ranking/filtering_ranker_unittest.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/ranking/types.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -17,15 +18,12 @@
 using testing::UnorderedElementsAre;
 using testing::UnorderedElementsAreArray;
 
-class TestDriveIdResult : public ChromeSearchResult {
+class TestDriveIdResult : public TestResult {
  public:
   TestDriveIdResult(const std::string& id,
                     ResultType type,
                     const absl::optional<std::string>& drive_id)
-      : drive_id_(drive_id) {
-    set_id(id);
-    SetResultType(type);
-  }
+      : TestResult(id, type), drive_id_(drive_id) {}
 
   ~TestDriveIdResult() override {}
 
diff --git a/chrome/browser/ui/app_list/search/ranking/query_highlighter_unittest.cc b/chrome/browser/ui/app_list/search/ranking/query_highlighter_unittest.cc
index 0918f6f..bc44f67 100644
--- a/chrome/browser/ui/app_list/search/ranking/query_highlighter_unittest.cc
+++ b/chrome/browser/ui/app_list/search/ranking/query_highlighter_unittest.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/ranking/ranker.h"
 #include "chrome/browser/ui/app_list/search/ranking/types.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -15,15 +16,6 @@
 
 using Tag = ash::SearchResultTag;
 
-class TestResult : public ChromeSearchResult {
- public:
-  TestResult() = default;
-  ~TestResult() override = default;
-
-  // ChromeSearchResult overrides:
-  void Open(int event_flags) override {}
-};
-
 Results MakeResults(const std::vector<std::u16string>& titles) {
   Results results;
   for (const auto& title : titles) {
diff --git a/chrome/browser/ui/app_list/search/ranking/removed_results_ranker_unittest.cc b/chrome/browser/ui/app_list/search/ranking/removed_results_ranker_unittest.cc
index 7461089..41acd9de 100644
--- a/chrome/browser/ui/app_list/search/ranking/removed_results_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/ranking/removed_results_ranker_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/ranking/types.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,15 +21,6 @@
 using testing::UnorderedElementsAre;
 using testing::UnorderedElementsAreArray;
 
-class TestResult : public ChromeSearchResult {
- public:
-  explicit TestResult(const std::string& id) { set_id(id); }
-  ~TestResult() override {}
-
-  // ChromeSearchResult:
-  void Open(int event_flags) override {}
-};
-
 std::unique_ptr<TestResult> MakeResult(const std::string& id) {
   return std::make_unique<TestResult>(id);
 }
diff --git a/chrome/browser/ui/app_list/search/ranking/score_normalizing_ranker_unittest.cc b/chrome/browser/ui/app_list/search/ranking/score_normalizing_ranker_unittest.cc
index 4e2a543..c315092 100644
--- a/chrome/browser/ui/app_list/search/ranking/score_normalizing_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/ranking/score_normalizing_ranker_unittest.cc
@@ -10,34 +10,22 @@
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/ranking/types.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace app_list {
 namespace {
 
-class TestResult : public ChromeSearchResult {
- public:
-  explicit TestResult(const std::string& id,
-                      double score,
-                      ResultType result_type) {
-    set_id(id);
-    SetDisplayScore(score);
-    SetResultType(result_type);
-  }
-  ~TestResult() override {}
-
-  // ChromeSearchResult overrides:
-  void Open(int event_flags) override {}
-};
-
 Results MakeResults(const std::vector<std::string>& ids,
                     const std::vector<double> scores,
                     ResultType result_type) {
   Results res;
   CHECK_EQ(ids.size(), scores.size());
   for (size_t i = 0; i < ids.size(); ++i) {
-    res.push_back(std::make_unique<TestResult>(ids[i], scores[i], result_type));
+    res.push_back(std::make_unique<TestResult>(ids[i], result_type,
+                                               Category::kUnknown,
+                                               /*display_score=*/scores[i]));
   }
   return res;
 }
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new_unittest.cc b/chrome/browser/ui/app_list/search/search_controller_impl_new_unittest.cc
index b68a21a7..b42586c7 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_new_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new_unittest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/app_list/search/ranking/ranker_delegate.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
 #include "chrome/test/base/chrome_ash_test_base.h"
 #include "chrome/test/base/testing_profile.h"
@@ -28,34 +29,15 @@
 namespace {
 
 // TODO(crbug.com/1258415): Since we have a lot of class fakes now, we should
-// generalize them and split them into a test utils directory.
+// generalize them and split them into a test utils directory. This has been
+// done for the TestResult class, and could also be done for various util
+// functions such as MakeResults().
 
 using testing::ElementsAreArray;
 using testing::UnorderedElementsAreArray;
 using Category = ash::AppListSearchResultCategory;
 using Result = ash::AppListSearchResultType;
 
-class TestSearchResult : public ChromeSearchResult {
- public:
-  TestSearchResult(const std::string& id,
-                   Category category,
-                   int best_match_rank,
-                   double relevance) {
-    set_id(id);
-    SetCategory(category);
-    scoring().best_match_rank = best_match_rank;
-    set_relevance(relevance);
-    scoring().ftrl_result_score = relevance;
-  }
-
-  TestSearchResult(const TestSearchResult&) = delete;
-  TestSearchResult& operator=(const TestSearchResult&) = delete;
-  ~TestSearchResult() override = default;
-
- private:
-  void Open(int event_flags) override { NOTIMPLEMENTED(); }
-};
-
 class TestSearchProvider : public SearchProvider {
  public:
   TestSearchProvider(ash::AppListSearchResultType result_type,
@@ -150,8 +132,9 @@
     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<TestSearchResult>(
-        ids[i], categories[i], best_match_ranks[i], scores[i]));
+    results.emplace_back(std::make_unique<TestResult>(
+        ids[i], categories[i], best_match_ranks[i],
+        /*relevance=*/scores[i], /*ftrl_result_score=*/scores[i]));
   }
   return results;
 }
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_unittest.cc b/chrome/browser/ui/app_list/search/search_controller_impl_unittest.cc
index 1326d5e..42228605 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/test/base/chrome_ash_test_base.h"
 
@@ -20,19 +21,6 @@
 
 using ::test::TestAppListControllerDelegate;
 
-// TestSearchResult ------------------------------------------------------------
-
-class TestSearchResult : public ChromeSearchResult {
- public:
-  TestSearchResult() = default;
-  TestSearchResult(const TestSearchResult&) = delete;
-  TestSearchResult& operator=(const TestSearchResult&) = delete;
-  ~TestSearchResult() override = default;
-
- private:
-  void Open(int event_flags) override { NOTIMPLEMENTED(); }
-};
-
 // SearchControllerImplTest
 // --------------------------------------------------------
 
@@ -89,7 +77,7 @@
                         /*request_to_dismiss_view=*/true,
                         /*expect_to_dismiss_view=*/true});
 
-  auto result = std::make_unique<TestSearchResult>();
+  auto result = std::make_unique<TestResult>();
   for (auto& test_case : test_cases) {
     ash::ShellTestApi().SetTabletModeEnabledForTest(test_case.is_tablet_mode);
 
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/chip_ranker_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/chip_ranker_unittest.cc
index 1cb4e6c..28def606 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/chip_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/chip_ranker_unittest.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/mixer.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -27,14 +28,10 @@
 
 using ResultType = ash::AppListSearchResultType;
 
-class TestSearchResult : public ChromeSearchResult {
+class ChipTestResult : public TestResult {
  public:
-  TestSearchResult(const std::string& id, ResultType type)
-      : instance_id_(instantiation_count++) {
-    set_id(id);
-    SetTitle(base::UTF8ToUTF16(id));
-    SetResultType(type);
-
+  ChipTestResult(const std::string& id, ResultType type)
+      : TestResult(id, type), instance_id_(instantiation_count++) {
     switch (type) {
       case ResultType::kFileChip:
       case ResultType::kDriveChip:
@@ -55,10 +52,10 @@
     }
   }
 
-  TestSearchResult(const TestSearchResult&) = delete;
-  TestSearchResult& operator=(const TestSearchResult&) = delete;
+  ChipTestResult(const ChipTestResult&) = delete;
+  ChipTestResult& operator=(const ChipTestResult&) = delete;
 
-  ~TestSearchResult() override {}
+  ~ChipTestResult() override {}
 
   // ChromeSearchResult overrides:
   void Open(int event_flags) override {}
@@ -69,7 +66,7 @@
   int instance_id_;
 };
 
-int TestSearchResult::instantiation_count = 0;
+int ChipTestResult::instantiation_count = 0;
 
 MATCHER_P(HasId, id, "") {
   bool match = base::UTF16ToUTF8(arg.result->title()) == id;
@@ -128,10 +125,10 @@
 
   std::unique_ptr<ChipRanker> ranker_;
 
-  // This is used only to make the ownership clear for the TestSearchResult
+  // This is used only to make the ownership clear for the ChipTestResult
   // objects that the return value of MakeSearchResults() contains raw pointers
   // to.
-  std::list<TestSearchResult> results_;
+  std::list<ChipTestResult> results_;
 };
 
 // Check that ranking an empty list has no effect.
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
index d71124f..8965558 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_predictor.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.h"
+#include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_database_params.h"
 #include "components/history/core/browser/history_service.h"
@@ -53,30 +54,6 @@
 using testing::UnorderedElementsAre;
 using testing::WhenSorted;
 
-class TestSearchResult : public ChromeSearchResult {
- public:
-  TestSearchResult(const std::string& id, ResultType type)
-      : instance_id_(instantiation_count++) {
-    set_id(id);
-    SetTitle(base::UTF8ToUTF16(id));
-    SetResultType(type);
-  }
-
-  TestSearchResult(const TestSearchResult&) = delete;
-  TestSearchResult& operator=(const TestSearchResult&) = delete;
-
-  ~TestSearchResult() override {}
-
-  // ChromeSearchResult overrides:
-  void Open(int event_flags) override {}
-
- private:
-  static int instantiation_count;
-
-  int instance_id_;
-};
-int TestSearchResult::instantiation_count = 0;
-
 MATCHER_P(HasId, id, "") {
   bool match = base::UTF16ToUTF8(arg.result->title()) == id;
   if (!match)
@@ -177,10 +154,10 @@
 
   void Wait() { task_environment_.RunUntilIdle(); }
 
-  // This is used only to make the ownership clear for the TestSearchResult
+  // This is used only to make the ownership clear for the TestResult
   // objects that the return value of MakeSearchResults() contains raw pointers
   // to.
-  std::list<TestSearchResult> test_search_results_;
+  std::list<TestResult> test_search_results_;
 
   ScopedTempDir temp_dir_;
   content::BrowserTaskEnvironment task_environment_;
diff --git a/chrome/browser/ui/app_list/search/test/ranking_test_util.cc b/chrome/browser/ui/app_list/search/test/ranking_test_util.cc
index 100e6a8..bbd8e7c0 100644
--- a/chrome/browser/ui/app_list/search/test/ranking_test_util.cc
+++ b/chrome/browser/ui/app_list/search/test/ranking_test_util.cc
@@ -4,19 +4,49 @@
 
 #include "chrome/browser/ui/app_list/search/test/ranking_test_util.h"
 
+#include "base/strings/utf_string_conversions.h"
+
 namespace app_list {
 
 // TestResult ------------------------------------------------------------------
 
 TestResult::TestResult(const std::string& id,
-                       double score,
                        ResultType result_type,
-                       Category category) {
+                       Category category,
+                       double display_score,
+                       double normalized_relevance) {
   set_id(id);
-  SetDisplayScore(score);
-  scoring().normalized_relevance = score;
+  SetTitle(base::UTF8ToUTF16(id));
   SetResultType(result_type);
   SetCategory(category);
+  SetDisplayScore(display_score);
+  scoring().normalized_relevance = normalized_relevance;
+}
+
+TestResult::TestResult(const std::string& id,
+                       double relevance,
+                       double normalized_relevance,
+                       DisplayType display_type,
+                       bool best_match) {
+  set_id(id);
+  SetTitle(base::UTF8ToUTF16(id));
+  set_relevance(relevance);
+  scoring().normalized_relevance = normalized_relevance;
+  SetDisplayType(display_type);
+  SetBestMatch(best_match);
+}
+
+TestResult::TestResult(const std::string& id,
+                       Category category,
+                       int best_match_rank,
+                       double relevance,
+                       double ftrl_result_score) {
+  set_id(id);
+  SetTitle(base::UTF8ToUTF16(id));
+  SetCategory(category);
+  scoring().best_match_rank = best_match_rank;
+  set_relevance(relevance);
+  scoring().ftrl_result_score = ftrl_result_score;
 }
 
 TestResult::~TestResult() {}
@@ -38,8 +68,9 @@
   Results res;
   CHECK_EQ(ids.size(), scores.size());
   for (size_t i = 0; i < ids.size(); ++i) {
-    res.push_back(
-        std::make_unique<TestResult>(ids[i], scores[i], result_type, category));
+    res.push_back(std::make_unique<TestResult>(
+        ids[i], result_type, category, /*display_score=*/scores[i],
+        /*normalized_relevance=*/scores[i]));
   }
   return res;
 }
diff --git a/chrome/browser/ui/app_list/search/test/ranking_test_util.h b/chrome/browser/ui/app_list/search/test/ranking_test_util.h
index 9ac3a95c..e9b248ad7 100644
--- a/chrome/browser/ui/app_list/search/test/ranking_test_util.h
+++ b/chrome/browser/ui/app_list/search/test/ranking_test_util.h
@@ -18,10 +18,31 @@
 
 class TestResult : public ChromeSearchResult {
  public:
+  // TestResult is used by many test suites. Each test suite operates on
+  // different members of ChromeSearchResult. This set of constructors aims to
+  //
+  // (a) generalize across the use cases (to minimize constructor number) and
+  // (b) retain flexibility (to keep points-of-use from becoming cumbersome).
+  TestResult() = default;
+
   TestResult(const std::string& id,
-             double score,
              ResultType result_type = ResultType::kUnknown,
-             Category category = Category::kUnknown);
+             Category category = Category::kUnknown,
+             double display_score = 0.0,
+             double normalized_relevance = 0.0);
+
+  TestResult(const std::string& id,
+             double relevance,
+             double normalized_relevance = 0.0,
+             DisplayType display_type = DisplayType::kNone,
+             bool best_match = false);
+
+  TestResult(const std::string& id,
+             Category category,
+             int best_match_rank,
+             double relevance,
+             double ftrl_result_score);
+
   ~TestResult() override;
 
   // ChromeSearchResult overrides:
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc
index d074862..beaa738 100644
--- a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc
@@ -460,7 +460,6 @@
   window->SetProperty(ash::kShelfIDKey, shelf_id.Serialize());
   window->SetProperty(ash::kArcPackageNameKey, info->package_name());
   window->SetProperty(ash::kAppIDKey, shelf_id.app_id);
-  window->SetProperty(aura::client::kSkipImeProcessing, true);
 
   if (info->launch_intent().empty())
     return;
diff --git a/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
index 7f42cb6e..a0f7f10f 100644
--- a/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
@@ -116,4 +116,6 @@
     out_properties_container.SetProperty(
         app_restore::kParentToHiddenContainerKey, true);
   }
+
+  out_properties_container.SetProperty(aura::client::kSkipImeProcessing, true);
 }
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc
index a5ad0c56..c8efe84 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -758,6 +758,9 @@
 #if BUILDFLAG(IS_LINUX)
 // Flaky on Linux Ozone. See https://crbug.com/1230723.
 #define MAYBE_SwitchToTabCorrectWindow DISABLED_SwitchToTabCorrectWindow
+#elif BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_WIN)
+// Flaky on Lacros and Win. See https://crbug.com/674497.
+#define MAYBE_SwitchToTabCorrectWindow DISABLED_SwitchToTabCorrectWindow
 #else
 #define MAYBE_SwitchToTabCorrectWindow SwitchToTabCorrectWindow
 #endif
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h
index c79f69a9..a3e133bc 100644
--- a/chrome/browser/ui/color/chrome_color_id.h
+++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -116,6 +116,9 @@
   E(kColorInfoBarContentAreaSeparator, \
     ThemeProperties::COLOR_INFOBAR_CONTENT_AREA_SEPARATOR) \
   E_CPONLY(kColorInfoBarForeground) \
+  /* Intent Picker colors. */ \
+  E_CPONLY(kColorIntentPickerItemBackgroundHovered) \
+  E_CPONLY(kColorIntentPickerItemBackgroundSelected) \
   /* Location bar colors. */ \
   E(kColorLocationBarBorder, ThemeProperties::COLOR_LOCATION_BAR_BORDER) \
   E(kColorLocationBarBorderOpaque, \
diff --git a/chrome/browser/ui/color/chrome_color_mixer.cc b/chrome/browser/ui/color/chrome_color_mixer.cc
index 5100c9e..40222c009 100644
--- a/chrome/browser/ui/color/chrome_color_mixer.cc
+++ b/chrome/browser/ui/color/chrome_color_mixer.cc
@@ -289,6 +289,12 @@
   mixer[kColorInfoBarContentAreaSeparator] =
       ui::AlphaBlend(kColorToolbarButtonIcon, kColorInfoBarBackground, 0x3A);
   mixer[kColorInfoBarForeground] = {kColorToolbarText};
+  mixer[kColorIntentPickerItemBackgroundHovered] = ui::SetAlpha(
+      ui::GetColorWithMaxContrast(ui::kColorDialogBackground), 0x0F);  // 6%.
+  // TODO(crbug.com/1330183): Improve selection color.
+  mixer[kColorIntentPickerItemBackgroundSelected] = ui::BlendForMinContrast(
+      ui::kColorDialogBackground, ui::kColorDialogBackground, ui::kColorAccent,
+      1.2);
   mixer[kColorLocationBarBorder] = {SkColorSetA(SK_ColorBLACK, 0x4D)};
   mixer[kColorLocationBarBorderOpaque] =
       ui::GetResultingPaintColor(kColorLocationBarBorder, kColorToolbar);
diff --git a/chrome/browser/ui/intent_picker_tab_helper.cc b/chrome/browser/ui/intent_picker_tab_helper.cc
index 866124b..2b633952 100644
--- a/chrome/browser/ui/intent_picker_tab_helper.cc
+++ b/chrome/browser/ui/intent_picker_tab_helper.cc
@@ -28,7 +28,6 @@
 #include "content/public/browser/navigation_handle.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/models/image_model.h"
-#include "ui/gfx/favicon_size.h"
 #include "ui/gfx/image/image.h"
 #include "url/origin.h"
 
@@ -212,7 +211,8 @@
   Profile* profile =
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
 
-  LoadSingleAppIcon(profile, app_type, app_id, gfx::kFaviconSize,
+  LoadSingleAppIcon(profile, app_type, app_id,
+                    apps::GetIntentPickerBubbleIconSize(),
                     base::BindOnce(&IntentPickerTabHelper::OnAppIconLoaded,
                                    weak_factory_.GetWeakPtr(), std::move(apps),
                                    std::move(callback), index));
diff --git a/chrome/browser/ui/views/autofill_assistant/password_change/assistant_side_panel_coordinator_impl.cc b/chrome/browser/ui/views/autofill_assistant/password_change/assistant_side_panel_coordinator_impl.cc
index 4200200f..6ee2e4f 100644
--- a/chrome/browser/ui/views/autofill_assistant/password_change/assistant_side_panel_coordinator_impl.cc
+++ b/chrome/browser/ui/views/autofill_assistant/password_change/assistant_side_panel_coordinator_impl.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_registry.h"
+#include "ui/views/layout/box_layout.h"
 
 namespace {
 constexpr int kAssistantIconSize = 16;
@@ -109,6 +110,8 @@
 AssistantSidePanelCoordinatorImpl::CreateSidePanelView() {
   std::unique_ptr<views::View> side_panel_view;
   side_panel_view = std::make_unique<views::View>();
+  side_panel_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical));
   if (side_panel_view_child_) {
     side_panel_view->AddChildView(std::move(side_panel_view_child_));
   }
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index d06b4aa3dcb..4f30bb4 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/apps/intent_helper/intent_picker_helpers.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/hover_button.h"
@@ -35,17 +36,25 @@
 #include "ui/base/models/image_model.h"
 #include "ui/color/color_id.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/text_constants.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/animation/ink_drop_host_view.h"
+#include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/layout_types.h"
+#include "ui/views/layout/table_layout.h"
+#include "ui/views/layout/table_layout_view.h"
+#include "ui/views/style/typography.h"
+#include "ui/views/view_class_properties.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
 #include "ui/chromeos/devicetype_utils.h"
@@ -55,12 +64,199 @@
 
 constexpr char kInvalidLaunchName[] = "";
 
+constexpr int kGridItemPreferredSize = 96;
+constexpr int kGridItemsPerRow = 3;
+constexpr int kGridInteriorColumnPadding = 8;
+constexpr int kGridInteriorRowPadding = 8;
+constexpr int kGridExteriorColumnPadding = 8;
+
+constexpr int kGridItemTopInset = 12;
+constexpr int kGridItemInset = 2;
+constexpr int kGridItemInteriorPadding = 8;
+constexpr int kGridItemBorderRadius = 4;
+
 bool IsKeyboardCodeArrow(ui::KeyboardCode key_code) {
   return key_code == ui::VKEY_UP || key_code == ui::VKEY_DOWN ||
          key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_LEFT;
 }
 
-// IntentPickerLabelButton
+bool IsDoubleClick(const ui::Event& event) {
+  return (event.IsMouseEvent() && event.AsMouseEvent()->GetClickCount() == 2) ||
+         (event.IsGestureEvent() &&
+          event.AsGestureEvent()->details().tap_count() == 2);
+}
+
+// Callback for when an app is selected in the app list. First parameter is the
+// index, second parameter is true if the dialog should be immediately accepted.
+using AppSelectedCallback = base::RepeatingCallback<void(size_t, bool)>;
+
+// Grid view:
+
+// A Button which displays an app icon and name, as part of a grid layout of
+// apps.
+class IntentPickerAppGridButton : public views::Button {
+ public:
+  METADATA_HEADER(IntentPickerAppGridButton);
+
+  IntentPickerAppGridButton(PressedCallback callback,
+                            const ui::ImageModel& icon_model,
+                            const std::string& display_name)
+      : views::Button(std::move(callback)) {
+    auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
+        views::BoxLayout::Orientation::kVertical,
+        gfx::Insets::TLBR(kGridItemTopInset, kGridItemInset, kGridItemInset,
+                          kGridItemInset),
+        kGridItemInteriorPadding, true));
+    layout->set_main_axis_alignment(
+        views::BoxLayout::MainAxisAlignment::kStart);
+    layout->set_cross_axis_alignment(
+        views::BoxLayout::CrossAxisAlignment::kCenter);
+
+    auto* icon_view =
+        AddChildView(std::make_unique<views::ImageView>(icon_model));
+    icon_view->SetCanProcessEventsWithinSubtree(false);
+
+    auto* name_label = AddChildView(std::make_unique<views::Label>(
+        base::UTF8ToUTF16(display_name), views::style::CONTEXT_BUTTON));
+    name_label->SetMultiLine(true);
+    name_label->SetMaxLines(2);
+    name_label->SetMaximumWidth(kGridItemPreferredSize);
+    name_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_CENTER);
+    name_label->SetVerticalAlignment(gfx::VerticalAlignment::ALIGN_TOP);
+
+    SetFocusBehavior(FocusBehavior::ALWAYS);
+    SetAccessibleName(name_label->GetText());
+    SetPreferredSize(gfx::Size(kGridItemPreferredSize, kGridItemPreferredSize));
+  }
+  IntentPickerAppGridButton(const IntentPickerAppGridButton&) = delete;
+  IntentPickerAppGridButton& operator=(const IntentPickerAppGridButton&) =
+      delete;
+  ~IntentPickerAppGridButton() override = default;
+
+  void SetSelected(bool selected) {
+    selected_ = selected;
+    UpdateBackground();
+  }
+
+  // views::Button:
+  void StateChanged(ButtonState old_state) override { UpdateBackground(); }
+
+ private:
+  void UpdateBackground() {
+    ui::ColorId color;
+    if (selected_ || GetState() == ButtonState::STATE_PRESSED) {
+      color = kColorIntentPickerItemBackgroundSelected;
+    } else if (GetState() == ButtonState::STATE_HOVERED) {
+      color = kColorIntentPickerItemBackgroundHovered;
+    } else {
+      SetBackground(nullptr);
+      return;
+    }
+
+    SetBackground(
+        views::CreateThemedRoundedRectBackground(color, kGridItemBorderRadius));
+  }
+
+  bool selected_ = false;
+};
+
+BEGIN_METADATA(IntentPickerAppGridButton, views::Button)
+END_METADATA
+
+// Displays a list of apps as a grid of buttons.
+class IntentPickerAppGridView
+    : public IntentPickerBubbleView::IntentPickerAppsView {
+ public:
+  METADATA_HEADER(IntentPickerAppGridView);
+
+  IntentPickerAppGridView(
+      const std::vector<IntentPickerBubbleView::AppInfo>& apps,
+      AppSelectedCallback selected_callback)
+      : selected_callback_(selected_callback) {
+    auto table_view = std::make_unique<views::TableLayoutView>();
+    table_view->SetID(IntentPickerBubbleView::ViewId::kItemContainer);
+
+    table_view->AddPaddingColumn(views::TableLayout::kFixedSize,
+                                 kGridExteriorColumnPadding);
+    for (int i = 0; i < kGridItemsPerRow; i++) {
+      table_view->AddColumn(views::LayoutAlignment::kCenter,
+                            views::LayoutAlignment::kStart,
+                            views::TableLayout::kFixedSize,
+                            views::TableLayout::ColumnSize::kUsePreferred,
+                            /*fixed_width=*/0, /*min_width=*/0);
+      if (i < kGridItemsPerRow - 1) {
+        table_view->AddPaddingColumn(views::TableLayout::kFixedSize,
+                                     kGridInteriorColumnPadding);
+      }
+    }
+    table_view->AddPaddingColumn(views::TableLayout::kFixedSize,
+                                 kGridExteriorColumnPadding);
+
+    // Add padding to the exterior of the grid so that the focus ring on app
+    // items is not clipped.
+    constexpr int kFocusRingPadding = views::FocusRing::kDefaultHaloInset +
+                                      views::FocusRing::kDefaultHaloThickness;
+
+    int row_count = (apps.size() - 1) / kGridItemsPerRow + 1;
+    table_view->AddPaddingRow(views::TableLayout::kFixedSize,
+                              kFocusRingPadding);
+    for (int i = 0; i < row_count; i++) {
+      table_view->AddRows(1, views::TableLayout::kFixedSize);
+      if (i < row_count - 1) {
+        table_view->AddPaddingRow(views::TableLayout::kFixedSize,
+                                  kGridInteriorRowPadding);
+      }
+    }
+    table_view->AddPaddingRow(views::TableLayout::kFixedSize,
+                              kFocusRingPadding);
+
+    for (size_t i = 0; i < apps.size(); i++) {
+      auto app_button = std::make_unique<IntentPickerAppGridButton>(
+          base::BindRepeating(&IntentPickerAppGridView::OnAppPressed,
+                              base::Unretained(this), i),
+          apps[i].icon_model, apps[i].display_name);
+      table_view->AddChildView(std::move(app_button));
+    }
+
+    SetContents(std::move(table_view));
+    // Clip height so that at most two rows are visible, with a peek of the
+    // third if it exists.
+    ClipHeightTo(kGridItemPreferredSize, kGridItemPreferredSize * 2.5f);
+  }
+
+  void SetSelectedIndex(size_t index) override {
+    SetSelectedIndexInternal(index, false);
+  }
+
+  size_t GetSelectedIndex() const override { return selected_app_index_; }
+
+ private:
+  void OnAppPressed(size_t index, const ui::Event& event) {
+    SetSelectedIndexInternal(index, IsDoubleClick(event));
+  }
+
+  void SetSelectedIndexInternal(size_t index, bool accepted) {
+    GetButtonAtIndex(selected_app_index_)->SetSelected(false);
+    selected_app_index_ = index;
+    GetButtonAtIndex(selected_app_index_)->SetSelected(true);
+    selected_callback_.Run(index, accepted);
+  }
+
+  IntentPickerAppGridButton* GetButtonAtIndex(size_t index) {
+    const auto& children = contents()->children();
+    return static_cast<IntentPickerAppGridButton*>(children[index]);
+  }
+
+  AppSelectedCallback selected_callback_;
+
+  size_t selected_app_index_ = 0;
+};
+
+BEGIN_METADATA(IntentPickerAppGridView, views::ScrollView)
+ADD_PROPERTY_METADATA(size_t, SelectedIndex)
+END_METADATA
+
+// List view:
 
 // A button that represents a candidate intent handler.
 class IntentPickerLabelButton : public views::LabelButton {
@@ -108,7 +304,7 @@
 
   IntentPickerAppListView(
       const std::vector<IntentPickerBubbleView::AppInfo>& apps,
-      base::RepeatingCallback<void(size_t, bool)> selected_callback)
+      AppSelectedCallback selected_callback)
       : selected_callback_(selected_callback) {
     auto scrollable_view = std::make_unique<views::View>();
     scrollable_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -185,10 +381,7 @@
     GetIntentPickerLabelButtonAt(selected_app_index_)->MarkAsSelected(event);
 
     bool accepted = false;
-    if (event && ((event->IsMouseEvent() &&
-                   event->AsMouseEvent()->GetClickCount() == 2) ||
-                  (event->IsGestureEvent() &&
-                   event->AsGestureEvent()->details().tap_count() == 2))) {
+    if (event && IsDoubleClick(*event)) {
       accepted = true;
     }
 
@@ -215,7 +408,7 @@
     return static_cast<IntentPickerLabelButton*>(children[index]);
   }
 
-  base::RepeatingCallback<void(size_t, bool)> selected_callback_;
+  AppSelectedCallback selected_callback_;
 
   size_t selected_app_index_ = 0;
 };
@@ -406,6 +599,7 @@
   const auto* provider = ChromeLayoutProvider::Get();
   auto insets = provider->GetDialogInsetsForContentType(leading_content_type,
                                                         trailing_content_type);
+
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical,
       gfx::Insets::TLBR(insets.top(), 0, insets.bottom(), 0),
@@ -434,10 +628,15 @@
   }
 
   // Create a container for all of the individual app views.
-  auto list_view = std::make_unique<IntentPickerAppListView>(
-      app_info_, base::BindRepeating(&IntentPickerBubbleView::OnAppSelected,
-                                     base::Unretained(this)));
-  apps_view_ = AddChildView(std::move(list_view));
+  if (use_grid_view_) {
+    apps_view_ = AddChildView(std::make_unique<IntentPickerAppGridView>(
+        app_info_, base::BindRepeating(&IntentPickerBubbleView::OnAppSelected,
+                                       base::Unretained(this))));
+  } else {
+    apps_view_ = AddChildView(std::make_unique<IntentPickerAppListView>(
+        app_info_, base::BindRepeating(&IntentPickerBubbleView::OnAppSelected,
+                                       base::Unretained(this))));
+  }
 
   if (show_origin) {
     std::u16string origin_text = l10n_util::GetStringFUTF16(
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
index b19b106..be7987b2 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
@@ -406,12 +406,12 @@
 
     std::vector<apps::IntentPickerAppInfo> app_info;
     const auto add_entry = [&app_info](const std::string& str) {
+      auto icon_size = apps::GetIntentPickerBubbleIconSize();
       app_info.emplace_back(
           apps::PickerEntryType::kUnknown,
           ui::ImageModel::FromImage(
               gfx::Image::CreateFrom1xBitmap(favicon::GenerateMonogramFavicon(
-                  GURL("https://" + str + ".com"), gfx::kFaviconSize,
-                  gfx::kFaviconSize))),
+                  GURL("https://" + str + ".com"), icon_size, icon_size))),
           "Launch name " + str, "Display name " + str);
     };
     add_entry("a");
@@ -437,7 +437,13 @@
       animation_mode_reset_;
 };
 
-IN_PROC_BROWSER_TEST_F(IntentPickerDialogTest, InvokeUi_default) {
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+// Flaky on Mac and Win. See https://crbug.com/1330302.
+#define MAYBE_InvokeUi_default DISABLED_InvokeUi_default
+#else
+#define MAYBE_InvokeUi_default InvokeUi_default
+#endif
+IN_PROC_BROWSER_TEST_F(IntentPickerDialogTest, MAYBE_InvokeUi_default) {
   ShowAndVerifyUi();
 }
 
@@ -458,5 +464,6 @@
 };
 
 IN_PROC_BROWSER_TEST_F(IntentPickerDialogGridViewTest, InvokeUi_default) {
+  set_baseline("3652664");
   ShowAndVerifyUi();
 }
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
index 117a6b7..83ab137 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
@@ -11,8 +11,11 @@
 #include "base/callback_helpers.h"
 #include "base/feature_list.h"
 #include "base/memory/raw_ptr.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/intent_helper/apps_navigation_types.h"
+#include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_helpers.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/test_with_browser_view.h"
@@ -61,7 +64,8 @@
 
   void TearDown() override {
     // Make sure the bubble is destroyed before the profile to avoid a crash.
-    bubble_->GetWidget()->CloseNow();
+    if (bubble_)
+      bubble_->GetWidget()->CloseNow();
 
     TestWithBrowserView::TearDown();
   }
@@ -165,7 +169,7 @@
     last_selection_should_persist_ = should_persist;
   }
 
-  raw_ptr<IntentPickerBubbleView> bubble_;
+  raw_ptr<IntentPickerBubbleView> bubble_ = nullptr;
   raw_ptr<views::View> anchor_view_;
   std::vector<AppInfo> app_info_;
   std::unique_ptr<ui::test::EventGenerator> event_generator_;
@@ -325,7 +329,29 @@
   EXPECT_EQ(children_without_origin, children_with_same_origin);
 }
 
-TEST_F(IntentPickerBubbleViewTest, RememberCheckbox) {
+enum class BubbleInterfaceType { kListView, kGridView };
+
+class IntentPickerBubbleViewLayoutTest
+    : public IntentPickerBubbleViewTest,
+      public ::testing::WithParamInterface<BubbleInterfaceType> {
+ public:
+  void SetUp() override {
+    if (GetParam() == BubbleInterfaceType::kGridView) {
+      feature_list_.InitAndEnableFeature(
+          apps::features::kLinkCapturingUiUpdate);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          apps::features::kLinkCapturingUiUpdate);
+    }
+
+    IntentPickerBubbleViewTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_P(IntentPickerBubbleViewLayoutTest, RememberCheckbox) {
   AddApp(apps::PickerEntryType::kDevice, "device_id", "Android Phone");
   AddApp(apps::PickerEntryType::kWeb, "web_app_id", "Web App");
   AddApp(apps::PickerEntryType::kArc, "arc_app_id", "Arc App");
@@ -350,7 +376,7 @@
   ASSERT_TRUE(checkbox->GetEnabled());
 }
 
-TEST_F(IntentPickerBubbleViewTest, AcceptDialog) {
+TEST_P(IntentPickerBubbleViewLayoutTest, AcceptDialog) {
   AddApp(apps::PickerEntryType::kWeb, "web_app_id_1", "Web App");
   AddApp(apps::PickerEntryType::kWeb, "web_app_id_2", "Web App");
   CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false,
@@ -365,7 +391,7 @@
   EXPECT_EQ(last_close_reason_, apps::IntentPickerCloseReason::OPEN_APP);
 }
 
-TEST_F(IntentPickerBubbleViewTest, AcceptDialogWithRememberSelection) {
+TEST_P(IntentPickerBubbleViewLayoutTest, AcceptDialogWithRememberSelection) {
   AddApp(apps::PickerEntryType::kArc, "arc_app_id", "ARC App");
   CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true,
                    BubbleType::kLinkCapturing,
@@ -382,7 +408,7 @@
   EXPECT_EQ(last_close_reason_, apps::IntentPickerCloseReason::OPEN_APP);
 }
 
-TEST_F(IntentPickerBubbleViewTest, CancelDialog) {
+TEST_P(IntentPickerBubbleViewLayoutTest, CancelDialog) {
   AddApp(apps::PickerEntryType::kWeb, "web_app_id_1", "Web App");
   AddApp(apps::PickerEntryType::kWeb, "web_app_id_2", "Web App");
   CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true,
@@ -397,7 +423,7 @@
   EXPECT_EQ(last_close_reason_, apps::IntentPickerCloseReason::STAY_IN_CHROME);
 }
 
-TEST_F(IntentPickerBubbleViewTest, CloseDialog) {
+TEST_P(IntentPickerBubbleViewLayoutTest, CloseDialog) {
   AddApp(apps::PickerEntryType::kWeb, "web_app_id_1", "Web App");
   CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false,
                    BubbleType::kLinkCapturing,
@@ -410,7 +436,16 @@
             apps::IntentPickerCloseReason::DIALOG_DEACTIVATED);
 }
 
-TEST_F(IntentPickerBubbleViewTest, KeyboardNavigation) {
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_KeyboardNavigation DISABLED_KeyboardNavigation
+#else
+#define MAYBE_KeyboardNavigation KeyboardNavigation
+#endif
+TEST_P(IntentPickerBubbleViewLayoutTest, MAYBE_KeyboardNavigation) {
+  if (GetParam() == BubbleInterfaceType::kGridView) {
+    // TODO(crbug.com/1321501): Support keyboard navigation in grid view.
+    GTEST_SKIP();
+  }
   CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false,
                    BubbleType::kLinkCapturing,
                    /*initiating_origin=*/absl::nullopt);
@@ -431,7 +466,7 @@
   EXPECT_EQ(bubble_->GetSelectedIndex(), 0u);
 }
 
-TEST_F(IntentPickerBubbleViewTest, DoubleClickToAccept) {
+TEST_P(IntentPickerBubbleViewLayoutTest, DoubleClickToAccept) {
   AddApp(apps::PickerEntryType::kWeb, "web_app_id", "Web App");
   CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false,
                    BubbleType::kLinkCapturing,
@@ -444,3 +479,8 @@
   EXPECT_EQ(last_selected_launch_name_, "web_app_id");
   EXPECT_EQ(last_close_reason_, apps::IntentPickerCloseReason::OPEN_APP);
 }
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         IntentPickerBubbleViewLayoutTest,
+                         testing::Values(BubbleInterfaceType::kListView,
+                                         BubbleInterfaceType::kGridView));
diff --git a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
index bbbf873..1fbdc58 100644
--- a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
+++ b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
@@ -806,9 +806,7 @@
   EXPECT_EQ(1, browser()->tab_strip_model()->count());
 
   // Verifies the tab has an associated tab helper for System App's AppId.
-  auto* tab_helper = web_app::WebAppTabHelper::FromWebContents(web_contents);
-  EXPECT_TRUE(tab_helper);
-  EXPECT_EQ(tab_helper->GetAppId(),
+  EXPECT_EQ(*web_app::WebAppTabHelper::GetAppId(web_contents),
             *web_app::GetAppIdForSystemWebApp(browser()->profile(),
                                               GetMockAppType()));
 }
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
index 6f9a32da..372ddc8 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -223,9 +223,7 @@
               apps::mojom::LaunchSource::kFromTest));
   DCHECK(web_contents);
 
-  WebAppTabHelper* tab_helper = WebAppTabHelper::FromWebContents(web_contents);
-  DCHECK(tab_helper);
-  EXPECT_EQ(app_id, tab_helper->GetAppId());
+  EXPECT_EQ(app_id, *WebAppTabHelper::GetAppId(web_contents));
 
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
   EXPECT_EQ(browser, chrome::FindLastActive());
diff --git a/chrome/browser/ui/web_applications/web_app_metrics.cc b/chrome/browser/ui/web_applications/web_app_metrics.cc
index 19890d7..1e7427b 100644
--- a/chrome/browser/ui/web_applications/web_app_metrics.cc
+++ b/chrome/browser/ui/web_applications/web_app_metrics.cc
@@ -119,18 +119,15 @@
 
   // A presence of WebAppTabHelper with valid app_id indicates an installed
   // web app.
-  WebAppTabHelper* tab_helper = WebAppTabHelper::FromWebContents(web_contents);
-  if (!tab_helper)
-    return;
-  AppId app_id = tab_helper->GetAppId();
-  if (app_id.empty())
+  const AppId* app_id = WebAppTabHelper::GetAppId(web_contents);
+  if (!app_id)
     return;
 
   // No HostedAppBrowserController if app is running as a tab in common browser.
   const bool in_window = !!browser->app_controller();
   const bool user_installed = WebAppProvider::GetForLocalAppsUnchecked(profile_)
                                   ->registrar()
-                                  .WasInstalledByUser(app_id);
+                                  .WasInstalledByUser(*app_id);
 
   // Record all web apps:
   RecordTabOrWindowHistogram("WebApp.Engagement", in_window, engagement_type);
@@ -208,9 +205,9 @@
          change.GetRemove()->contents) {
       if (contents.remove_reason ==
           TabStripModelChange::RemoveReason::kDeleted) {
-        auto* tab_helper = WebAppTabHelper::FromWebContents(contents.contents);
-        if (tab_helper && !tab_helper->GetAppId().empty())
-          app_last_interacted_time_.erase(tab_helper->GetAppId());
+        const AppId* app_id = WebAppTabHelper::GetAppId(contents.contents);
+        if (app_id)
+          app_last_interacted_time_.erase(*app_id);
         // Newly-selected foreground contents should not be going away.
         if (contents.contents == foreground_web_contents_) {
           base::debug::DumpWithoutCrashing();
@@ -226,12 +223,10 @@
 void WebAppMetrics::OnSuspend() {
   // Update current tab as foreground time.
   if (foreground_web_contents_) {
-    auto* tab_helper =
-        WebAppTabHelper::FromWebContents(foreground_web_contents_);
-    if (tab_helper && !tab_helper->GetAppId().empty() &&
-        app_last_interacted_time_.contains(tab_helper->GetAppId())) {
+    const AppId* app_id = WebAppTabHelper::GetAppId(foreground_web_contents_);
+    if (app_id && app_last_interacted_time_.contains(*app_id)) {
       UpdateUkmData(foreground_web_contents_, TabSwitching::kFrom);
-      app_last_interacted_time_.erase(tab_helper->GetAppId());
+      app_last_interacted_time_.erase(*app_id);
     }
   }
   // Update all other tabs as background time.
@@ -241,9 +236,8 @@
     for (int i = 0; i < tab_count; i++) {
       WebContents* contents = browser->tab_strip_model()->GetWebContentsAt(i);
       DCHECK(contents);
-      auto* tab_helper = WebAppTabHelper::FromWebContents(contents);
-      if (tab_helper && !tab_helper->GetAppId().empty() &&
-          app_last_interacted_time_.contains(tab_helper->GetAppId())) {
+      const AppId* app_id = WebAppTabHelper::GetAppId(contents);
+      if (app_id && app_last_interacted_time_.contains(*app_id)) {
         UpdateUkmData(contents, TabSwitching::kBackgroundClosing);
       }
     }
@@ -251,13 +245,15 @@
   app_last_interacted_time_.clear();
 }
 
-void WebAppMetrics::NotifyOnAssociatedAppChanged(WebContents* web_contents,
-                                                 const AppId& previous_app_id,
-                                                 const AppId& new_app_id) {
+void WebAppMetrics::NotifyOnAssociatedAppChanged(
+    content::WebContents* web_contents,
+    const absl::optional<AppId>& previous_app_id,
+    const absl::optional<AppId>& new_app_id) {
   // Ensure we aren't counting closed app as still open.
   // TODO (crbug.com/1081187): If there were multiple app instances open, this
   // will prevent background time being counted until the app is next active.
-  app_last_interacted_time_.erase(previous_app_id);
+  if (previous_app_id.has_value())
+    app_last_interacted_time_.erase(previous_app_id.value());
   // Don't record any UKM data here. It will be recorded in
   // |NotifyInstallableWebAppStatusUpdated| once fully fetched.
 }
@@ -326,19 +322,17 @@
     return;
   DailyInteraction features;
 
-  auto* tab_helper = WebAppTabHelper::FromWebContents(web_contents);
-  if (tab_helper &&
-      provider->registrar().IsLocallyInstalled(tab_helper->GetAppId())) {
+  const AppId* app_id = WebAppTabHelper::GetAppId(web_contents);
+  if (app_id && provider->registrar().IsLocallyInstalled(*app_id)) {
     // App is installed
-    const AppId& app_id = tab_helper->GetAppId();
-    features.start_url = provider->registrar().GetAppStartUrl(app_id);
+    features.start_url = provider->registrar().GetAppStartUrl(*app_id);
     features.installed = true;
     auto install_source =
-        provider->registrar().GetAppInstallSourceForMetrics(app_id);
+        provider->registrar().GetAppInstallSourceForMetrics(*app_id);
     if (install_source)
       features.install_source = static_cast<int>(*install_source);
     DisplayMode display_mode =
-        provider->registrar().GetAppEffectiveDisplayMode(app_id);
+        provider->registrar().GetAppEffectiveDisplayMode(*app_id);
     features.effective_display_mode = static_cast<int>(display_mode);
     // AppBannerManager treats already-installed web-apps as non-promotable, so
     // include already-installed findings as promotable.
@@ -349,8 +343,8 @@
     if (provider->ui_manager().IsInAppWindow(web_contents) ||
         mode == TabSwitching::kForegroundClosing) {
       base::Time now = base::Time::Now();
-      if (app_last_interacted_time_.contains(app_id)) {
-        base::TimeDelta delta = now - app_last_interacted_time_[app_id];
+      if (app_last_interacted_time_.contains(*app_id)) {
+        base::TimeDelta delta = now - app_last_interacted_time_[*app_id];
         if (delta < max_valid_session_delta_) {
           switch (mode) {
             case TabSwitching::kFrom:
@@ -364,7 +358,7 @@
           }
         }
       }
-      app_last_interacted_time_[app_id] = now;
+      app_last_interacted_time_[*app_id] = now;
 
       // Note: real web app launch counts 2 sessions immediately, as app window
       // is actually activated twice in the launch process.
diff --git a/chrome/browser/ui/web_applications/web_app_metrics.h b/chrome/browser/ui/web_applications/web_app_metrics.h
index 87027917..71ec325a 100644
--- a/chrome/browser/ui/web_applications/web_app_metrics.h
+++ b/chrome/browser/ui/web_applications/web_app_metrics.h
@@ -63,9 +63,10 @@
   void OnSuspend() override;
 
   // Called when a web contents changes associated AppId (may be empty).
-  void NotifyOnAssociatedAppChanged(content::WebContents* web_contents,
-                                    const AppId& previous_app_id,
-                                    const AppId& new_app_id);
+  void NotifyOnAssociatedAppChanged(
+      content::WebContents* web_contents,
+      const absl::optional<AppId>& previous_app_id,
+      const absl::optional<AppId>& new_app_id);
 
   // Notify WebAppMetrics that an installability check has been completed for
   // a WebContents (see AppBannerManager::OnInstallableWebAppStatusUpdated).
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
index 25ee954..d8a801b 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
@@ -389,8 +389,8 @@
 
 void WebAppUiManagerImpl::NotifyOnAssociatedAppChanged(
     content::WebContents* web_contents,
-    const AppId& previous_app_id,
-    const AppId& new_app_id) const {
+    const absl::optional<AppId>& previous_app_id,
+    const absl::optional<AppId>& new_app_id) const {
   WebAppMetrics* web_app_metrics = WebAppMetrics::Get(profile_);
   // Unavailable in guest sessions.
   if (!web_app_metrics)
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h
index 48af10de..db2acba 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h
@@ -55,9 +55,10 @@
   bool IsAppInQuickLaunchBar(const AppId& app_id) const override;
   bool IsInAppWindow(content::WebContents* web_contents,
                      const AppId* app_id) const override;
-  void NotifyOnAssociatedAppChanged(content::WebContents* web_contents,
-                                    const AppId& previous_app_id,
-                                    const AppId& new_app_id) const override;
+  void NotifyOnAssociatedAppChanged(
+      content::WebContents* web_contents,
+      const absl::optional<AppId>& previous_app_id,
+      const absl::optional<AppId>& new_app_id) const override;
   bool CanReparentAppTabToWindow(const AppId& app_id,
                                  bool shortcut_created) const override;
   void ReparentAppTabToWindow(content::WebContents* contents,
diff --git a/chrome/browser/ui/web_applications/web_app_ui_utils.cc b/chrome/browser/ui/web_applications/web_app_ui_utils.cc
index ac708a2..ae29e64 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_utils.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_utils.cc
@@ -53,17 +53,21 @@
     return absl::nullopt;
   }
 
-  WebAppTabHelper* helper = WebAppTabHelper::FromWebContents(web_contents);
-  if (!helper || helper->GetAppId().empty() || !helper->acting_as_app())
+  const web_app::AppId* app_id =
+      web_app::WebAppTabHelper::GetAppId(web_contents);
+  if (!app_id)
+    return absl::nullopt;
+
+  if (!web_app::WebAppTabHelper::FromWebContents(web_contents)->acting_as_app())
     return absl::nullopt;
 
   if (!WebAppProvider::GetForWebApps(browser->profile())
            ->registrar()
-           .IsInstalled(helper->GetAppId())) {
+           .IsInstalled(*app_id)) {
     return absl::nullopt;
   }
 
-  return helper->GetAppId();
+  return *app_id;
 }
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/web_applications/manifest_update_manager.cc b/chrome/browser/web_applications/manifest_update_manager.cc
index 036c2350..6ed94b8 100644
--- a/chrome/browser/web_applications/manifest_update_manager.cc
+++ b/chrome/browser/web_applications/manifest_update_manager.cc
@@ -59,43 +59,43 @@
 }
 
 void ManifestUpdateManager::MaybeUpdate(const GURL& url,
-                                        const AppId& app_id,
+                                        const absl::optional<AppId>& app_id,
                                         content::WebContents* web_contents) {
   if (!started_) {
     return;
   }
 
-  if (app_id.empty() || !registrar_->IsLocallyInstalled(app_id)) {
+  if (!app_id.has_value() || !registrar_->IsLocallyInstalled(*app_id)) {
     NotifyResult(url, app_id, ManifestUpdateResult::kNoAppInScope);
     return;
   }
 
-  if (IsSystemWebApp(*registrar_, *system_web_apps_delegate_map_, app_id)) {
-    NotifyResult(url, app_id, ManifestUpdateResult::kAppIsSystemWebApp);
+  if (IsSystemWebApp(*registrar_, *system_web_apps_delegate_map_, *app_id)) {
+    NotifyResult(url, *app_id, ManifestUpdateResult::kAppIsSystemWebApp);
     return;
   }
 
-  if (registrar_->IsPlaceholderApp(app_id, WebAppManagement::kPolicy)) {
-    NotifyResult(url, app_id, ManifestUpdateResult::kAppIsPlaceholder);
+  if (registrar_->IsPlaceholderApp(*app_id, WebAppManagement::kPolicy)) {
+    NotifyResult(url, *app_id, ManifestUpdateResult::kAppIsPlaceholder);
     return;
   }
 
-  if (base::Contains(tasks_, app_id))
+  if (base::Contains(tasks_, *app_id))
     return;
 
-  if (!MaybeConsumeUpdateCheck(url.DeprecatedGetOriginAsURL(), app_id)) {
-    NotifyResult(url, app_id, ManifestUpdateResult::kThrottled);
+  if (!MaybeConsumeUpdateCheck(url.DeprecatedGetOriginAsURL(), *app_id)) {
+    NotifyResult(url, *app_id, ManifestUpdateResult::kThrottled);
     return;
   }
 
   tasks_.insert_or_assign(
-      app_id, std::make_unique<ManifestUpdateTask>(
-                  url, app_id, web_contents,
-                  base::BindOnce(&ManifestUpdateManager::OnUpdateStopped,
-                                 base::Unretained(this)),
-                  hang_update_checks_for_testing_, *registrar_, *icon_manager_,
-                  ui_manager_, install_finalizer_, *os_integration_manager_,
-                  sync_bridge_));
+      *app_id, std::make_unique<ManifestUpdateTask>(
+                   url, *app_id, web_contents,
+                   base::BindOnce(&ManifestUpdateManager::OnUpdateStopped,
+                                  base::Unretained(this)),
+                   hang_update_checks_for_testing_, *registrar_, *icon_manager_,
+                   ui_manager_, install_finalizer_, *os_integration_manager_,
+                   sync_bridge_));
 }
 
 bool ManifestUpdateManager::IsUpdateConsumed(const AppId& app_id) {
@@ -167,14 +167,15 @@
 }
 
 void ManifestUpdateManager::NotifyResult(const GURL& url,
-                                         const AppId& app_id,
+                                         const absl::optional<AppId>& app_id,
                                          ManifestUpdateResult result) {
   // Don't log kNoAppInScope because it will be far too noisy (most page loads
   // will hit it).
   if (result != ManifestUpdateResult::kNoAppInScope) {
     base::UmaHistogramEnumeration("Webapp.Update.ManifestUpdateResult", result);
-    if (registrar_->HasExternalAppWithInstallSource(
-            app_id, ExternalInstallSource::kExternalDefault)) {
+    if (app_id.has_value() &&
+        registrar_->HasExternalAppWithInstallSource(
+            app_id.value(), ExternalInstallSource::kExternalDefault)) {
       base::UmaHistogramEnumeration(
           "Webapp.Update.ManifestUpdateResult.DefaultApp", result);
     }
diff --git a/chrome/browser/web_applications/manifest_update_manager.h b/chrome/browser/web_applications/manifest_update_manager.h
index 29f5464..a87ca01 100644
--- a/chrome/browser/web_applications/manifest_update_manager.h
+++ b/chrome/browser/web_applications/manifest_update_manager.h
@@ -61,7 +61,7 @@
   void Shutdown();
 
   void MaybeUpdate(const GURL& url,
-                   const AppId& app_id,
+                   const absl::optional<AppId>& app_id,
                    content::WebContents* web_contents);
   bool IsUpdateConsumed(const AppId& app_id);
 
@@ -92,7 +92,7 @@
   void OnUpdateStopped(const ManifestUpdateTask& task,
                        ManifestUpdateResult result);
   void NotifyResult(const GURL& url,
-                    const AppId& app_id,
+                    const absl::optional<AppId>& app_id,
                     ManifestUpdateResult result);
 
   raw_ptr<WebAppRegistrar> registrar_ = nullptr;
diff --git a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc
index 25765e9f..c206ffdf 100644
--- a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc
@@ -1267,7 +1267,8 @@
   WaitForTestSystemAppInstall();
 
   std::unique_ptr<content::WebContents> web_contents = CreateTestWebContents();
-  WebAppTabHelper tab_helper(web_contents.get());
+  WebAppTabHelper::CreateForWebContents(web_contents.get());
+  auto& tab_helper = *WebAppTabHelper::FromWebContents(web_contents.get());
 
   // Simulate when first navigating into app's launch url.
   {
@@ -1276,7 +1277,8 @@
     mock_nav_handle.set_is_same_document(false);
     EXPECT_CALL(mock_nav_handle, ForceEnableOriginTrials(main_url_trials_));
     tab_helper.ReadyToCommitNavigation(&mock_nav_handle);
-    ASSERT_EQ(maybe_installation_->GetAppId(), tab_helper.GetAppId());
+    ASSERT_EQ(maybe_installation_->GetAppId(),
+              *WebAppTabHelper::GetAppId(web_contents.get()));
   }
 
   // Simulate loading app's embedded child-frame that has origin trials.
@@ -1303,7 +1305,8 @@
   WaitForTestSystemAppInstall();
 
   std::unique_ptr<content::WebContents> web_contents = CreateTestWebContents();
-  WebAppTabHelper tab_helper(web_contents.get());
+  WebAppTabHelper::CreateForWebContents(web_contents.get());
+  auto& tab_helper = *WebAppTabHelper::FromWebContents(web_contents.get());
 
   // Simulate when first navigating into app's launch url.
   {
@@ -1312,7 +1315,8 @@
     mock_nav_handle.set_is_same_document(false);
     EXPECT_CALL(mock_nav_handle, ForceEnableOriginTrials(main_url_trials_));
     tab_helper.ReadyToCommitNavigation(&mock_nav_handle);
-    ASSERT_EQ(maybe_installation_->GetAppId(), tab_helper.GetAppId());
+    ASSERT_EQ(maybe_installation_->GetAppId(),
+              *WebAppTabHelper::GetAppId(web_contents.get()));
   }
 
   // Simulate same-document navigation.
@@ -1336,7 +1340,8 @@
   WaitForTestSystemAppInstall();
 
   std::unique_ptr<content::WebContents> web_contents = CreateTestWebContents();
-  WebAppTabHelper tab_helper(web_contents.get());
+  WebAppTabHelper::CreateForWebContents(web_contents.get());
+  auto& tab_helper = *WebAppTabHelper::FromWebContents(web_contents.get());
 
   // Simulate when first navigating into app's launch url.
   {
@@ -1345,7 +1350,8 @@
     mock_nav_handle.set_is_same_document(false);
     EXPECT_CALL(mock_nav_handle, ForceEnableOriginTrials(main_url_trials_));
     tab_helper.ReadyToCommitNavigation(&mock_nav_handle);
-    ASSERT_EQ(maybe_installation_->GetAppId(), tab_helper.GetAppId());
+    ASSERT_EQ(maybe_installation_->GetAppId(),
+              *WebAppTabHelper::GetAppId(web_contents.get()));
   }
 
   // Simulate navigating to a different site without origin trials.
@@ -1355,17 +1361,18 @@
     mock_nav_handle.set_is_same_document(false);
     EXPECT_CALL(mock_nav_handle, ForceEnableOriginTrials).Times(0);
     tab_helper.ReadyToCommitNavigation(&mock_nav_handle);
-    ASSERT_EQ("", tab_helper.GetAppId());
+    ASSERT_EQ(nullptr, WebAppTabHelper::GetAppId(web_contents.get()));
   }
 
-  // Simulatenavigating back to a SWA with origin trials.
+  // Simulate navigating back to a SWA with origin trials.
   {
     content::MockNavigationHandle mock_nav_handle(main_url_, nullptr);
     mock_nav_handle.set_is_in_primary_main_frame(true);
     mock_nav_handle.set_is_same_document(false);
     EXPECT_CALL(mock_nav_handle, ForceEnableOriginTrials(main_url_trials_));
     tab_helper.ReadyToCommitNavigation(&mock_nav_handle);
-    ASSERT_EQ(maybe_installation_->GetAppId(), tab_helper.GetAppId());
+    ASSERT_EQ(maybe_installation_->GetAppId(),
+              *WebAppTabHelper::GetAppId(web_contents.get()));
   }
 
   // Simulate navigating the main frame to a url embedded by SWA. This url has
@@ -1377,7 +1384,7 @@
     mock_nav_handle.set_is_same_document(false);
     EXPECT_CALL(mock_nav_handle, ForceEnableOriginTrials).Times(0);
     tab_helper.ReadyToCommitNavigation(&mock_nav_handle);
-    ASSERT_EQ("", tab_helper.GetAppId());
+    ASSERT_EQ(nullptr, WebAppTabHelper::GetAppId(web_contents.get()));
   }
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/web_applications/test/fake_web_app_ui_manager.h b/chrome/browser/web_applications/test/fake_web_app_ui_manager.h
index 6e72f257..d005fb7 100644
--- a/chrome/browser/web_applications/test/fake_web_app_ui_manager.h
+++ b/chrome/browser/web_applications/test/fake_web_app_ui_manager.h
@@ -38,9 +38,10 @@
   bool IsAppInQuickLaunchBar(const AppId& app_id) const override;
   bool IsInAppWindow(content::WebContents* web_contents,
                      const AppId* app_id) const override;
-  void NotifyOnAssociatedAppChanged(content::WebContents* web_contents,
-                                    const AppId& previous_app_id,
-                                    const AppId& new_app_id) const override {}
+  void NotifyOnAssociatedAppChanged(
+      content::WebContents* web_contents,
+      const absl::optional<AppId>& previous_app_id,
+      const absl::optional<AppId>& new_app_id) const override {}
   bool CanReparentAppTabToWindow(const AppId& app_id,
                                  bool shortcut_created) const override;
   void ReparentAppTabToWindow(content::WebContents* contents,
diff --git a/chrome/browser/web_applications/web_app_tab_helper.cc b/chrome/browser/web_applications/web_app_tab_helper.cc
index 1d86f77..3bee865c 100644
--- a/chrome/browser/web_applications/web_app_tab_helper.cc
+++ b/chrome/browser/web_applications/web_app_tab_helper.cc
@@ -31,6 +31,14 @@
   }
 }
 
+const AppId* WebAppTabHelper::GetAppId(content::WebContents* web_contents) {
+  auto* tab_helper = WebAppTabHelper::FromWebContents(web_contents);
+  if (!tab_helper)
+    return nullptr;
+  return tab_helper->app_id_.has_value() ? &tab_helper->app_id_.value()
+                                         : nullptr;
+}
+
 WebAppTabHelper::WebAppTabHelper(content::WebContents* web_contents)
     : content::WebContentsUserData<WebAppTabHelper>(*web_contents),
       content::WebContentsObserver(web_contents),
@@ -39,15 +47,11 @@
   DCHECK(provider_);
   observation_.Observe(&provider_->install_manager());
   SetAppId(
-      FindAppIdWithUrlInScope(web_contents->GetSiteInstance()->GetSiteURL()));
+      FindAppWithUrlInScope(web_contents->GetSiteInstance()->GetSiteURL()));
 }
 
 WebAppTabHelper::~WebAppTabHelper() = default;
 
-const AppId& WebAppTabHelper::GetAppId() const {
-  return app_id_;
-}
-
 const base::UnguessableToken& WebAppTabHelper::GetAudioFocusGroupIdForTesting()
     const {
   return audio_focus_group_id_;
@@ -61,14 +65,16 @@
   return *launch_queue_;
 }
 
-void WebAppTabHelper::SetAppId(const AppId& app_id) {
-  DCHECK(app_id.empty() || provider_->registrar().IsInstalled(app_id) ||
-         provider_->registrar().IsUninstalling(app_id));
+void WebAppTabHelper::SetAppId(absl::optional<AppId> app_id) {
+  // Empty string should not be used to indicate "no app ID".
+  DCHECK(!app_id || !app_id->empty());
+  DCHECK(!app_id || provider_->registrar().IsInstalled(*app_id) ||
+         provider_->registrar().IsUninstalling(*app_id));
   if (app_id_ == app_id)
     return;
 
-  AppId previous_app_id = app_id_;
-  app_id_ = app_id;
+  absl::optional<AppId> previous_app_id = std::move(app_id_);
+  app_id_ = std::move(app_id);
 
   OnAssociatedAppChanged(previous_app_id, app_id_);
 }
@@ -77,8 +83,7 @@
     content::NavigationHandle* navigation_handle) {
   if (navigation_handle->IsInPrimaryMainFrame()) {
     const GURL& url = navigation_handle->GetURL();
-    const AppId app_id = FindAppIdWithUrlInScope(url);
-    SetAppId(app_id);
+    SetAppId(FindAppWithUrlInScope(url));
   }
 
   // If navigating to a System Web App (including navigation in sub frames), let
@@ -88,8 +93,9 @@
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   auto* swa_manager =
       ash::SystemWebAppManager::GetForLocalAppsUnchecked(profile);
-  if (swa_manager && swa_manager->IsSystemWebApp(GetAppId())) {
-    swa_manager->OnReadyToCommitNavigation(GetAppId(), navigation_handle);
+  if (app_id_.has_value() && swa_manager &&
+      swa_manager->IsSystemWebApp(app_id_.value())) {
+    swa_manager->OnReadyToCommitNavigation(app_id_.value(), navigation_handle);
   }
 }
 
@@ -115,7 +121,7 @@
   // See the declaration of WebContentsObserver::PrimaryPageChanged for more
   // information.
   provider_->manifest_update_manager().MaybeUpdate(
-      page.GetMainDocument().GetLastCommittedURL(), GetAppId(), web_contents());
+      page.GetMainDocument().GetLastCommittedURL(), app_id_, web_contents());
 
   ReinstallPlaceholderAppIfNecessary(
       page.GetMainDocument().GetLastCommittedURL());
@@ -130,7 +136,7 @@
   auto* new_tab_helper = FromWebContents(new_web_contents);
 
   // Clone common state:
-  new_tab_helper->SetAppId(GetAppId());
+  new_tab_helper->SetAppId(app_id_);
 }
 
 bool WebAppTabHelper::IsInAppWindow() const {
@@ -139,45 +145,35 @@
 
 void WebAppTabHelper::OnWebAppInstalled(const AppId& installed_app_id) {
   // Check if current web_contents url is in scope for the newly installed app.
-  AppId app_id = FindAppIdWithUrlInScope(web_contents()->GetURL());
-  if (app_id != installed_app_id)
-    return;
-
-  SetAppId(app_id);
+  absl::optional<AppId> app_id =
+      FindAppWithUrlInScope(web_contents()->GetURL());
+  if (app_id == installed_app_id)
+    SetAppId(app_id);
 }
 
 void WebAppTabHelper::OnWebAppWillBeUninstalled(
     const AppId& uninstalled_app_id) {
-  if (GetAppId() == uninstalled_app_id)
-    ResetAppId();
+  if (app_id_ == uninstalled_app_id)
+    SetAppId(absl::nullopt);
 }
 
 void WebAppTabHelper::OnWebAppInstallManagerDestroyed() {
   observation_.Reset();
-  ResetAppId();
+  SetAppId(absl::nullopt);
 }
 
-void WebAppTabHelper::ResetAppId() {
-  if (app_id_.empty())
-    return;
-
-  AppId previous_app_id = app_id_;
-  app_id_.clear();
-
-  OnAssociatedAppChanged(previous_app_id, app_id_);
-}
-
-void WebAppTabHelper::OnAssociatedAppChanged(const AppId& previous_app_id,
-                                             const AppId& new_app_id) {
+void WebAppTabHelper::OnAssociatedAppChanged(
+    const absl::optional<AppId>& previous_app_id,
+    const absl::optional<AppId>& new_app_id) {
   provider_->ui_manager().NotifyOnAssociatedAppChanged(
       web_contents(), previous_app_id, new_app_id);
   UpdateAudioFocusGroupId();
 }
 
 void WebAppTabHelper::UpdateAudioFocusGroupId() {
-  if (!app_id_.empty() && IsInAppWindow()) {
+  if (app_id_.has_value() && IsInAppWindow()) {
     audio_focus_group_id_ =
-        provider_->audio_focus_id_map().CreateOrGetIdForApp(app_id_);
+        provider_->audio_focus_id_map().CreateOrGetIdForApp(app_id_.value());
   } else {
     audio_focus_group_id_ = base::UnguessableToken::Null();
   }
@@ -190,8 +186,9 @@
   provider_->policy_manager().ReinstallPlaceholderAppIfNecessary(url);
 }
 
-AppId WebAppTabHelper::FindAppIdWithUrlInScope(const GURL& url) const {
-  return provider_->registrar().FindAppWithUrlInScope(url).value_or(AppId());
+absl::optional<AppId> WebAppTabHelper::FindAppWithUrlInScope(
+    const GURL& url) const {
+  return provider_->registrar().FindAppWithUrlInScope(url);
 }
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(WebAppTabHelper);
diff --git a/chrome/browser/web_applications/web_app_tab_helper.h b/chrome/browser/web_applications/web_app_tab_helper.h
index 63a15d6..f3e238f 100644
--- a/chrome/browser/web_applications/web_app_tab_helper.h
+++ b/chrome/browser/web_applications/web_app_tab_helper.h
@@ -15,6 +15,7 @@
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace content {
 class WebContents;
@@ -32,13 +33,16 @@
  public:
   static void CreateForWebContents(content::WebContents* contents);
 
+  // Retrieves the WebAppTabHelper's app ID off |web_contents|, returns nullptr
+  // if there is no tab helper or app ID.
+  static const AppId* GetAppId(content::WebContents* web_contents);
+
   explicit WebAppTabHelper(content::WebContents* web_contents);
   WebAppTabHelper(const WebAppTabHelper&) = delete;
   WebAppTabHelper& operator=(const WebAppTabHelper&) = delete;
   ~WebAppTabHelper() override;
 
-  const AppId& GetAppId() const;
-  void SetAppId(const AppId& app_id);
+  void SetAppId(absl::optional<AppId> app_id);
   const base::UnguessableToken& GetAudioFocusGroupIdForTesting() const;
 
   bool acting_as_app() const { return acting_as_app_; }
@@ -69,8 +73,8 @@
   void ResetAppId();
 
   // Runs any logic when the associated app either changes or is removed.
-  void OnAssociatedAppChanged(const AppId& previous_app_id,
-                              const AppId& new_app_id);
+  void OnAssociatedAppChanged(const absl::optional<AppId>& previous_app_id,
+                              const absl::optional<AppId>& new_app_id);
 
   // Updates the audio focus group id based on the current web app.
   void UpdateAudioFocusGroupId();
@@ -78,10 +82,10 @@
   // Triggers a reinstall of a placeholder app for |url|.
   void ReinstallPlaceholderAppIfNecessary(const GURL& url);
 
-  AppId FindAppIdWithUrlInScope(const GURL& url) const;
+  absl::optional<AppId> FindAppWithUrlInScope(const GURL& url) const;
 
-  // WebApp associated with this tab. Empty string if no app associated.
-  AppId app_id_;
+  // WebApp associated with this tab.
+  absl::optional<AppId> app_id_;
 
   // True when the associated `WebContents` is acting as an app. Specifically,
   // this should only be true if `app_id_` is non empty, and the WebContents was
diff --git a/chrome/browser/web_applications/web_app_ui_manager.h b/chrome/browser/web_applications/web_app_ui_manager.h
index 08367d1..ced29ca 100644
--- a/chrome/browser/web_applications/web_app_ui_manager.h
+++ b/chrome/browser/web_applications/web_app_ui_manager.h
@@ -62,9 +62,10 @@
   // |app_id|, or any web app window if |app_id| is nullptr.
   virtual bool IsInAppWindow(content::WebContents* web_contents,
                              const AppId* app_id = nullptr) const = 0;
-  virtual void NotifyOnAssociatedAppChanged(content::WebContents* web_contents,
-                                            const AppId& previous_app_id,
-                                            const AppId& new_app_id) const = 0;
+  virtual void NotifyOnAssociatedAppChanged(
+      content::WebContents* web_contents,
+      const absl::optional<AppId>& previous_app_id,
+      const absl::optional<AppId>& new_app_id) const = 0;
 
   virtual bool CanReparentAppTabToWindow(const AppId& app_id,
                                          bool shortcut_created) const = 0;
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 3eeb423..05bc6fe3 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1653933461-50eea8bf3349d44db7de6f5093a1d30b33455cd8.profdata
+chrome-linux-main-1653976784-5ec3fd62a9ae180ebeded59e762505692ba93253.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 95b9b3c6..d984ae66 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1653933461-a95b214f374d3a69a0b4754936918c08fd08168d.profdata
+chrome-mac-main-1653976784-95679d4d3a8ebbcceff7b1979fd6b5a828d5cc73.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index b59b8b8d..a2f07f0 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1653922768-611624f3d4743460a4ade7a89684f2731a459975.profdata
+chrome-win32-main-1653976784-6415ab13549b0cce8111c60f520a9a7bcd566a77.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index a9137148..9958f45 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1653933461-4453083d56c04cc5d1b1700ba5cdc2c627fc229a.profdata
+chrome-win64-main-1653976784-707d08981560556aa84f85f501a9fc76a826b42d.profdata
diff --git a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
index d8af9f7..abd1079 100644
--- a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
@@ -49,6 +49,20 @@
   flush();
 }
 
+/**
+ * @param {!Element} select
+ * @param {number} index
+ */
+function selectContainerByIndex(select, index) {
+  assertTrue(!!select);
+  const mdSelect =
+      select.root.querySelector('select#selectContainer.md-select');
+  assertTrue(!!mdSelect);
+  mdSelect.selectedIndex = index;
+  mdSelect.dispatchEvent(new CustomEvent('change'));
+  flush();
+}
+
 suite('CrostiniPageTests', function() {
   setup(function() {
     crostiniBrowserProxy = new TestCrostiniBrowserProxy();
@@ -499,19 +513,30 @@
     suite('SubPagePortForwarding', function() {
       /** @type {?SettingsCrostiniPortForwarding} */
       let subpage;
+
+      const allContainers = /** @type {!Array<!ContainerInfo>}*/
+          ([
+            {id: {vm_name: 'termina', container_name: 'penguin'}},
+            {id: {vm_name: 'not-termina', container_name: 'not-penguin'}}
+          ]);
       setup(async function() {
         crostiniBrowserProxy.portOperationSuccess = true;
+        crostiniBrowserProxy.containerInfo = allContainers;
         setCrostiniPrefs(true, {
           forwardedPorts: [
             {
               port_number: 5000,
               protocol_type: 0,
               label: 'Label1',
+              vm_name: 'termina',
+              container_name: 'penguin',
             },
             {
               port_number: 5001,
               protocol_type: 1,
               label: 'Label2',
+              vm_name: 'not-termina',
+              container_name: 'not-penguin',
             },
           ]
         });
@@ -528,7 +553,7 @@
       test('DisplayPorts', async function() {
         // Extra list item for the titles.
         assertEquals(
-            3, subpage.shadowRoot.querySelectorAll('.list-item').length);
+            4, subpage.shadowRoot.querySelectorAll('.list-item').length);
       });
 
       test('AddPortSuccess', async function() {
@@ -538,19 +563,25 @@
 
         await flushTasks();
         subpage = subpage.$$('settings-crostini-add-port-dialog');
-        const portNumberInput = subpage.$$('#portNumberInput');
+        const portNumberInput = subpage.root.querySelector('#portNumberInput');
         portNumberInput, focus();
         portNumberInput.value = '5002';
         portNumberInput, blur();
         assertEquals(portNumberInput.invalid, false);
-        const portLabelInput = subpage.$$('#portLabelInput');
+        const portLabelInput = subpage.root.querySelector('#portLabelInput');
         portLabelInput.value = 'Some Label';
-        subpage.$$('cr-dialog cr-button[id="continue"]').click();
+        const select =
+            subpage.root.querySelector('settings-crostini-container-select');
+        selectContainerByIndex(select, 1);
+
+        subpage.root.querySelector('cr-dialog cr-button[id="continue"]')
+            .click();
         assertEquals(
             1, crostiniBrowserProxy.getCallCount('addCrostiniPortForward'));
-        assertEquals(
-            4,
-            crostiniBrowserProxy.getArgs('addCrostiniPortForward')[0].length);
+        const args = crostiniBrowserProxy.getArgs('addCrostiniPortForward')[0];
+        assertEquals(4, args.length);
+        assertEquals(args[0].vm_name, 'not-termina');
+        assertEquals(args[0].container_name, 'not-penguin');
       });
 
       test('AddPortFail', async function() {
@@ -560,9 +591,10 @@
 
         await flushTasks();
         subpage = subpage.$$('settings-crostini-add-port-dialog');
-        const portNumberInput = subpage.$$('#portNumberInput');
-        const portLabelInput = subpage.$$('#portLabelInput');
-        const continueButton = subpage.$$('cr-dialog cr-button[id="continue"]');
+        const portNumberInput = subpage.root.querySelector('#portNumberInput');
+        const portLabelInput = subpage.root.querySelector('#portLabelInput');
+        const continueButton =
+            subpage.root.querySelector('cr-dialog cr-button[id="continue"]');
 
         assertEquals(portNumberInput.invalid, false);
         portNumberInput.focus();
@@ -586,7 +618,8 @@
         portNumberInput.focus();
         portNumberInput.value = '5000';
         portNumberInput.blur();
-        subpage.$$('cr-dialog cr-button[id="continue"]').click();
+        subpage.root.querySelector('cr-dialog cr-button[id="continue"]')
+            .click();
         assertEquals(continueButton.disabled, true);
         assertEquals(portNumberInput.invalid, true);
         assertEquals(
@@ -608,7 +641,7 @@
 
         await flushTasks();
         subpage = subpage.$$('settings-crostini-add-port-dialog');
-        subpage.$$('cr-dialog cr-button[id="cancel"]').click();
+        subpage.root.querySelector('cr-dialog cr-button[id="cancel"]').click();
 
         await flushTasks();
         subpage = crostiniPage.$$('settings-crostini-port-forwarding');
@@ -623,19 +656,24 @@
         await flushTasks();
         subpage.$$('#removeAllPortsButton').click();
         assertEquals(
-            1,
+            2,
             crostiniBrowserProxy.getCallCount('removeAllCrostiniPortForwards'));
       });
 
       test('RemoveSinglePort', async function() {
         await flushTasks();
         subpage = crostiniPage.$$('settings-crostini-port-forwarding');
-        subpage.$$('#showRemoveSinglePortMenu0').click();
+        subpage.$$('#showRemoveSinglePortMenu0-0').click();
         await flushTasks();
 
         subpage.$$('#removeSinglePortButton').click();
         assertEquals(
             1, crostiniBrowserProxy.getCallCount('removeCrostiniPortForward'));
+        const args =
+            crostiniBrowserProxy.getArgs('removeCrostiniPortForward')[0];
+        assertEquals(3, args.length);
+        assertEquals(args[0].vm_name, 'termina');
+        assertEquals(args[0].container_name, 'penguin');
       });
 
 
@@ -643,7 +681,7 @@
         assertFalse(subpage.$$('#errorToast').open);
         await flushTasks();
         subpage = crostiniPage.$$('settings-crostini-port-forwarding');
-        subpage.$$('#toggleActivationButton0').click();
+        subpage.$$('#toggleActivationButton0-0').click();
 
         await flushTasks();
         assertEquals(
@@ -657,7 +695,8 @@
         crostiniBrowserProxy.portOperationSuccess = false;
         assertFalse(subpage.$$('#errorToast').open);
         subpage = crostiniPage.$$('settings-crostini-port-forwarding');
-        const crToggle = subpage.$$('#toggleActivationButton1');
+        const crToggle = subpage.$$('#toggleActivationButton1-0');
+        assertTrue(!!crToggle);
         assertEquals(crToggle.checked, false);
         crToggle.click();
 
@@ -672,7 +711,7 @@
       test('DeactivateSinglePort', async function() {
         await flushTasks();
         subpage = crostiniPage.$$('settings-crostini-port-forwarding');
-        const crToggle = subpage.$$('#toggleActivationButton0');
+        const crToggle = subpage.$$('#toggleActivationButton0-0');
         crToggle.checked = true;
         crToggle.click();
 
@@ -692,7 +731,7 @@
             },
           ]
         });
-        const crToggle = subpage.$$('#toggleActivationButton0');
+        const crToggle = subpage.$$('#toggleActivationButton0-0');
 
         webUIListenerCallback(
             'crostini-port-forwarder-active-ports-changed',
@@ -707,6 +746,13 @@
       });
 
       test('PortPrefsChange', async function() {
+        // Default prefs should have list items per port, plus one per
+        // container.
+        assertEquals(
+            4, subpage.shadowRoot.querySelectorAll('.list-item').length);
+
+        // When only one the default container has ports, we lose an item for
+        // the extra container heading.
         setCrostiniPrefs(true, {
           forwardedPorts: [
             {
@@ -717,7 +763,7 @@
             {
               port_number: 5001,
               protocol_type: 0,
-              label: 'Label1',
+              label: 'Label2',
             },
           ]
         });
@@ -733,12 +779,12 @@
             {
               port_number: 5001,
               protocol_type: 0,
-              label: 'Label1',
+              label: 'Label2',
             },
             {
               port_number: 5002,
               protocol_type: 0,
-              label: 'Label1',
+              label: 'Label3',
             },
           ]
         });
@@ -750,7 +796,7 @@
       });
 
       test('CrostiniStopAndStart', async function() {
-        const crToggle = subpage.$$('#toggleActivationButton0');
+        const crToggle = subpage.$$('#toggleActivationButton0-0');
         assertFalse(crToggle.disabled);
 
         webUIListenerCallback('crostini-status-changed', false);
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index a751ab3..d2b53c1 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -235,6 +235,9 @@
 
   # https://crbug.com/1329761
   "policy.DefaultSerialGuardSetting",
+
+  # b/233264555
+  "inputs.PhysicalKeyboardEmojiSuggestion",
 ]
 
 # To create filters to be used on specific builders add them like this:
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 0c29eeaf..8caab0f 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -876,6 +876,8 @@
         "autofill_assistant/browser/service/mock_service.cc",
         "autofill_assistant/browser/service/mock_service.h",
         "autofill_assistant/browser/web/batch_element_checker_browsertest.cc",
+        "autofill_assistant/browser/web/mock_autofill_assistant_agent.cc",
+        "autofill_assistant/browser/web/mock_autofill_assistant_agent.h",
         "autofill_assistant/browser/web/mock_web_controller.cc",
         "autofill_assistant/browser/web/mock_web_controller.h",
         "autofill_assistant/browser/web/semantic_element_finder_browsertest.cc",
diff --git a/components/assist_ranker/base_predictor_unittest.cc b/components/assist_ranker/base_predictor_unittest.cc
index cfbd551..65411ba 100644
--- a/components/assist_ranker/base_predictor_unittest.cc
+++ b/components/assist_ranker/base_predictor_unittest.cc
@@ -33,21 +33,9 @@
 const char kTestUrlParamName[] = "ranker-model-url";
 const char kTestDefaultModelUrl[] = "https://foo.bar/model.bin";
 
-// The allowed features must be metrics of kTestLoggingName in ukm.xml,
-// though the types do not need to match.
-const char kBoolFeature[] = "DidOptIn";
-const char kIntFeature[] = "DurationAfterScrollMs";
-const char kFloatFeature[] = "FontSize";
-const char kStringFeature[] = "IsEntity";
-const char kStringListFeature[] = "IsEntityEligible";
-const char kFeatureNotAllowed[] = "not_allowed";
-
 const char kTestNavigationUrl[] = "https://foo.com";
 
-const base::flat_set<std::string> kFeatureAllowlist({kBoolFeature, kIntFeature,
-                                                     kFloatFeature,
-                                                     kStringFeature,
-                                                     kStringListFeature});
+const base::flat_set<std::string> kFeatureAllowlist;
 
 const base::Feature kTestRankerQuery{"TestRankerQuery",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
@@ -159,41 +147,6 @@
   EXPECT_FALSE(predictor->IsReady());
 }
 
-TEST_F(BasePredictorTest, LogExampleToUkm) {
-  auto predictor = FakePredictor::Create();
-  RankerExample example;
-  auto& features = *example.mutable_features();
-  features[kBoolFeature].set_bool_value(true);
-  features[kIntFeature].set_int32_value(42);
-  features[kFloatFeature].set_float_value(42.0f);
-  features[kStringFeature].set_string_value("42");
-  features[kStringListFeature].mutable_string_list()->add_string_value("42");
-
-  // This feature will not be logged.
-  features[kFeatureNotAllowed].set_bool_value(false);
-
-  predictor->LogExampleToUkm(example, GetSourceId());
-
-  EXPECT_EQ(1U, GetTestUkmRecorder()->sources_count());
-  EXPECT_EQ(1U, GetTestUkmRecorder()->entries_count());
-  std::vector<const ukm::mojom::UkmEntry*> entries =
-      GetTestUkmRecorder()->GetEntriesByName(kTestLoggingName);
-  EXPECT_EQ(1U, entries.size());
-  GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kBoolFeature,
-                                          72057594037927937);
-  GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kIntFeature,
-                                          216172782113783850);
-  GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kFloatFeature,
-                                          144115189185773568);
-  GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kStringFeature,
-                                          288230377208836903);
-  GetTestUkmRecorder()->ExpectEntryMetric(entries[0], kStringListFeature,
-                                          360287971246764839);
-
-  EXPECT_FALSE(
-      GetTestUkmRecorder()->EntryHasMetric(entries[0], kFeatureNotAllowed));
-}
-
 TEST_F(BasePredictorTest, GetPredictThresholdReplacement) {
   float altered_threshold = 0.78f;  // Arbitrary value.
   const PredictorConfig altered_threshold_config{
diff --git a/components/assist_ranker/predictor_config_definitions.cc b/components/assist_ranker/predictor_config_definitions.cc
index 0d2c38c..a46a3a7 100644
--- a/components/assist_ranker/predictor_config_definitions.cc
+++ b/components/assist_ranker/predictor_config_definitions.cc
@@ -47,40 +47,7 @@
 // the UKM generated API.
 const base::flat_set<std::string>* GetContextualSearchFeatureAllowlist() {
   static auto* kContextualSearchFeatureAllowlist =
-      new base::flat_set<std::string>({"DidOptIn",
-                                       "DurationAfterScrollMs",
-                                       "EntityImpressionsCount",
-                                       "EntityOpensCount",
-                                       "FontSize",
-                                       "IsEntity",
-                                       "IsEntityEligible",
-                                       "IsHttp",
-                                       "IsLanguageMismatch",
-                                       "IsLongWord",
-                                       "IsSecondTapOverride",
-                                       "IsShortWord",
-                                       "IsWordEdge",
-                                       "OpenCount",
-                                       "OutcomeRankerDidPredict",
-                                       "OutcomeRankerPrediction",
-                                       "OutcomeRankerPredictionScore",
-                                       "OutcomeWasCardsDataShown",
-                                       "OutcomeWasPanelOpened",
-                                       "OutcomeWasQuickActionClicked",
-                                       "OutcomeWasQuickAnswerSeen",
-                                       "PortionOfElement",
-                                       "Previous28DayCtrPercent",
-                                       "Previous28DayImpressionsCount",
-                                       "PreviousWeekCtrPercent",
-                                       "PreviousWeekImpressionsCount",
-                                       "QuickActionImpressionsCount",
-                                       "QuickActionsIgnored",
-                                       "QuickActionsTaken",
-                                       "QuickAnswerCount",
-                                       "ScreenTopDps",
-                                       "TapCount",
-                                       "TapDurationMs",
-                                       "WasScreenBottom"});
+      new base::flat_set<std::string>();
   return kContextualSearchFeatureAllowlist;
 }
 
diff --git a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandlerImpl.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandlerImpl.java
index d929c1a..c9b1a7d 100644
--- a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandlerImpl.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandlerImpl.java
@@ -128,6 +128,15 @@
         client.showFatalError();
     }
 
+    @Override
+    public boolean isSupervisedUser() {
+        AutofillAssistantClient client = getOrCreateClient();
+        if (client == null) {
+            return false;
+        }
+        return client.isSupervisedUser();
+    }
+
     /**
      * Returns a client for the current tab or {@code null} if there's no current tab or the current
      * tab doesn't have an associated browser content.
diff --git a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AutofillAssistantClient.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AutofillAssistantClient.java
index a4c554e..bcf9928 100644
--- a/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AutofillAssistantClient.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AutofillAssistantClient.java
@@ -194,6 +194,16 @@
                 mNativeClientAndroid, AutofillAssistantClient.this);
     }
 
+    /**
+     * Check whether the user is supervised.
+     * @return supervised state
+     */
+    public boolean isSupervisedUser() {
+        if (mNativeClientAndroid == 0) return false;
+        return AutofillAssistantClientJni.get().isSupervisedUser(
+                mNativeClientAndroid, AutofillAssistantClient.this);
+    }
+
     @CalledByNative
     private void chooseAccountAsyncIfNecessary(@Nullable String userName) {
         if (mAccountInitializationStarted) return;
@@ -400,6 +410,7 @@
                 String actionId, String experimentId, String[] argumentNames,
                 String[] argumentValues, @Nullable AssistantOverlayCoordinator overlayCoordinator);
         void showFatalError(long nativeClientAndroid, AutofillAssistantClient caller);
+        boolean isSupervisedUser(long nativeClientAndroid, AutofillAssistantClient caller);
         void onSpokenFeedbackAccessibilityServiceChanged(
                 long nativeClientAndroid, AutofillAssistantClient caller, boolean enabled);
         AssistantDependencies getDependencies(
diff --git a/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandler.java b/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandler.java
index ef896c4..3226d52 100644
--- a/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandler.java
+++ b/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AutofillAssistantActionHandler.java
@@ -73,4 +73,10 @@
      * Displays a generic error message to the user.
      */
     void showFatalError();
+
+    /**
+     * Check whether the user is supervised.
+     * @return supervised state
+     */
+    boolean isSupervisedUser();
 }
diff --git a/components/autofill_assistant/browser/actions/action.cc b/components/autofill_assistant/browser/actions/action.cc
index db95450..8103f5a 100644
--- a/components/autofill_assistant/browser/actions/action.cc
+++ b/components/autofill_assistant/browser/actions/action.cc
@@ -279,6 +279,9 @@
     case ActionProto::ActionInfoCase::kRegisterPasswordResetRequest:
       out << "RegisterPasswordResetRequest";
       break;
+    case ActionProto::ActionInfoCase::kSetNativeValue:
+      out << "SetNativeValue";
+      break;
     case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET:
       out << "ACTION_INFO_NOT_SET";
       break;
diff --git a/components/autofill_assistant/browser/android/client_android.cc b/components/autofill_assistant/browser/android/client_android.cc
index 9709e066..35552da 100644
--- a/components/autofill_assistant/browser/android/client_android.cc
+++ b/components/autofill_assistant/browser/android/client_android.cc
@@ -369,6 +369,13 @@
       Metrics::DropOutReason::NO_SCRIPTS);
 }
 
+bool ClientAndroid::IsSupervisedUser(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller) {
+  return dependencies_->GetCommonDependencies()->IsSupervisedUser(
+      GetWebContents()->GetBrowserContext());
+}
+
 void ClientAndroid::OnSpokenFeedbackAccessibilityServiceChanged(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
diff --git a/components/autofill_assistant/browser/android/client_android.h b/components/autofill_assistant/browser/android/client_android.h
index 36c5914..c427dcefb 100644
--- a/components/autofill_assistant/browser/android/client_android.h
+++ b/components/autofill_assistant/browser/android/client_android.h
@@ -106,6 +106,9 @@
   void ShowFatalError(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& jcaller);
 
+  bool IsSupervisedUser(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& jcaller);
+
   void OnSpokenFeedbackAccessibilityServiceChanged(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller,
diff --git a/components/autofill_assistant/browser/dom_action.proto b/components/autofill_assistant/browser/dom_action.proto
index 1336d6f..c3c8805e 100644
--- a/components/autofill_assistant/browser/dom_action.proto
+++ b/components/autofill_assistant/browser/dom_action.proto
@@ -220,3 +220,12 @@
   // not resolved in time, the action will report a |TIMED_OUT| error.
   optional int32 timeout_ms = 3;
 }
+
+// Set an element value through native.
+message SetNativeValueProto {
+  // The target element. Must be an instance of a |WebFormControlElement|,
+  // otherwise the action will fail.
+  optional ClientIdProto client_id = 1;
+  // The value to set.
+  optional TextValue value = 2;
+}
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index 46a68cdf..b3c147e6 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -460,6 +460,14 @@
     case ActionProto::ActionInfoCase::kRegisterPasswordResetRequest:
       return std::make_unique<RegisterPasswordResetRequestAction>(delegate,
                                                                   action);
+    case ActionProto::ActionInfoCase::kSetNativeValue:
+      return PerformOnSingleElementAction::WithClientId(
+          delegate, action, action.set_native_value().client_id(),
+          base::BindOnce(
+              &action_delegate_util::PerformWithTextValue, delegate,
+              action.set_native_value().value(),
+              base::BindOnce(&WebController::SetNativeValue,
+                             delegate->GetWebController()->GetWeakPtr())));
     case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: {
       VLOG(1) << "Encountered action with ACTION_INFO_NOT_SET";
       return std::make_unique<UnsupportedAction>(delegate, action);
@@ -728,6 +736,10 @@
       success = ParseActionFromString(action_id, bytes, error_message,
                                       proto.mutable_external_action());
       break;
+    case ActionProto::ActionInfoCase::kSetNativeValue:
+      success = ParseActionFromString(action_id, bytes, error_message,
+                                      proto.mutable_set_native_value());
+      break;
     case ActionProto::ActionInfoCase::kRegisterPasswordResetRequest:
       success = ParseActionFromString(
           action_id, bytes, error_message,
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 683223f6..3022de7 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -1053,6 +1053,7 @@
     ExecuteJsProto execute_js = 93;
     RegisterPasswordResetRequestProto register_password_reset_request = 94;
     ExternalActionProto external_action = 95;
+    SetNativeValueProto set_native_value = 96;
   }
 
   // Set to true to make the client remove any contextual information if the
diff --git a/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.cc b/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.cc
new file mode 100644
index 0000000..5811665
--- /dev/null
+++ b/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.cc
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h"
+
+#include "base/test/bind.h"
+#include "content/public/browser/render_frame_host.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+
+namespace autofill_assistant {
+
+MockAutofillAssistantAgent::MockAutofillAssistantAgent() = default;
+MockAutofillAssistantAgent::~MockAutofillAssistantAgent() = default;
+
+void MockAutofillAssistantAgent::BindPendingReceiver(
+    mojo::ScopedInterfaceEndpointHandle handle) {
+  receivers_.Add(this,
+                 mojo::PendingAssociatedReceiver<mojom::AutofillAssistantAgent>(
+                     std::move(handle)));
+}
+
+// static
+void MockAutofillAssistantAgent::RegisterForAllFrames(
+    content::WebContents* web_contents,
+    MockAutofillAssistantAgent* agent) {
+  web_contents->GetMainFrame()->ForEachRenderFrameHost(
+      base::BindLambdaForTesting([agent](content::RenderFrameHost* host) {
+        host->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting(
+            mojom::AutofillAssistantAgent::Name_,
+            base::BindRepeating(
+                &MockAutofillAssistantAgent::BindPendingReceiver,
+                base::Unretained(agent)));
+      }));
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h b/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h
new file mode 100644
index 0000000..4b3fca57
--- /dev/null
+++ b/components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_MOCK_AUTOFILL_ASSISTANT_AGENT_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_MOCK_AUTOFILL_ASSISTANT_AGENT_H_
+
+#include "base/callback.h"
+#include "base/time/time.h"
+#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+class MockAutofillAssistantAgent : public mojom::AutofillAssistantAgent {
+ public:
+  MockAutofillAssistantAgent();
+  ~MockAutofillAssistantAgent() override;
+
+  void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle);
+  static void RegisterForAllFrames(content::WebContents* web_contents,
+                                   MockAutofillAssistantAgent* agent);
+
+  MOCK_METHOD(void,
+              GetSemanticNodes,
+              (int32_t role,
+               int32_t objective,
+               bool ignore_objective,
+               base::TimeDelta model_timeout,
+               base::OnceCallback<void(mojom::NodeDataStatus,
+                                       const std::vector<NodeData>&)> callback),
+              (override));
+  MOCK_METHOD(void,
+              SetElementValue,
+              (int32_t backend_node_id,
+               const std::u16string& value,
+               bool send_events,
+               base::OnceCallback<void(bool)> callback),
+              (override));
+
+ private:
+  mojo::AssociatedReceiverSet<mojom::AutofillAssistantAgent> receivers_;
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_WEB_MOCK_AUTOFILL_ASSISTANT_AGENT_H_
diff --git a/components/autofill_assistant/browser/web/semantic_element_finder_browsertest.cc b/components/autofill_assistant/browser/web/semantic_element_finder_browsertest.cc
index e17d448..1b564d807 100644
--- a/components/autofill_assistant/browser/web/semantic_element_finder_browsertest.cc
+++ b/components/autofill_assistant/browser/web/semantic_element_finder_browsertest.cc
@@ -35,7 +35,7 @@
 #include "components/autofill_assistant/browser/web/element_finder_result.h"
 #include "components/autofill_assistant/browser/web/element_finder_result_type.h"
 #include "components/autofill_assistant/browser/web/element_store.h"
-#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
+#include "components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h"
 #include "components/autofill_assistant/content/common/autofill_assistant_types.mojom.h"
 #include "components/autofill_assistant/content/common/node_data.h"
 #include "content/public/browser/render_frame_host.h"
@@ -48,7 +48,6 @@
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "url/gurl.h"
 
 namespace autofill_assistant {
@@ -60,31 +59,6 @@
 using ::testing::Return;
 using ::testing::WithArgs;
 
-class MockAutofillAssistantAgent : public mojom::AutofillAssistantAgent {
- public:
-  MockAutofillAssistantAgent() = default;
-  ~MockAutofillAssistantAgent() override = default;
-
-  void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle) {
-    receivers_.Add(
-        this, mojo::PendingAssociatedReceiver<mojom::AutofillAssistantAgent>(
-                  std::move(handle)));
-  }
-
-  MOCK_METHOD(void,
-              GetSemanticNodes,
-              (int32_t role,
-               int32_t objective,
-               bool ignore_objective,
-               base::TimeDelta model_timeout,
-               base::OnceCallback<void(mojom::NodeDataStatus,
-                                       const std::vector<NodeData>&)> callback),
-              (override));
-
- private:
-  mojo::AssociatedReceiverSet<mojom::AutofillAssistantAgent> receivers_;
-};
-
 }  // namespace
 
 class SemanticElementFinderBrowserTest
@@ -103,16 +77,8 @@
   void SetUpOnMainThread() override {
     BaseBrowserTest::SetUpOnMainThread();
 
-    // Register the same agent on all frames, such that the callback can be
-    // mocked.
-    shell()->web_contents()->GetMainFrame()->ForEachRenderFrameHost(
-        base::BindLambdaForTesting([this](content::RenderFrameHost* host) {
-          host->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting(
-              mojom::AutofillAssistantAgent::Name_,
-              base::BindRepeating(
-                  &MockAutofillAssistantAgent::BindPendingReceiver,
-                  base::Unretained(&autofill_assistant_agent_)));
-        }));
+    MockAutofillAssistantAgent::RegisterForAllFrames(
+        shell()->web_contents(), &autofill_assistant_agent_);
 
     annotate_dom_model_service_ = std::make_unique<AnnotateDomModelService>(
         /* opt_guide= */ nullptr, /* background_task_runner= */ nullptr);
diff --git a/components/autofill_assistant/browser/web/web_controller.cc b/components/autofill_assistant/browser/web/web_controller.cc
index 09b6b5e..3a06cd59 100644
--- a/components/autofill_assistant/browser/web/web_controller.cc
+++ b/components/autofill_assistant/browser/web/web_controller.cc
@@ -35,6 +35,8 @@
 #include "components/autofill_assistant/browser/web/element_finder_result_type.h"
 #include "components/autofill_assistant/browser/web/selector_observer.h"
 #include "components/autofill_assistant/browser/web/web_controller_util.h"
+#include "components/autofill_assistant/content/browser/content_autofill_assistant_driver.h"
+#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
@@ -1001,6 +1003,8 @@
                             const autofill::FormData&,
                             const autofill::FormFieldData&)> callback) {
   if (!element.backend_node_id()) {
+    DVLOG(1) << __func__
+             << "No backend node id on element intended for native execution.";
     std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__), nullptr,
                             autofill::FormData(), autofill::FormFieldData());
     return;
@@ -1621,6 +1625,39 @@
       WebControllerErrorInfoProto::EXECUTE_JS, std::move(callback));
 }
 
+void WebController::SetNativeValue(
+    const std::string& value,
+    const ElementFinderResult& element,
+    base::OnceCallback<void(const ClientStatus&)> callback) {
+  if (!element.backend_node_id()) {
+    DVLOG(1) << __func__
+             << "No backend node id on element intended for native execution.";
+    std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__));
+    return;
+  }
+
+  auto* render_frame_host = element.render_frame_host();
+  DCHECK(render_frame_host);
+  auto* driver = ContentAutofillAssistantDriver::GetOrCreateForRenderFrameHost(
+      render_frame_host, annotate_dom_model_service_);
+  if (!driver) {
+    std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__));
+    return;
+  }
+  driver->GetAutofillAssistantAgent()->SetElementValue(
+      *element.backend_node_id(), base::UTF8ToUTF16(value),
+      /* send_events= */ true,
+      base::BindOnce(&WebController::OnSetElementValue,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void WebController::OnSetElementValue(
+    base::OnceCallback<void(const ClientStatus&)> callback,
+    bool success) const {
+  std::move(callback).Run(success ? OkClientStatus()
+                                  : UnexpectedErrorStatus(__FILE__, __LINE__));
+}
+
 base::WeakPtr<WebController> WebController::GetWeakPtr() const {
   return weak_ptr_factory_.GetWeakPtr();
 }
diff --git a/components/autofill_assistant/browser/web/web_controller.h b/components/autofill_assistant/browser/web/web_controller.h
index 3427d02f..7914f7e5 100644
--- a/components/autofill_assistant/browser/web/web_controller.h
+++ b/components/autofill_assistant/browser/web/web_controller.h
@@ -385,6 +385,11 @@
       const ElementFinderResult& element,
       base::OnceCallback<void(const ClientStatus&)> callback);
 
+  virtual void SetNativeValue(
+      const std::string& value,
+      const ElementFinderResult& element,
+      base::OnceCallback<void(const ClientStatus&)> callback);
+
   virtual base::WeakPtr<WebController> GetWeakPtr() const;
 
  private:
@@ -525,6 +530,8 @@
   void OnDispatchJsEvent(base::OnceCallback<void(const ClientStatus&)> callback,
                          const DevtoolsClient::ReplyStatus& reply_status,
                          std::unique_ptr<runtime::EvaluateResult> result) const;
+  void OnSetElementValue(base::OnceCallback<void(const ClientStatus&)> callback,
+                         bool success) const;
 
   // Weak pointer is fine here since it must outlive this web controller, which
   // is guaranteed by the owner of this object.
diff --git a/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index e28aa5a4..0b6088d 100644
--- a/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -59,7 +59,7 @@
 #include "components/autofill_assistant/browser/web/element_finder_result.h"
 #include "components/autofill_assistant/browser/web/element_finder_result_type.h"
 #include "components/autofill_assistant/browser/web/element_store.h"
-#include "components/autofill_assistant/content/common/node_data.h"
+#include "components/autofill_assistant/browser/web/mock_autofill_assistant_agent.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -97,6 +97,9 @@
   void SetUpOnMainThread() override {
     BaseBrowserTest::SetUpOnMainThread();
 
+    MockAutofillAssistantAgent::RegisterForAllFrames(
+        shell()->web_contents(), &autofill_assistant_agent_);
+
     web_controller_ = WebController::CreateForWebContents(
         shell()->web_contents(), &user_data_, &log_info_,
         /* annotate_dom_model_service= */ nullptr,
@@ -939,11 +942,29 @@
     return ClientStatus(captured_processed_actions[0].status());
   }
 
+  ClientStatus GetBackendNodeId(const ElementFinderResult& element,
+                                int* backend_node_id) {
+    ClientStatus result_status;
+
+    base::RunLoop run_loop;
+    web_controller_->GetBackendNodeId(
+        element,
+        base::BindLambdaForTesting([&](const ClientStatus& status, int id) {
+          result_status = status;
+          *backend_node_id = id;
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+
+    return result_status;
+  }
+
  protected:
   std::unique_ptr<WebController> web_controller_;
   UserData user_data_;
   UserModel user_model_;
   ProcessedActionStatusDetailsProto log_info_;
+  MockAutofillAssistantAgent autofill_assistant_agent_;
 };
 
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ElementExistenceCheck) {
@@ -3396,4 +3417,31 @@
   EXPECT_NEAR(after_reset_width, initial_width, 1);
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SetFieldValueThroughNative) {
+  ClientStatus element_status;
+  ElementFinderResult input;
+  FindElement(Selector({"#input1"}), &element_status, &input);
+  ASSERT_EQ(ACTION_APPLIED, element_status.proto_status());
+
+  int backend_node_id;
+  ASSERT_EQ(ACTION_APPLIED,
+            GetBackendNodeId(input, &backend_node_id).proto_status());
+  std::u16string expected_value = u"native";
+  EXPECT_CALL(autofill_assistant_agent_,
+              SetElementValue(backend_node_id, expected_value,
+                              /* send_events= */ true, _))
+      .WillOnce(RunOnceCallback<3>(true));
+
+  ClientStatus fill_status;
+  base::RunLoop run_loop;
+  web_controller_->SetNativeValue(
+      "native", input,
+      base::BindOnce(&WebControllerBrowserTest::OnClientStatus,
+                     base::Unretained(this), run_loop.QuitClosure(),
+                     &fill_status));
+  run_loop.Run();
+
+  EXPECT_EQ(ACTION_APPLIED, fill_status.proto_status());
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc b/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc
index 86fed0b..99bd750 100644
--- a/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc
+++ b/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc
@@ -50,7 +50,7 @@
   ContentAutofillAssistantDriver* driver =
       ContentAutofillAssistantDriver::GetOrCreateForCurrentDocument(
           render_frame_host);
-  if (driver) {
+  if (driver && annotate_dom_model_service) {
     driver->SetAnnotateDomModelService(annotate_dom_model_service);
   }
   return driver;
diff --git a/components/autofill_assistant/content/common/autofill_assistant_agent.mojom b/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
index ff86b393..9c1c9160 100644
--- a/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
+++ b/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
@@ -5,6 +5,7 @@
 module autofill_assistant.mojom;
 
 import "components/autofill_assistant/content/common/autofill_assistant_types.mojom";
+import "mojo/public/mojom/base/string16.mojom";
 import "mojo/public/mojom/base/time.mojom";
 
 // There is one instance of this interface per render frame in the renderer
@@ -16,4 +17,12 @@
                    mojo_base.mojom.TimeDelta timeout)
       => (NodeDataStatus status,
           array<autofill_assistant.mojom.NodeData> nodes);
+
+  // Set the value of a web element. The target needs to be a form control
+  // element, otherwise the call fails. This works for input, textarea and
+  // select. For a select element it finds the option with a value matching
+  // the given parameter (exactly) and makes that option the current selection.
+  SetElementValue(int32 backend_node_id, mojo_base.mojom.String16 value,
+                  bool send_events)
+      => (bool success);
 };
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc b/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
index 91adc27d..cdf09d9 100644
--- a/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
+++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
@@ -11,8 +11,11 @@
 #include "components/optimization_guide/machine_learning_tflite_buildflags.h"
 #include "content/public/renderer/render_frame.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/modules/autofill_assistant/node_signals.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_form_control_element.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 
@@ -132,6 +135,7 @@
     GetSemanticNodesCallback callback) {
   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (!frame) {
+    VLOG(1) << "Failed to get semantic nodes, no frame.";
     std::move(callback).Run(mojom::NodeDataStatus::kUnexpectedError,
                             std::vector<NodeData>());
     return;
@@ -236,4 +240,30 @@
   std::move(callback).Run(mojom::NodeDataStatus::kSuccess, nodes);
 }
 
+void AutofillAssistantAgent::SetElementValue(const int32_t backend_node_id,
+                                             const std::u16string& value,
+                                             bool send_events,
+                                             SetElementValueCallback callback) {
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+  if (!frame) {
+    VLOG(1) << "Failed to set Element value, no frame.";
+    std::move(callback).Run(false);
+    return;
+  }
+
+  blink::WebElement target_element =
+      frame->GetDocument().GetElementByDevToolsNodeId(backend_node_id);
+  if (target_element.IsNull() || !target_element.IsFormControlElement()) {
+    VLOG(3) << "Failed to set Element value, invalid target.";
+    std::move(callback).Run(false);
+    return;
+  }
+
+  blink::WebFormControlElement target_form_control_element =
+      target_element.To<blink::WebFormControlElement>();
+  target_form_control_element.SetValue(blink::WebString::FromUTF16(value),
+                                       send_events);
+  std::move(callback).Run(true);
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent.h b/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
index 3d711549..41773f1 100644
--- a/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
+++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
@@ -47,6 +47,10 @@
                         bool ignore_objective,
                         base::TimeDelta model_timeout,
                         GetSemanticNodesCallback callback) override;
+  void SetElementValue(int32_t backend_node_id,
+                       const std::u16string& value,
+                       bool send_events,
+                       SetElementValueCallback callback) override;
 
  private:
   // content::RenderFrameObserver:
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc b/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
index 01a3d13..ea6d3e0 100644
--- a/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
+++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
@@ -20,6 +20,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_form_control_element.h"
+#include "third_party/blink/public/web/web_local_frame.h"
 
 namespace autofill_assistant {
 namespace {
@@ -240,5 +244,67 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(AutofillAssistantAgentBrowserTest, SetElementValueForInput) {
+  LoadHTML(R"(<input id="id">)");
+
+  base::MockCallback<base::OnceCallback<void(bool)>> callback;
+  EXPECT_CALL(callback, Run(true));
+
+  const int backend_node_id = 3;  // Incrementally assigned, extracted manually.
+  autofill_assistant_agent_->SetElementValue(backend_node_id, u"value",
+                                             /* send_events= */ true,
+                                             callback.Get());
+  base::RunLoop().RunUntilIdle();
+
+  const auto web_element = GetMainRenderFrame()
+                               ->GetWebFrame()
+                               ->GetDocument()
+                               .GetElementById(blink::WebString::FromUTF8("id"))
+                               .To<blink::WebFormControlElement>();
+  EXPECT_EQ(web_element.Value(), "value");
+}
+
+TEST_F(AutofillAssistantAgentBrowserTest, SetElementValueForSelect) {
+  LoadHTML(R"(
+    <select id="id">
+      <option value="dog">Dog</option>
+      <option value="cat">Cat</option>
+    </select>)");
+
+  base::MockCallback<base::OnceCallback<void(bool)>> callback;
+  EXPECT_CALL(callback, Run(true));
+
+  const auto web_element = GetMainRenderFrame()
+                               ->GetWebFrame()
+                               ->GetDocument()
+                               .GetElementById(blink::WebString::FromUTF8("id"))
+                               .To<blink::WebFormControlElement>();
+
+  EXPECT_EQ(web_element.Value(), "dog");
+
+  const int backend_node_id = 3;  // Incrementally assigned, extracted manually.
+  autofill_assistant_agent_->SetElementValue(backend_node_id, u"cat",
+                                             /* send_events= */ true,
+                                             callback.Get());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(web_element.Value(), "cat");
+}
+
+TEST_F(AutofillAssistantAgentBrowserTest,
+       SetElementValueFailsForNonFormControl) {
+  LoadHTML(R"(
+    <div id="id"></div>)");
+
+  base::MockCallback<base::OnceCallback<void(bool)>> callback;
+  EXPECT_CALL(callback, Run(false));
+
+  const int backend_node_id = 3;  // Incrementally assigned, extracted manually.
+  autofill_assistant_agent_->SetElementValue(backend_node_id, u"value",
+                                             /* send_events= */ true,
+                                             callback.Get());
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace
 }  // namespace autofill_assistant
diff --git a/components/history/core/browser/top_sites_impl.cc b/components/history/core/browser/top_sites_impl.cc
index 27dee79..3906137a 100644
--- a/components/history/core/browser/top_sites_impl.cc
+++ b/components/history/core/browser/top_sites_impl.cc
@@ -14,6 +14,7 @@
 #include "base/check.h"
 #include "base/hash/md5.h"
 #include "base/location.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -100,6 +101,14 @@
 // TODO(sky): rename actual value to 'most_visited_blocked_urls.'
 const char kBlockedUrlsPrefsKey[] = "ntp.most_visited_blacklist";
 
+void RecordDBMetrics(const base::TimeTicks db_query_time,
+                     const size_t result_size) {
+  base::UmaHistogramTimes("History.TopSites.SearchTermsExtractionTime",
+                          base::TimeTicks::Now() - db_query_time);
+  base::UmaHistogramCounts10000("History.TopSites.SearchTermsExtractedCount",
+                                result_size);
+}
+
 }  // namespace
 
 // Initially, histogram is not recorded.
@@ -344,7 +353,9 @@
   // TODO(crbug.com/1317829): Investigate moving this to a background thread.
   MostVisitedURLList repeatable_query_sites;
   std::vector<std::unique_ptr<KeywordSearchTermVisit>> search_terms;
+  const base::TimeTicks db_query_time = base::TimeTicks::Now();
   history::GetMostRepeatedSearchTermsFromEnumerator(*enumerator, &search_terms);
+  RecordDBMetrics(db_query_time, search_terms.size());
   for (const auto& search_term : search_terms) {
     GURL url;
     if (!GetSearchResultsPageForDefaultSearchProvider(
diff --git a/components/history/core/browser/top_sites_impl_unittest.cc b/components/history/core/browser/top_sites_impl_unittest.cc
index 2a80891d..1d7bf80 100644
--- a/components/history/core/browser/top_sites_impl_unittest.cc
+++ b/components/history/core/browser/top_sites_impl_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
@@ -424,6 +425,7 @@
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndDisableFeature(kOrganicRepeatableQueries);
+    base::HistogramTester histogram_tester;
 
     StartQueryForMostVisited();
     WaitForHistory();
@@ -440,10 +442,16 @@
     ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2));
     EXPECT_EQ(srp_2, querier.urls()[0].url);
     EXPECT_EQ(news, querier.urls()[1].url);
+
+    histogram_tester.ExpectTotalCount(
+        "History.TopSites.SearchTermsExtractionTime", 0);
+    histogram_tester.ExpectTotalCount(
+        "History.TopSites.SearchTermsExtractedCount", 0);
   }
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndEnableFeature(kOrganicRepeatableQueries);
+    base::HistogramTester histogram_tester;
 
     RefreshTopSitesAndRecreate();
 
@@ -459,12 +467,20 @@
     EXPECT_EQ(news, querier.urls()[0].url);
     EXPECT_EQ(srp_1, querier.urls()[1].url);
     EXPECT_EQ(srp_2, querier.urls()[2].url);
+
+    histogram_tester.ExpectTotalCount(
+        "History.TopSites.SearchTermsExtractionTime", 1);
+    histogram_tester.ExpectTotalCount(
+        "History.TopSites.SearchTermsExtractedCount", 1);
+    histogram_tester.ExpectUniqueSample(
+        "History.TopSites.SearchTermsExtractedCount", 2, 1);
   }
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndEnableFeatureWithParameters(
         kOrganicRepeatableQueries,
         {{kPrivilegeRepeatableQueries.name, "true"}});
+    base::HistogramTester histogram_tester;
 
     RefreshTopSitesAndRecreate();
 
@@ -479,12 +495,20 @@
     EXPECT_EQ(srp_1, querier.urls()[0].url);
     EXPECT_EQ(srp_2, querier.urls()[1].url);
     EXPECT_EQ(news, querier.urls()[2].url);
+
+    histogram_tester.ExpectTotalCount(
+        "History.TopSites.SearchTermsExtractionTime", 1);
+    histogram_tester.ExpectTotalCount(
+        "History.TopSites.SearchTermsExtractedCount", 1);
+    histogram_tester.ExpectUniqueSample(
+        "History.TopSites.SearchTermsExtractedCount", 2, 1);
   }
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndEnableFeatureWithParameters(
         kOrganicRepeatableQueries, {{kPrivilegeRepeatableQueries.name, "true"},
                                     {kMaxNumRepeatableQueries.name, "1"}});
+    base::HistogramTester histogram_tester;
 
     RefreshTopSitesAndRecreate();
 
@@ -498,6 +522,13 @@
     ASSERT_NO_FATAL_FAILURE(ContainsPrepopulatePages(querier, 2));
     EXPECT_EQ(srp_1, querier.urls()[0].url);
     EXPECT_EQ(news, querier.urls()[1].url);
+
+    histogram_tester.ExpectTotalCount(
+        "History.TopSites.SearchTermsExtractionTime", 1);
+    histogram_tester.ExpectTotalCount(
+        "History.TopSites.SearchTermsExtractedCount", 1);
+    histogram_tester.ExpectUniqueSample(
+        "History.TopSites.SearchTermsExtractedCount", 2, 1);
   }
 }
 
diff --git a/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
index 688ee705..89758e4 100644
--- a/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
+++ b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
@@ -313,11 +313,15 @@
 }
 
 void PageLoadMetricsObserverTester::SimulateMediaPlayed() {
+  SimulateMediaPlayed(web_contents()->GetMainFrame());
+}
+
+void PageLoadMetricsObserverTester::SimulateMediaPlayed(
+    content::RenderFrameHost* rfh) {
   content::WebContentsObserver::MediaPlayerInfo video_type(
       true /* has_video*/, true /* has_audio */);
-  content::RenderFrameHost* render_frame_host = web_contents()->GetMainFrame();
   metrics_web_contents_observer_->MediaStartedPlaying(
-      video_type, content::MediaPlayerId(render_frame_host->GetGlobalId(), 0));
+      video_type, content::MediaPlayerId(rfh->GetGlobalId(), 0));
 }
 
 void PageLoadMetricsObserverTester::SimulateCookieAccess(
diff --git a/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
index a87d14d2..37f862f 100644
--- a/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
+++ b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
@@ -139,6 +139,7 @@
 
   // Simulate playing a media element.
   void SimulateMediaPlayed();
+  void SimulateMediaPlayed(content::RenderFrameHost* rfh);
 
   // Simulate accessingcookies.
   void SimulateCookieAccess(const content::CookieAccessDetails& details);
diff --git a/components/page_load_metrics/browser/page_load_metrics_observer_interface.h b/components/page_load_metrics/browser/page_load_metrics_observer_interface.h
index 2e14b0d..9f31d28 100644
--- a/components/page_load_metrics/browser/page_load_metrics_observer_interface.h
+++ b/components/page_load_metrics/browser/page_load_metrics_observer_interface.h
@@ -18,7 +18,6 @@
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/common/mobile_metrics/mobile_friendliness.h"
 #include "third_party/blink/public/common/use_counter/use_counter_feature.h"
-#include "third_party/blink/public/mojom/frame/frame.mojom.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
 
 #include "url/gurl.h"
diff --git a/components/services/unzip/public/cpp/unzip.cc b/components/services/unzip/public/cpp/unzip.cc
index fa07ee0..1b4575f 100644
--- a/components/services/unzip/public/cpp/unzip.cc
+++ b/components/services/unzip/public/cpp/unzip.cc
@@ -36,7 +36,8 @@
 }
 
 class UnzipParams : public base::RefCounted<UnzipParams>,
-                    public unzip::mojom::UnzipFilter {
+                    public unzip::mojom::UnzipFilter,
+                    public unzip::mojom::UnzipListener {
  public:
   UnzipParams(mojo::PendingRemote<mojom::Unzipper> unzipper,
               UnzipCallback callback)
@@ -59,6 +60,15 @@
     filter_callback_ = std::move(filter_callback);
   }
 
+  void SetListener(UnzipListenerCallback listener_callback) {
+    DCHECK(listener_callback);
+    listener_callback_ = std::move(listener_callback);
+  }
+
+  mojo::Receiver<unzip::mojom::UnzipListener>* GetListenerReceiver() {
+    return &listener_receiver_;
+  }
+
  private:
   friend class base::RefCounted<UnzipParams>;
 
@@ -71,11 +81,19 @@
     std::move(callback).Run(filter_callback_.Run(path));
   }
 
-  // The Remote and UnzipFilter are stored so they do not get deleted before the
-  // callback runs.
+  // unzip::mojom::UnzipListener implementation:
+  void OnProgress(uint64_t bytes) override {
+    DCHECK(listener_callback_);
+    listener_callback_.Run(bytes);
+  }
+
+  // The Remote, UnzipFilter and UnzipListener are stored so they do not
+  // get deleted before the callback runs.
   mojo::Remote<mojom::Unzipper> unzipper_;
   mojo::Receiver<unzip::mojom::UnzipFilter> filter_receiver_{this};
   UnzipFilterCallback filter_callback_;
+  mojo::Receiver<unzip::mojom::UnzipListener> listener_receiver_{this};
+  UnzipListenerCallback listener_callback_;
   UnzipCallback callback_;
 };
 
@@ -143,6 +161,7 @@
              const base::FilePath& zip_path,
              const base::FilePath& output_dir,
              UnzipFilterCallback filter_callback,
+             UnzipListenerCallback listener_callback,
              mojom::UnzipOptionsPtr options,
              UnzipCallback result_callback) {
   base::File zip_file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
@@ -173,9 +192,17 @@
                             std::move(filter_callback));
   }
 
+  mojo::PendingRemote<unzip::mojom::UnzipListener> listener_remote;
+  if (listener_callback) {
+    mojo::Receiver<unzip::mojom::UnzipListener>* listener =
+        unzip_params->GetListenerReceiver();
+    unzip_params->SetListener(std::move(listener_callback));
+    listener_remote = listener->BindNewPipeAndPassRemote();
+  }
+
   unzip_params->unzipper()->Unzip(
       std::move(zip_file), std::move(directory_remote), std::move(options),
-      std::move(filter_remote),
+      std::move(filter_remote), std::move(listener_remote),
       base::BindOnce(&UnzipParams::InvokeCallback, unzip_params));
 }
 
@@ -244,6 +271,7 @@
            const base::FilePath& zip_file,
            const base::FilePath& output_dir,
            mojom::UnzipOptionsPtr options,
+           UnzipListenerCallback listener_callback,
            UnzipCallback result_callback) {
   DCHECK(!result_callback.is_null());
 
@@ -254,7 +282,10 @@
   runner->PostTask(
       FROM_HERE,
       base::BindOnce(&DoUnzip, std::move(unzipper), zip_file, output_dir,
-                     UnzipFilterCallback(), std::move(options),
+                     UnzipFilterCallback(),
+                     base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
+                                        std::move(listener_callback)),
+                     std::move(options),
                      base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
                                         std::move(result_callback))));
 }
@@ -273,7 +304,8 @@
   runner->PostTask(
       FROM_HERE,
       base::BindOnce(&DoUnzip, std::move(unzipper), zip_path, output_dir,
-                     filter_callback, unzip::mojom::UnzipOptions::New(),
+                     filter_callback, UnzipListenerCallback(),
+                     unzip::mojom::UnzipOptions::New(),
                      base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
                                         std::move(result_callback))));
 }
diff --git a/components/services/unzip/public/cpp/unzip.h b/components/services/unzip/public/cpp/unzip.h
index 36dd9f1..3de69cc 100644
--- a/components/services/unzip/public/cpp/unzip.h
+++ b/components/services/unzip/public/cpp/unzip.h
@@ -33,10 +33,12 @@
                      UnzipFilterCallback filter_callback,
                      UnzipCallback result_callback);
 
+using UnzipListenerCallback = base::RepeatingCallback<void(uint64_t bytes)>;
 void Unzip(mojo::PendingRemote<mojom::Unzipper> unzipper,
            const base::FilePath& zip_file,
            const base::FilePath& output_dir,
            mojom::UnzipOptionsPtr options,
+           UnzipListenerCallback listener_callback,
            UnzipCallback result_callback);
 
 using DetectEncodingCallback = base::OnceCallback<void(Encoding)>;
diff --git a/components/services/unzip/public/cpp/unzip_unittest.cc b/components/services/unzip/public/cpp/unzip_unittest.cc
index 2c79a817..2174a9e 100644
--- a/components/services/unzip/public/cpp/unzip_unittest.cc
+++ b/components/services/unzip/public/cpp/unzip_unittest.cc
@@ -151,6 +151,31 @@
     return result;
   }
 
+  uint64_t DoGetProgressSize(const base::FilePath& zip_file,
+                             const base::FilePath& output_dir) {
+    mojo::PendingRemote<mojom::Unzipper> unzipper;
+    receivers_.Add(&unzipper_, unzipper.InitWithNewPipeAndPassReceiver());
+
+    base::RunLoop run_loop;
+    uint64_t bytes = 0;
+    mojom::UnzipOptionsPtr options = unzip::mojom::UnzipOptions::New("auto");
+
+    UnzipListenerCallback progress_callback =
+        base::BindLambdaForTesting([&](uint64_t written_bytes) {
+          bytes = written_bytes;
+          run_loop.QuitClosure().Run();
+        });
+
+    UnzipCallback result_callback = base::BindLambdaForTesting(
+        [&](const bool success) { run_loop.QuitClosure().Run(); });
+
+    Unzip(std::move(unzipper), zip_file, output_dir, std::move(options),
+          std::move(progress_callback), std::move(result_callback));
+
+    run_loop.Run();
+    return bytes;
+  }
+
  protected:
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -297,5 +322,12 @@
   EXPECT_EQ(8, CountFiles(unzip_dir_, &some_files_empty));
 }
 
+TEST_F(UnzipTest, GetExtractedProgressSize) {
+  uint64_t result =
+      DoGetProgressSize(GetArchivePath("good_archive.zip"), unzip_dir_);
+  // Check: first file extracted is 23 bytes long.
+  EXPECT_EQ(23ul, result);
+}
+
 }  // namespace
 }  // namespace unzip
diff --git a/components/services/unzip/public/mojom/unzipper.mojom b/components/services/unzip/public/mojom/unzipper.mojom
index 872eec6..1d5969a 100644
--- a/components/services/unzip/public/mojom/unzipper.mojom
+++ b/components/services/unzip/public/mojom/unzipper.mojom
@@ -29,6 +29,13 @@
   ShouldUnzipFile(mojo_base.mojom.FilePath path) => (bool result);
 };
 
+// Listener for a ZIP extraction operation.
+interface UnzipListener {
+  // Regularly called during ZIP extraction operation to report progress,
+  // with the total number of |bytes| processed since the last call.
+  OnProgress(uint64 bytes);
+};
+
 // Interface to the out-of-process unzipper. The unzipper unzips a ZIP
 // archive, often for the purpose of unpacking software updates.
 [ServiceSandbox=sandbox.mojom.Sandbox.kUtility]
@@ -39,11 +46,13 @@
   // If provided, |filter| is called for each entry of the archive (which incurs
   // one IPC for each entry) and only the entries for which it returns true are
   // extracted.
+  // If provided, |listener| is called repeatedly with the bytes extracted.
   Unzip(
       mojo_base.mojom.ReadOnlyFile zip_file,
       pending_remote<filesystem.mojom.Directory> output_dir,
       UnzipOptions options,
-      pending_remote<UnzipFilter>? filter) => (bool result);
+      pending_remote<UnzipFilter>? filter,
+      pending_remote<UnzipListener>? listener) => (bool result);
 
   // Detects the encoding of filenames stored in the ZIP archive.
   // Returns an Encoding as defined in
diff --git a/components/services/unzip/unzipper_impl.cc b/components/services/unzip/unzipper_impl.cc
index 899b73e..70ed9fa 100644
--- a/components/services/unzip/unzipper_impl.cc
+++ b/components/services/unzip/unzipper_impl.cc
@@ -14,7 +14,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "components/services/filesystem/public/mojom/directory.mojom.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/ced/src/compact_enc_det/compact_enc_det.h"
 #include "third_party/zlib/google/redact.h"
 #include "third_party/zlib/google/zip.h"
@@ -181,11 +180,31 @@
   return encoding;
 }
 
+void UnzipperImpl::Listener(const mojo::Remote<mojom::UnzipListener>& listener,
+                            uint64_t bytes) {
+  listener->OnProgress(bytes);
+}
+
+bool DoUnzip(base::File zip_file,
+             mojo::Remote<filesystem::mojom::Directory> output_dir,
+             std::string encoding_name,
+             zip::FilterCallback filter_cb,
+             zip::UnzipProgressCallback progress_cb) {
+  return zip::Unzip(
+      zip_file.GetPlatformFile(),
+      base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
+      base::BindRepeating(&CreateDirectory, output_dir.get()),
+      {.encoding = std::move(encoding_name),
+       .filter = std::move(filter_cb),
+       .progress = std::move(progress_cb)});
+}
+
 void UnzipperImpl::Unzip(
     base::File zip_file,
     mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
     mojom::UnzipOptionsPtr set_options,
     mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
+    mojo::PendingRemote<mojom::UnzipListener> listener_remote,
     UnzipCallback callback) {
   DCHECK(zip_file.IsValid());
 
@@ -198,6 +217,13 @@
         &Filter, mojo::Remote<mojom::UnzipFilter>(std::move(filter_remote)));
   }
 
+  zip::UnzipProgressCallback progress_cb;
+  if (listener_remote) {
+    mojo::Remote<mojom::UnzipListener> listener(std::move(listener_remote));
+    progress_cb =
+        base::BindRepeating(&UnzipperImpl::Listener, std::move(listener));
+  }
+
   std::string encoding_name;
   if (set_options->encoding == "auto") {
     Encoding encoding = GetEncoding(zip_file);
@@ -207,11 +233,13 @@
   } else {
     encoding_name = set_options->encoding;
   }
-  std::move(callback).Run(zip::Unzip(
-      zip_file.GetPlatformFile(),
-      base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
-      base::BindRepeating(&CreateDirectory, output_dir.get()),
-      {.encoding = std::move(encoding_name), .filter = std::move(filter_cb)}));
+
+  base::SequencedTaskRunnerHandle::Get()->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&DoUnzip, std::move(zip_file), std::move(output_dir),
+                     std::move(encoding_name), std::move(filter_cb),
+                     std::move(progress_cb)),
+      base::BindOnce(std::move(callback)));
 }
 
 void UnzipperImpl::DetectEncoding(base::File zip_file,
diff --git a/components/services/unzip/unzipper_impl.h b/components/services/unzip/unzipper_impl.h
index 2a2a9cc..39f2695 100644
--- a/components/services/unzip/unzipper_impl.h
+++ b/components/services/unzip/unzipper_impl.h
@@ -10,6 +10,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace unzip {
 
@@ -34,6 +35,7 @@
       mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
       mojom::UnzipOptionsPtr options,
       mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
+      mojo::PendingRemote<mojom::UnzipListener> listener_remote,
       UnzipCallback callback) override;
 
   void DetectEncoding(base::File zip_file,
@@ -42,6 +44,9 @@
   void GetExtractedSize(base::File zip_file,
                         GetExtractedSizeCallback callback) override;
 
+  static void Listener(const mojo::Remote<mojom::UnzipListener>& listener,
+                       uint64_t bytes);
+
   mojo::Receiver<mojom::Unzipper> receiver_{this};
 };
 
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 3223f04..10f24608 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -1527,6 +1527,7 @@
         {{features::kBackForwardCacheMemoryControls,
           {{"memory_threshold_for_back_forward_cache_in_mb",
             memory_threshold}}},
+         {features::kBackForwardCache_NoMemoryLimit_Trial, {}},
          {blink::features::kLoadingTasksUnfreezable, {}}},
         {});
   }
@@ -1535,25 +1536,37 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// Navigate from A to B and go back.
+// Ensure that the BackForwardCache trial is not activated and the
+// BackForwardCache_NoMemoryLimit_Trial trial got activated as expected on
+// low-memory devices.
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestForLowMemoryDevices,
                        DisableBFCacheForLowEndDevices) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
 
-  // Ensure that the trial starts inactive.
+  // Ensure that the BackForwardCache trial starts inactive, and the
+  // BackForwardCache_NoMemoryLimit_Trial trial starts active.
   EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
       base::FeatureList::GetFieldTrial(features::kBackForwardCache)
           ->trial_name()));
+  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(
+          features::kBackForwardCache_NoMemoryLimit_Trial)
+          ->trial_name()));
 
   EXPECT_FALSE(IsBackForwardCacheEnabled());
 
-  // Ensure that we do not activate the trial when querying bfcache status,
-  // which is protected by low-memory setting.
+  // Ensure that we do not activate the BackForwardCache trial when querying
+  // bfcache status, and the BackForwardCache_NoMemoryLimit_Trial trial stays
+  // active.
   EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
       base::FeatureList::GetFieldTrial(features::kBackForwardCache)
           ->trial_name()));
+  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(
+          features::kBackForwardCache_NoMemoryLimit_Trial)
+          ->trial_name()));
 
   // 1) Navigate to A.
   EXPECT_TRUE(NavigateToURL(shell(), url_a));
@@ -1567,14 +1580,28 @@
   // memory is less than the memory threshold.
   delete_observer_rfh_a.WaitUntilDeleted();
 
-  // Nothing is recorded when the memory is less than the threshold value.
-  ExpectOutcomeDidNotChange(FROM_HERE);
-  ExpectNotRestoredDidNotChange(FROM_HERE);
+  // 4) Go back to check the
+  // NotRestoredReasons.kBackForwardCacheDisabledByLowMemory is recorded when
+  // the memory is less than the threshold value.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
 
-  // Ensure that the trial still hasn't been activated.
+  ExpectNotRestored(
+      {
+          BackForwardCacheMetrics::NotRestoredReason::kBackForwardCacheDisabled,
+          BackForwardCacheMetrics::NotRestoredReason::
+              kBackForwardCacheDisabledByLowMemory,
+      },
+      {}, {}, {}, {}, FROM_HERE);
+
+  // Ensure that the BackForwardCache trial still hasn't been activated, and the
+  // BackForwardCache_NoMemoryLimit_Trial trial stays active.
   EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
       base::FeatureList::GetFieldTrial(features::kBackForwardCache)
           ->trial_name()));
+  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(
+          features::kBackForwardCache_NoMemoryLimit_Trial)
+          ->trial_name()));
 }
 
 // Trigger network reqeuests, then navigate from A to B, then go back.
@@ -1652,23 +1679,56 @@
     : public BackForwardCacheBrowserTest {
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
+    BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
+
     // Set the value of memory threshold less than the physical memory and check
     // if back-forward cache is enabled or not.
     std::string memory_threshold =
         base::NumberToString(base::SysInfo::AmountOfPhysicalMemoryMB() - 1);
-    EnableFeatureAndSetParams(features::kBackForwardCacheMemoryControls,
-                              "memory_threshold_for_back_forward_cache_in_mb",
-                              memory_threshold);
-    EnableFeatureAndSetParams(blink::features::kLoadingTasksUnfreezable,
-                              "max_buffered_bytes_per_process", "1000");
-
-    BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        {{features::kBackForwardCacheMemoryControls,
+          {{"memory_threshold_for_back_forward_cache_in_mb",
+            memory_threshold}}},
+         {features::kBackForwardCache_NoMemoryLimit_Trial, {}},
+         {blink::features::kLoadingTasksUnfreezable, {}}},
+        {});
   }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// Navigate from A to B and go back.
+// Ensure that the BackForwardCache_NoMemoryLimit_Trial and the
+// BackForwardCache trials got activated as expected on high-memory devices
+// when the BackForwardCache feature is enabled.
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestForHighMemoryDevices,
                        EnableBFCacheForHighMemoryDevices) {
+  // Ensure that the BackForwardCache and the
+  // BackForwardCache_NoMemoryLimit_Trial trials start active
+  // on high-memory devices when the BackForwardCache feature is enabled,
+  // because IsBackForwardCacheEnabled() got queried already before the test
+  // starts.
+  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(features::kBackForwardCache)
+          ->trial_name()));
+  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(
+          features::kBackForwardCache_NoMemoryLimit_Trial)
+          ->trial_name()));
+
+  EXPECT_TRUE(IsBackForwardCacheEnabled());
+
+  // Ensure that the BackForwardCache and the
+  // BackForwardCache_NoMemoryLimit_Trial trial stays active after
+  // querying IsBackForwardCacheEnabled().
+  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(features::kBackForwardCache)
+          ->trial_name()));
+  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(
+          features::kBackForwardCache_NoMemoryLimit_Trial)
+          ->trial_name()));
+
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
@@ -1683,6 +1743,16 @@
   // 3) A should be stored in back-forward cache because the physical memory is
   // greater than the memory threshold.
   EXPECT_TRUE(rfh_a->IsInBackForwardCache());
+
+  // Ensure that the BackForwardCache and the
+  // BackForwardCache_NoMemoryLimit_Trial trial stays active.
+  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(features::kBackForwardCache)
+          ->trial_name()));
+  EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(
+          features::kBackForwardCache_NoMemoryLimit_Trial)
+          ->trial_name()));
 }
 
 // Trigger network reqeuests, then navigate from A to B, then go back.
@@ -1745,6 +1815,88 @@
           ->trial_name()));
 }
 
+// Tests for high memory devices that have the BackForwardCache feature flag
+// disabled.
+class BackForwardCacheBrowserTestForHighMemoryDevicesWithBFCacheDisabled
+    : public BackForwardCacheBrowserTest {
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
+
+    // Set the value of memory threshold less than the physical memory and check
+    // if back-forward cache is enabled or not.
+    std::string memory_threshold =
+        base::NumberToString(base::SysInfo::AmountOfPhysicalMemoryMB() - 1);
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/
+        {{features::kBackForwardCacheMemoryControls,
+          {{"memory_threshold_for_back_forward_cache_in_mb",
+            memory_threshold}}},
+         {features::kBackForwardCache_NoMemoryLimit_Trial, {}},
+         {blink::features::kLoadingTasksUnfreezable, {}}},
+        /*disabled_features=*/
+        {features::kBackForwardCache});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Ensure that the BackForwardCache_NoMemoryLimit_Trial does not get activated
+// on high-memory devices that have the BackForwardCache feature disabled.
+IN_PROC_BROWSER_TEST_F(
+    BackForwardCacheBrowserTestForHighMemoryDevicesWithBFCacheDisabled,
+    HighMemoryDevicesWithBFacheDisabled) {
+  // Ensure that BackForwardCache_NoMemoryLimit_Trial trials starts inactive
+  // on high-memory devices that have the BackForwardCache feature disabled.
+  EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(
+          features::kBackForwardCache_NoMemoryLimit_Trial)
+          ->trial_name()));
+
+  // Ensure that IsBackForwardCacheEnabled() returns false, because the
+  // BackForwardCache feature is disabled.
+  EXPECT_FALSE(IsBackForwardCacheEnabled());
+
+  // Ensure that the BackForwardCache_NoMemoryLimit_Trial trial stays inactive
+  // after querying IsBackForwardCacheEnabled().
+  EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(
+          features::kBackForwardCache_NoMemoryLimit_Trial)
+          ->trial_name()));
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // 1) Navigate to A.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = current_frame_host();
+  RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
+
+  // 2) Navigate to B.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b));
+
+  // 3) A shouldn't be stored in back-forward cache because the BackForwardCache
+  // feature is disabled.
+  delete_observer_rfh_a.WaitUntilDeleted();
+
+  // 4) Go back to check that only kBackForwardCacheDisabled is recorded.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+
+  ExpectNotRestored(
+      {
+          BackForwardCacheMetrics::NotRestoredReason::kBackForwardCacheDisabled,
+      },
+      {}, {}, {}, {}, FROM_HERE);
+
+  // Ensure that the BackForwardCache_NoMemoryLimit_Trial trial stays inactive.
+  EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
+      base::FeatureList::GetFieldTrial(
+          features::kBackForwardCache_NoMemoryLimit_Trial)
+          ->trial_name()));
+}
+
 // Start an inifite dialogs in JS, yielding after each. The first dialog should
 // be dismissed by navigation. The later dialogs should be handled gracefully
 // and not appear while in BFCache. Finally, when the page comes out of BFCache,
diff --git a/content/browser/buckets/bucket_host.cc b/content/browser/buckets/bucket_host.cc
index fc3b5b8..6456c9f 100644
--- a/content/browser/buckets/bucket_host.cc
+++ b/content/browser/buckets/bucket_host.cc
@@ -30,10 +30,6 @@
   return remote;
 }
 
-void BucketHost::OnUpdate(const storage::BucketInfo& bucket_info) {
-  bucket_info_ = bucket_info;
-}
-
 void BucketHost::Persist(PersistCallback callback) {
   if (bucket_info_.persistent) {
     std::move(callback).Run(true, true);
@@ -48,8 +44,12 @@
   }
   if (it->second.Run(blink::PermissionType::DURABLE_STORAGE) ==
       blink::mojom::PermissionStatus::GRANTED) {
-    bucket_manager_host_->UpdateBucketPersistence(
-        bucket_info_.id, true, base::BindOnce(std::move(callback), true));
+    GetQuotaManagerProxy()->UpdateBucketPersistence(
+        bucket_info_.id, /*persistent=*/true,
+        base::SequencedTaskRunnerHandle::Get(),
+        base::BindOnce(
+            &BucketHost::DidUpdateBucket, weak_factory_.GetWeakPtr(),
+            base::BindOnce(std::move(callback), /*persisted=*/true)));
   } else {
     std::move(callback).Run(false, false);
   }
@@ -60,8 +60,10 @@
 }
 
 void BucketHost::Estimate(EstimateCallback callback) {
-  // TODO(ayui): Add implementation once connected to QuotaManager.
-  std::move(callback).Run(0, 0, true);
+  GetQuotaManagerProxy()->GetBucketUsageAndQuota(
+      bucket_info_, base::SequencedTaskRunnerHandle::Get(),
+      base::BindOnce(&BucketHost::DidGetUsageAndQuota,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void BucketHost::Durability(DurabilityCallback callback) {
@@ -69,8 +71,10 @@
 }
 
 void BucketHost::SetExpires(base::Time expires, SetExpiresCallback callback) {
-  bucket_manager_host_->UpdateBucketExpiration(bucket_info_.id, expires,
-                                               std::move(callback));
+  GetQuotaManagerProxy()->UpdateBucketExpiration(
+      bucket_info_.id, expires, base::SequencedTaskRunnerHandle::Get(),
+      base::BindOnce(&BucketHost::DidUpdateBucket, weak_factory_.GetWeakPtr(),
+                     std::move(callback)));
 }
 
 void BucketHost::Expires(ExpiresCallback callback) {
@@ -88,4 +92,31 @@
   bucket_manager_host_->RemoveBucketHost(bucket_info_.name);
 }
 
+storage::QuotaManagerProxy* BucketHost::GetQuotaManagerProxy() {
+  return bucket_manager_host_->GetQuotaManagerProxy();
+}
+
+void BucketHost::DidUpdateBucket(
+    base::OnceCallback<void(bool)> callback,
+    storage::QuotaErrorOr<storage::BucketInfo> bucket_info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!bucket_info.ok()) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  bucket_info_ = bucket_info.value();
+  std::move(callback).Run(true);
+}
+
+void BucketHost::DidGetUsageAndQuota(EstimateCallback callback,
+                                     blink::mojom::QuotaStatusCode code,
+                                     int64_t usage,
+                                     int64_t quota) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::move(callback).Run(usage, quota,
+                          code == blink::mojom::QuotaStatusCode::kOk);
+}
+
 }  // namespace content
diff --git a/content/browser/buckets/bucket_host.h b/content/browser/buckets/bucket_host.h
index f94bdb8..f408c4e 100644
--- a/content/browser/buckets/bucket_host.h
+++ b/content/browser/buckets/bucket_host.h
@@ -6,7 +6,10 @@
 #define CONTENT_BROWSER_BUCKETS_BUCKET_HOST_H_
 
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 #include "components/services/storage/public/cpp/buckets/bucket_info.h"
+#include "components/services/storage/public/cpp/quota_error_or.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "third_party/blink/public/common/permissions/permission_utils.h"
@@ -14,6 +17,10 @@
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 
+namespace storage {
+class QuotaManagerProxy;
+}  // namespace storage
+
 namespace content {
 
 class BucketManagerHost;
@@ -46,8 +53,6 @@
   mojo::PendingRemote<blink::mojom::BucketHost> CreateStorageBucketBinding(
       const PermissionDecisionCallback& permission_decision);
 
-  void OnUpdate(const storage::BucketInfo& bucket_info);
-
   // blink::mojom::BucketHost
   void Persist(PersistCallback callback) override;
   void Persisted(PersistedCallback callback) override;
@@ -59,6 +64,18 @@
  private:
   void OnReceiverDisconnected();
 
+  storage::QuotaManagerProxy* GetQuotaManagerProxy();
+
+  void DidUpdateBucket(base::OnceCallback<void(bool)> callback,
+                       storage::QuotaErrorOr<storage::BucketInfo> bucket_info);
+
+  void DidGetUsageAndQuota(EstimateCallback callback,
+                           blink::mojom::QuotaStatusCode code,
+                           int64_t usage,
+                           int64_t quota);
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
   // Raw pointer use is safe here because BucketManagerHost owns this
   // BucketHost.
   raw_ptr<BucketManagerHost> bucket_manager_host_;
@@ -73,6 +90,8 @@
   // permission to use certain web features (namely, DURABLE_STORAGE).
   std::map<mojo::ReceiverId, PermissionDecisionCallback>
       permission_decider_map_;
+
+  base::WeakPtrFactory<BucketHost> weak_factory_{this};
 };
 
 }  // namespace content
diff --git a/content/browser/buckets/bucket_manager_host.cc b/content/browser/buckets/bucket_manager_host.cc
index c416115..94f9ad89 100644
--- a/content/browser/buckets/bucket_manager_host.cc
+++ b/content/browser/buckets/bucket_manager_host.cc
@@ -129,24 +129,8 @@
   bucket_map_.erase(bucket_name);
 }
 
-void BucketManagerHost::UpdateBucketExpiration(
-    storage::BucketId id,
-    const base::Time& expiration,
-    base::OnceCallback<void(bool)> callback) {
-  manager_->quota_manager_proxy()->UpdateBucketExpiration(
-      id, expiration, base::SequencedTaskRunnerHandle::Get(),
-      base::BindOnce(&BucketManagerHost::DidUpdateBucket,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void BucketManagerHost::UpdateBucketPersistence(
-    storage::BucketId id,
-    bool persistent,
-    base::OnceCallback<void(bool)> callback) {
-  manager_->quota_manager_proxy()->UpdateBucketPersistence(
-      id, persistent, base::SequencedTaskRunnerHandle::Get(),
-      base::BindOnce(&BucketManagerHost::DidUpdateBucket,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
+storage::QuotaManagerProxy* BucketManagerHost::GetQuotaManagerProxy() {
+  return manager_->quota_manager_proxy().get();
 }
 
 void BucketManagerHost::OnReceiverDisconnect() {
@@ -193,24 +177,4 @@
   std::move(callback).Run(true);
 }
 
-void BucketManagerHost::DidUpdateBucket(
-    base::OnceCallback<void(bool)> callback,
-    storage::QuotaErrorOr<storage::BucketInfo> bucket_info) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!bucket_info.ok()) {
-    std::move(callback).Run(false);
-    return;
-  }
-
-  auto bucket = bucket_map_.find(bucket_info->name);
-  if (bucket == bucket_map_.end()) {
-    std::move(callback).Run(false);
-    return;
-  }
-
-  bucket->second->OnUpdate(bucket_info.value());
-  std::move(callback).Run(true);
-}
-
 }  // namespace content
diff --git a/content/browser/buckets/bucket_manager_host.h b/content/browser/buckets/bucket_manager_host.h
index b66475d..a8c7179 100644
--- a/content/browser/buckets/bucket_manager_host.h
+++ b/content/browser/buckets/bucket_manager_host.h
@@ -8,6 +8,7 @@
 #include <map>
 
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "components/services/storage/public/cpp/quota_error_or.h"
 #include "content/browser/buckets/bucket_host.h"
@@ -21,6 +22,7 @@
 
 namespace storage {
 struct BucketInfo;
+class QuotaManagerProxy;
 }  // namespace storage
 
 namespace content {
@@ -67,13 +69,7 @@
 
   void RemoveBucketHost(const std::string& name);
 
-  // These functions update bucket policies in the quota database.
-  void UpdateBucketExpiration(storage::BucketId bucket,
-                              const base::Time& expiration,
-                              base::OnceCallback<void(bool)> callback);
-  void UpdateBucketPersistence(storage::BucketId bucket,
-                               bool persistent,
-                               base::OnceCallback<void(bool)> callback);
+  storage::QuotaManagerProxy* GetQuotaManagerProxy();
 
  private:
   // Called when a receiver in the receiver set is disconnected.
@@ -88,9 +84,6 @@
                        DeleteBucketCallback callback,
                        blink::mojom::QuotaStatusCode status);
 
-  void DidUpdateBucket(base::OnceCallback<void(bool)> callback,
-                       storage::QuotaErrorOr<storage::BucketInfo> bucket_info);
-
   SEQUENCE_CHECKER(sequence_checker_);
 
   // Raw pointer is safe because BucketManager owns this BucketManagerHost, and
diff --git a/content/browser/notifications/blink_notification_service_impl.cc b/content/browser/notifications/blink_notification_service_impl.cc
index a3df152..0c59d3c 100644
--- a/content/browser/notifications/blink_notification_service_impl.cc
+++ b/content/browser/notifications/blink_notification_service_impl.cc
@@ -19,6 +19,7 @@
 #include "content/public/browser/permission_controller.h"
 #include "content/public/browser/platform_notification_service.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/weak_document_ptr.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "third_party/blink/public/common/notifications/notification_constants.h"
@@ -84,6 +85,7 @@
     RenderProcessHost* render_process_host,
     const url::Origin& origin,
     const GURL& document_url,
+    const WeakDocumentPtr& weak_document_ptr,
     mojo::PendingReceiver<blink::mojom::NotificationService> receiver)
     : notification_context_(notification_context),
       browser_context_(browser_context),
@@ -91,6 +93,7 @@
       render_process_host_id_(render_process_host->GetID()),
       origin_(origin),
       document_url_(document_url),
+      weak_document_ptr_(weak_document_ptr),
       receiver_(this, std::move(receiver)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(notification_context_);
@@ -179,16 +182,30 @@
 BlinkNotificationServiceImpl::CheckPermissionStatus() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  RenderProcessHost* rph = RenderProcessHost::FromID(render_process_host_id_);
-  if (!rph)
-    return blink::mojom::PermissionStatus::DENIED;
-
   // TODO(crbug.com/987654): It is odd that a service instance can be created
   // for cross-origin subframes, yet the instance is completely oblivious of
   // whether it is serving a top-level browsing context or an embedded one.
-  return browser_context_->GetPermissionController()
-      ->GetPermissionStatusForWorker(blink::PermissionType::NOTIFICATIONS, rph,
-                                     origin_);
+
+  // An empty |document_url_| should mean this is initiated by a worker.
+  // See: `RenderProcessHostImpl::CreateNotificationService` description in
+  // content/browser/renderer_host/render_process_host_impl.h
+  if (!document_url_.is_empty()) {
+    RenderFrameHost* rfh = weak_document_ptr_.AsRenderFrameHostIfValid();
+    if (!rfh) {
+      return blink::mojom::PermissionStatus::DENIED;
+    }
+    return browser_context_->GetPermissionController()
+        ->GetPermissionStatusForCurrentDocument(
+            blink::PermissionType::NOTIFICATIONS, rfh);
+  } else {
+    RenderProcessHost* rph = RenderProcessHost::FromID(render_process_host_id_);
+    if (!rph) {
+      return blink::mojom::PermissionStatus::DENIED;
+    }
+    return browser_context_->GetPermissionController()
+        ->GetPermissionStatusForWorker(blink::PermissionType::NOTIFICATIONS,
+                                       rph, origin_);
+  }
 }
 
 bool BlinkNotificationServiceImpl::ValidateNotificationDataAndResources(
diff --git a/content/browser/notifications/blink_notification_service_impl.h b/content/browser/notifications/blink_notification_service_impl.h
index dc8de24..20db1f6 100644
--- a/content/browser/notifications/blink_notification_service_impl.h
+++ b/content/browser/notifications/blink_notification_service_impl.h
@@ -13,6 +13,7 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/weak_document_ptr.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -44,6 +45,7 @@
       RenderProcessHost* render_process_host,
       const url::Origin& origin,
       const GURL& document_url,
+      const WeakDocumentPtr& weak_document_ptr,
       mojo::PendingReceiver<blink::mojom::NotificationService> receiver);
 
   BlinkNotificationServiceImpl(const BlinkNotificationServiceImpl&) = delete;
@@ -112,7 +114,10 @@
   url::Origin origin_;
   // The document url that this notification service is communicating with.
   // This is empty when used for a worker.
-  GURL document_url_;
+  const GURL document_url_;
+  // The weak document pointer that this notification service is communicating
+  // with. This is valid only for a document.
+  const WeakDocumentPtr weak_document_ptr_;
 
   mojo::Receiver<blink::mojom::NotificationService> receiver_;
 
diff --git a/content/browser/notifications/blink_notification_service_impl_unittest.cc b/content/browser/notifications/blink_notification_service_impl_unittest.cc
index fe015fa..41fb02d 100644
--- a/content/browser/notifications/blink_notification_service_impl_unittest.cc
+++ b/content/browser/notifications/blink_notification_service_impl_unittest.cc
@@ -131,6 +131,7 @@
         embedded_worker_helper_->context_wrapper(), &render_process_host_,
         url::Origin::Create(GURL(kTestOrigin)),
         /*document_url=*/GURL(),
+        /*weak_document_ptr=*/WeakDocumentPtr(),
         notification_service_remote_.BindNewPipeAndPassReceiver());
 
     // Provide a mock permission manager to the |browser_context_|.
@@ -405,7 +406,8 @@
             browser_context_.GetPermissionControllerDelegate());
 
     ON_CALL(*mock_permission_manager,
-            GetPermissionStatus(blink::PermissionType::NOTIFICATIONS, _, _))
+            GetPermissionStatusForCurrentDocument(
+                blink::PermissionType::NOTIFICATIONS, _))
         .WillByDefault(Return(permission_status));
     ON_CALL(*mock_permission_manager,
             GetPermissionStatusForWorker(blink::PermissionType::NOTIFICATIONS,
diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc
index fc24d16..61ac1d77 100644
--- a/content/browser/notifications/platform_notification_context_impl.cc
+++ b/content/browser/notifications/platform_notification_context_impl.cc
@@ -284,11 +284,12 @@
     RenderProcessHost* render_process_host,
     const url::Origin& origin,
     const GURL& document_url,
+    const WeakDocumentPtr& weak_document_ptr,
     mojo::PendingReceiver<blink::mojom::NotificationService> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   services_.push_back(std::make_unique<BlinkNotificationServiceImpl>(
       this, browser_context_, service_worker_context_, render_process_host,
-      origin, document_url, std::move(receiver)));
+      origin, document_url, weak_document_ptr, std::move(receiver)));
 }
 
 void PlatformNotificationContextImpl::RemoveService(
diff --git a/content/browser/notifications/platform_notification_context_impl.h b/content/browser/notifications/platform_notification_context_impl.h
index 69f000e..424fae7 100644
--- a/content/browser/notifications/platform_notification_context_impl.h
+++ b/content/browser/notifications/platform_notification_context_impl.h
@@ -80,6 +80,7 @@
       RenderProcessHost* render_process_host,
       const url::Origin& origin,
       const GURL& document_url,
+      const WeakDocumentPtr& weak_document_ptr,
       mojo::PendingReceiver<blink::mojom::NotificationService> receiver);
 
   // Removes |service| from the list of owned services, for example because the
diff --git a/content/browser/renderer_host/code_cache_host_impl.cc b/content/browser/renderer_host/code_cache_host_impl.cc
index 364a513..c076b42 100644
--- a/content/browser/renderer_host/code_cache_host_impl.cc
+++ b/content/browser/renderer_host/code_cache_host_impl.cc
@@ -346,9 +346,9 @@
     return;
   }
 
-  auto read_callback =
-      base::BindOnce(&CodeCacheHostImpl::OnReceiveCachedCode,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+  auto read_callback = base::BindOnce(
+      &CodeCacheHostImpl::OnReceiveCachedCode, weak_ptr_factory_.GetWeakPtr(),
+      cache_type, base::TimeTicks::Now(), std::move(callback));
   code_cache->FetchEntry(url, *origin_lock, network_isolation_key_,
                          std::move(read_callback));
 }
@@ -424,9 +424,17 @@
   return generated_code_cache_context_->generated_wasm_code_cache();
 }
 
-void CodeCacheHostImpl::OnReceiveCachedCode(FetchCachedCodeCallback callback,
-                                            const base::Time& response_time,
-                                            mojo_base::BigBuffer data) {
+void CodeCacheHostImpl::OnReceiveCachedCode(
+    blink::mojom::CodeCacheType cache_type,
+    base::TimeTicks start_time,
+    FetchCachedCodeCallback callback,
+    const base::Time& response_time,
+    mojo_base::BigBuffer data) {
+  if (cache_type == blink::mojom::CodeCacheType::kJavascript &&
+      data.size() > 0) {
+    base::UmaHistogramTimes("SiteIsolatedCodeCache.JS.FetchCodeCache",
+                            base::TimeTicks::Now() - start_time);
+  }
   std::move(callback).Run(response_time, std::move(data));
 }
 
diff --git a/content/browser/renderer_host/code_cache_host_impl.h b/content/browser/renderer_host/code_cache_host_impl.h
index 37868e01..478b66a6 100644
--- a/content/browser/renderer_host/code_cache_host_impl.h
+++ b/content/browser/renderer_host/code_cache_host_impl.h
@@ -102,7 +102,9 @@
 
   // Helpers.
   GeneratedCodeCache* GetCodeCache(blink::mojom::CodeCacheType cache_type);
-  void OnReceiveCachedCode(FetchCachedCodeCallback callback,
+  void OnReceiveCachedCode(blink::mojom::CodeCacheType cache_type,
+                           base::TimeTicks start_time,
+                           FetchCachedCodeCallback callback,
                            const base::Time& response_time,
                            mojo_base::BigBuffer data);
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index ed46d71..8d0465d 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -152,6 +152,7 @@
 #include "content/public/browser/render_widget_host_iterator.h"
 #include "content/public/browser/resource_coordinator_service.h"
 #include "content/public/browser/site_isolation_policy.h"
+#include "content/public/browser/weak_document_ptr.h"
 #include "content/public/browser/webrtc_log.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_client.h"
@@ -2054,12 +2055,20 @@
     mojo::PendingReceiver<blink::mojom::NotificationService> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  GURL document_url;
-  if (RenderFrameHost* rfh = RenderFrameHost::FromID(GetID(), render_frame_id))
-    document_url = rfh->GetLastCommittedURL();
+  // For workers:
+  if (render_frame_id == MSG_ROUTING_NONE) {
+    storage_partition_impl_->GetPlatformNotificationContext()->CreateService(
+        this, origin, /*document_url=*/GURL(),
+        /*weak_document_ptr=*/WeakDocumentPtr(), std::move(receiver));
+    return;
+  }
 
+  // For document:
+  RenderFrameHost* rfh = RenderFrameHost::FromID(GetID(), render_frame_id);
+  CHECK(rfh);
   storage_partition_impl_->GetPlatformNotificationContext()->CreateService(
-      this, origin, document_url, std::move(receiver));
+      this, origin, rfh->GetLastCommittedURL(), rfh->GetWeakDocumentPtr(),
+      std::move(receiver));
 }
 
 void RenderProcessHostImpl::CreateWebSocketConnector(
diff --git a/content/common/content_navigation_policy.cc b/content/common/content_navigation_policy.cc
index fe1bd6b..9028afdd2d 100644
--- a/content/common/content_navigation_policy.cc
+++ b/content/common/content_navigation_policy.cc
@@ -13,6 +13,11 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 
+namespace features {
+const base::Feature kBackForwardCache_NoMemoryLimit_Trial{
+    "BackForwardCache_NoMemoryLimit_Trial", base::FEATURE_ENABLED_BY_DEFAULT};
+}
+
 namespace content {
 
 bool DeviceHasEnoughMemoryForBackForwardCache() {
@@ -55,16 +60,42 @@
 }
 
 bool IsBackForwardCacheEnabled() {
-  if (!DeviceHasEnoughMemoryForBackForwardCache())
+  bool has_enough_memory = DeviceHasEnoughMemoryForBackForwardCache();
+  if (!has_enough_memory) {
+    // When the device does not have enough memory for BackForwardCache, return
+    // false so we won't try to put things in the back/forward cache.
+    // Also, trigger the activation of the BackForwardCache_NoMemoryLimit_Trial
+    // field trial by querying the feature flag. With this, we guarantee that
+    // all devices that do not have enough memory for BackForwardCache will be
+    // included in that field trial. See case #1 in the comment for the
+    // BackForwardCache_NoMemoryLimit_Trial in the header file for more details.
+    base::FeatureList::IsEnabled(
+        features::kBackForwardCache_NoMemoryLimit_Trial);
     return false;
+  }
 
   if (IsBackForwardCacheDisabledByCommandLine())
     return false;
 
   // The feature needs to be checked last, because checking the feature
   // activates the field trial and assigns the client either to a control or an
-  // experiment group - such assignment should be final.
-  return base::FeatureList::IsEnabled(features::kBackForwardCache);
+  // experiment group - such assignment should be final. This allows us to keep
+  // the BackForwardCache field trial to include only devices that have enough
+  // memory for BackForwardCache, and those devices only.
+  if (base::FeatureList::IsEnabled(features::kBackForwardCache)) {
+    // When the device does have enough memory for BackForwardCache, return
+    // true so we won't try to put things in the back/forward cache. Also,
+    // trigger the activation of the BackForwardCache_NoMemoryLimit_Trial field
+    // trial by querying the feature flag. With this, we guarantee that all
+    // devices that do have enough memory for BackForwardCache and have the
+    // BackForwardCache feature flag enabled will be included in that field
+    // trial. See case #2 in the comment for the
+    // BackForwardCache_NoMemoryLimit_Trial in the header file for more details.
+    base::FeatureList::IsEnabled(
+        features::kBackForwardCache_NoMemoryLimit_Trial);
+    return true;
+  }
+  return false;
 }
 
 bool IsSameSiteBackForwardCacheEnabled() {
diff --git a/content/common/content_navigation_policy.h b/content/common/content_navigation_policy.h
index 72c09e51..34be63d 100644
--- a/content/common/content_navigation_policy.h
+++ b/content/common/content_navigation_policy.h
@@ -5,11 +5,28 @@
 #ifndef CONTENT_COMMON_CONTENT_NAVIGATION_POLICY_H_
 #define CONTENT_COMMON_CONTENT_NAVIGATION_POLICY_H_
 
+#include "base/feature_list.h"
 #include "content/common/content_export.h"
 
 #include <array>
 #include <string>
 
+namespace features {
+
+// The BackForwardCache_NoMemoryLimit_Trial feature flag's sole purpose is to
+// make it possible to get a group for "all devices except when BackForwardCache
+// feature is specifically disabled due to non-memory-control reasons". This is
+// done by querying the flag if and only if the device satisifes one of the
+// following:
+// 1) The device does not have enough memory for BackForwardCache, or
+// 2) The device has enough memory and the BackForwardCache feature is enabled.
+// With that, we will include the devices that don't have enough memory while
+// avoiding activating the BackForwardCache experiment, and won’t include
+// devices that do have enough memory but have the BackForwardCache flag
+// disabled.
+CONTENT_EXPORT extern const base::Feature kBackForwardCache_NoMemoryLimit_Trial;
+}  // namespace features
+
 namespace content {
 
 CONTENT_EXPORT bool IsBackForwardCacheEnabled();
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index cf06cb6..bf5159a 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -254,7 +254,7 @@
 // crbug.com/1294648
 const base::FeatureParam<std::string> kDrDcBlockListByDevice{
     &kEnableDrDc, "BlockListByDevice", "LF9810_2GB"};
-#endif
+#endif  // BUILDFLAG(IS_ANDROID)
 
 // Enable SkiaRenderer Dawn graphics backend. On Windows this will use D3D12,
 // and on Linux this will use Vulkan.
@@ -272,6 +272,11 @@
 const base::Feature kReduceOpsTaskSplitting{
     "ReduceOpsTaskSplitting", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enabling this will make the GPU decode path use a mock implementation of
+// discardable memory.
+const base::Feature kNoDiscardableMemoryForGpuDecodePath{
+    "NoDiscardableMemoryForGpuDecodePath", base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool IsUsingVulkan() {
 #if BUILDFLAG(IS_ANDROID)
   // Force on if Vulkan feature is enabled from command line.
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h
index b4438fe0..d17e58df 100644
--- a/gpu/config/gpu_finch_features.h
+++ b/gpu/config/gpu_finch_features.h
@@ -65,6 +65,8 @@
 
 GPU_EXPORT extern const base::Feature kReduceOpsTaskSplitting;
 
+GPU_EXPORT extern const base::Feature kNoDiscardableMemoryForGpuDecodePath;
+
 GPU_EXPORT extern const base::Feature kEnableDrDc;
 
 GPU_EXPORT extern const base::Feature kForceGpuMainThreadToNormalPriorityDrDc;
diff --git a/infra/config/chops-weetbix-dev.cfg b/infra/config/chops-weetbix-dev.cfg
index 25a460a..a1205f9 100644
--- a/infra/config/chops-weetbix-dev.cfg
+++ b/infra/config/chops-weetbix-dev.cfg
@@ -15,18 +15,28 @@
   test_name_rules {
     name: "Blink Web Tests"
     # To match blink_web_tests as well as webgpu_blink_web_tests and any others.
-    pattern: "^ninja://:(?P<target>\\w*blink_web_tests)/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
-    like_template: "ninja://:${target}/%${testname}%"
+    pattern: "^ninja://:(?P<target>\\w*blink_web_tests)/(virtual/[^/]+/)?(?P<test>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
+    like_template: "ninja://:${target}/%${test}%"
   }
   test_name_rules {
     name: "Google Test (Value-parameterized)"
-    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<case>\\w+)/[\\w.]+$"
-    like_template: "ninja:${target}/%${suite}.${case}%"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<test>\\w+)/[\\w.]+$"
+    like_template: "ninja:${target}/%${suite}.${test}%"
   }
   test_name_rules {
     name: "Google Test (Type-parameterized)"
-    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<case>\\w+)$"
-    like_template: "ninja:${target}/%${suite}/%.${case}"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<test>\\w+)$"
+    like_template: "ninja:${target}/%${suite}/%.${test}"
+  }
+  test_name_rules {
+    name: "JUnit Test (Parameterized)"
+    # Matches parameterized JUnit tests like:
+    # ninja://android_webview/test:webview_instrumentation_test_apk/org.chromium.android_webview.test.MyTest#testFoo__parameter1
+    # Also matches tests parameterized with different command line flags,
+    # as constructed by https://source.chromium.org/chromium/chromium/src/+/main:build/android/pylib/instrumentation/instrumentation_test_instance.py?q=%22def%20GetUniqueTestName(%22
+    # E.g. ninja://chrome/android:chrome_public_test_apk/org.chromium.chrome.browser.omnibox.OmniboxTest#testDefaultText_with___disable_features=SpannableInlineAutocomplete
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(?P<class>[\\w$.]+)#(?P<test>\\w+?)(?P<sep>__|_with_)[\\w.=,]+$"
+    like_template: "ninja:${target}/${class}#${test}${sep}%"
   }
 }
 
diff --git a/infra/config/chops-weetbix.cfg b/infra/config/chops-weetbix.cfg
index 79b0601..e18ab06 100644
--- a/infra/config/chops-weetbix.cfg
+++ b/infra/config/chops-weetbix.cfg
@@ -11,18 +11,28 @@
   test_name_rules {
     name: "Blink Web Tests"
     # To match blink_web_tests as well as webgpu_blink_web_tests and any others.
-    pattern: "^ninja://:(?P<target>\\w*blink_web_tests)/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
-    like_template: "ninja://:${target}/%${testname}%"
+    pattern: "^ninja://:(?P<target>\\w*blink_web_tests)/(virtual/[^/]+/)?(?P<test>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
+    like_template: "ninja://:${target}/%${test}%"
   }
   test_name_rules {
     name: "Google Test (Value-parameterized)"
-    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<case>\\w+)/[\\w.]+$"
-    like_template: "ninja:${target}/%${suite}.${case}%"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<test>\\w+)/[\\w.]+$"
+    like_template: "ninja:${target}/%${suite}.${test}%"
   }
   test_name_rules {
     name: "Google Test (Type-parameterized)"
-    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<case>\\w+)$"
-    like_template: "ninja:${target}/%${suite}/%.${case}"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<test>\\w+)$"
+    like_template: "ninja:${target}/%${suite}/%.${test}"
+  }
+  test_name_rules {
+    name: "JUnit Test (Parameterized)"
+    # Matches parameterized JUnit tests like:
+    # ninja://android_webview/test:webview_instrumentation_test_apk/org.chromium.android_webview.test.MyTest#testFoo__parameter1
+    # Also matches tests parameterized with different command line flags,
+    # as constructed by https://source.chromium.org/chromium/chromium/src/+/main:build/android/pylib/instrumentation/instrumentation_test_instance.py?q=%22def%20GetUniqueTestName(%22
+    # E.g. ninja://chrome/android:chrome_public_test_apk/org.chromium.chrome.browser.omnibox.OmniboxTest#testDefaultText_with___disable_features=SpannableInlineAutocomplete
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(?P<class>[\\w$.]+)#(?P<test>\\w+?)(?P<sep>__|_with_)[\\w.=,]+$"
+    like_template: "ninja:${target}/${class}#${test}${sep}%"
   }
 }
 
diff --git a/infra/config/generated/luci/chops-weetbix-dev.cfg b/infra/config/generated/luci/chops-weetbix-dev.cfg
index 25a460a..a1205f9 100644
--- a/infra/config/generated/luci/chops-weetbix-dev.cfg
+++ b/infra/config/generated/luci/chops-weetbix-dev.cfg
@@ -15,18 +15,28 @@
   test_name_rules {
     name: "Blink Web Tests"
     # To match blink_web_tests as well as webgpu_blink_web_tests and any others.
-    pattern: "^ninja://:(?P<target>\\w*blink_web_tests)/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
-    like_template: "ninja://:${target}/%${testname}%"
+    pattern: "^ninja://:(?P<target>\\w*blink_web_tests)/(virtual/[^/]+/)?(?P<test>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
+    like_template: "ninja://:${target}/%${test}%"
   }
   test_name_rules {
     name: "Google Test (Value-parameterized)"
-    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<case>\\w+)/[\\w.]+$"
-    like_template: "ninja:${target}/%${suite}.${case}%"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<test>\\w+)/[\\w.]+$"
+    like_template: "ninja:${target}/%${suite}.${test}%"
   }
   test_name_rules {
     name: "Google Test (Type-parameterized)"
-    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<case>\\w+)$"
-    like_template: "ninja:${target}/%${suite}/%.${case}"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<test>\\w+)$"
+    like_template: "ninja:${target}/%${suite}/%.${test}"
+  }
+  test_name_rules {
+    name: "JUnit Test (Parameterized)"
+    # Matches parameterized JUnit tests like:
+    # ninja://android_webview/test:webview_instrumentation_test_apk/org.chromium.android_webview.test.MyTest#testFoo__parameter1
+    # Also matches tests parameterized with different command line flags,
+    # as constructed by https://source.chromium.org/chromium/chromium/src/+/main:build/android/pylib/instrumentation/instrumentation_test_instance.py?q=%22def%20GetUniqueTestName(%22
+    # E.g. ninja://chrome/android:chrome_public_test_apk/org.chromium.chrome.browser.omnibox.OmniboxTest#testDefaultText_with___disable_features=SpannableInlineAutocomplete
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(?P<class>[\\w$.]+)#(?P<test>\\w+?)(?P<sep>__|_with_)[\\w.=,]+$"
+    like_template: "ninja:${target}/${class}#${test}${sep}%"
   }
 }
 
diff --git a/infra/config/generated/luci/chops-weetbix.cfg b/infra/config/generated/luci/chops-weetbix.cfg
index 79b0601..e18ab06 100644
--- a/infra/config/generated/luci/chops-weetbix.cfg
+++ b/infra/config/generated/luci/chops-weetbix.cfg
@@ -11,18 +11,28 @@
   test_name_rules {
     name: "Blink Web Tests"
     # To match blink_web_tests as well as webgpu_blink_web_tests and any others.
-    pattern: "^ninja://:(?P<target>\\w*blink_web_tests)/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
-    like_template: "ninja://:${target}/%${testname}%"
+    pattern: "^ninja://:(?P<target>\\w*blink_web_tests)/(virtual/[^/]+/)?(?P<test>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
+    like_template: "ninja://:${target}/%${test}%"
   }
   test_name_rules {
     name: "Google Test (Value-parameterized)"
-    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<case>\\w+)/[\\w.]+$"
-    like_template: "ninja:${target}/%${suite}.${case}%"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<test>\\w+)/[\\w.]+$"
+    like_template: "ninja:${target}/%${suite}.${test}%"
   }
   test_name_rules {
     name: "Google Test (Type-parameterized)"
-    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<case>\\w+)$"
-    like_template: "ninja:${target}/%${suite}/%.${case}"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<test>\\w+)$"
+    like_template: "ninja:${target}/%${suite}/%.${test}"
+  }
+  test_name_rules {
+    name: "JUnit Test (Parameterized)"
+    # Matches parameterized JUnit tests like:
+    # ninja://android_webview/test:webview_instrumentation_test_apk/org.chromium.android_webview.test.MyTest#testFoo__parameter1
+    # Also matches tests parameterized with different command line flags,
+    # as constructed by https://source.chromium.org/chromium/chromium/src/+/main:build/android/pylib/instrumentation/instrumentation_test_instance.py?q=%22def%20GetUniqueTestName(%22
+    # E.g. ninja://chrome/android:chrome_public_test_apk/org.chromium.chrome.browser.omnibox.OmniboxTest#testDefaultText_with___disable_features=SpannableInlineAutocomplete
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(?P<class>[\\w$.]+)#(?P<test>\\w+?)(?P<sep>__|_with_)[\\w.=,]+$"
+    like_template: "ninja:${target}/${class}#${test}${sep}%"
   }
 }
 
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
index b98e189..fcb0ccc 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
@@ -78,9 +78,6 @@
 @property(nonatomic, strong)
     FormInputAccessoryViewHandler* formNavigationHandler;
 
-// The observer to determine when the keyboard dissapears and when it stays.
-@property(nonatomic, strong) KeyboardObserverHelper* keyboardObserver;
-
 // The object that provides suggestions while filling forms.
 @property(nonatomic, weak) id<FormInputSuggestionsProvider> provider;
 
@@ -184,8 +181,7 @@
                           name:UIApplicationDidEnterBackgroundNotification
                         object:nil];
 
-    _keyboardObserver = [[KeyboardObserverHelper alloc] init];
-    _keyboardObserver.consumer = self;
+    [[KeyboardObserverHelper sharedKeyboardObserver] addConsumer:self];
 
     // In BVC unit tests the password store doesn't exist. Skip creating the
     // fetcher.
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 76017fc..b57a074 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -504,9 +504,6 @@
 // The NewTabPageCoordinator associated with a WebState.
 - (NewTabPageCoordinator*)ntpCoordinatorForWebState:(web::WebState*)webState;
 
-// Whether the keyboard observer helper is viewed
-@property(nonatomic, strong) KeyboardObserverHelper* observer;
-
 // The coordinator that shows the Send Tab To Self UI.
 @property(nonatomic, strong) SendTabToSelfCoordinator* sendTabToSelfCoordinator;
 
@@ -584,7 +581,6 @@
 
     _footerFullscreenProgress = 1.0;
 
-    _observer = [[KeyboardObserverHelper alloc] init];
     if (browser)
       [self updateWithBrowser:browser];
   }
@@ -1258,7 +1254,8 @@
                                  isKindOfClass:[UITextField class]] ||
                              [firstResponder
                                  isKindOfClass:[UITextView class]] ||
-                             [self.observer isKeyboardOnScreen]];
+                             [[KeyboardObserverHelper sharedKeyboardObserver]
+                                 isKeyboardOnScreen]];
 }
 
 #pragma mark - UIResponder helpers
diff --git a/ios/chrome/browser/ui/util/keyboard_observer_helper.h b/ios/chrome/browser/ui/util/keyboard_observer_helper.h
index 599f94d..c721159 100644
--- a/ios/chrome/browser/ui/util/keyboard_observer_helper.h
+++ b/ios/chrome/browser/ui/util/keyboard_observer_helper.h
@@ -32,13 +32,23 @@
 // Helper to observe the keyboard and report updates.
 @interface KeyboardObserverHelper : NSObject
 
+// Singleton for KeyboardObserverHelper.
++ (instancetype)sharedKeyboardObserver;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Adds consumer of KeyboardObserverHelper.
+- (void)addConsumer:(id<KeyboardObserverHelperConsumer>)consumer;
+
 // Flag that indicates if the keyboard is on screen.
 // TODO(crbug.com/974226): look into deprecating keyboardOnScreen for
 // isKeyboardVisible.
 @property(nonatomic, readonly, getter=isKeyboardOnScreen) BOOL keyboardOnScreen;
 
-// The consumer to inform of the keyboard state changes.
-@property(nonatomic, weak) id<KeyboardObserverHelperConsumer> consumer;
+// Returns keyboard's height if it's docked, visible and not hardware.
+// Otherwise returns 0.
+// Note: This includes the keyboard accessory's height.
+@property(nonatomic, readonly) CGFloat visibleKeyboardHeight;
 
 @end
 
diff --git a/ios/chrome/browser/ui/util/keyboard_observer_helper.mm b/ios/chrome/browser/ui/util/keyboard_observer_helper.mm
index 239a657..abb6c62 100644
--- a/ios/chrome/browser/ui/util/keyboard_observer_helper.mm
+++ b/ios/chrome/browser/ui/util/keyboard_observer_helper.mm
@@ -13,8 +13,6 @@
 #error "This file requires ARC support."
 #endif
 
-// TODO(crbug.com/974226): look into making this a singleton with multiple
-// listeners.
 @interface KeyboardObserverHelper ()
 
 // Flag that indicates if the keyboard is on screen.
@@ -29,11 +27,41 @@
 // application lost focus in multiwindow mode.
 @property(nonatomic, weak) UIView* keyboardView;
 
+// Mutable array storing weak pointers to consumers.
+@property(nonatomic, strong) NSPointerArray* consumers;
+
 @end
 
 @implementation KeyboardObserverHelper
 
-#pragma mark - Public
+#pragma mark - Public class methods
+
++ (instancetype)sharedKeyboardObserver {
+  static KeyboardObserverHelper* sharedInstance;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    sharedInstance = [[KeyboardObserverHelper alloc] init];
+  });
+  return sharedInstance;
+}
+
+#pragma mark - Public instance methods
+
+- (void)addConsumer:(id<KeyboardObserverHelperConsumer>)consumer {
+  [self.consumers addPointer:(__bridge void*)consumer];
+  [self.consumers compact];
+}
+
+- (CGFloat)visibleKeyboardHeight {
+  if (self.keyboardState.isVisible && !self.keyboardState.isHardware &&
+      !self.keyboardState.isUndocked) {
+    return CGRectGetHeight(self.keyboardView.frame);
+  } else {
+    return 0;
+  }
+}
+
+#pragma mark - Private instance methods
 
 - (instancetype)init {
   self = [super init];
@@ -58,10 +86,14 @@
            selector:@selector(keyboardWillDidChangeFrame:)
                name:UIKeyboardDidChangeFrameNotification
              object:nil];
+    _consumers =
+        [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsWeakMemory];
   }
   return self;
 }
 
+#pragma mark - Private class methods
+
 + (UIView*)keyboardView {
   NSArray* windows = [UIApplication sharedApplication].windows;
   NSUInteger expectedMinWindows =
@@ -123,8 +155,11 @@
       keyboardView != self.keyboardView) {
     self.keyboardState = {isVisible, isUndocked, isHardware};
     self.keyboardView = keyboardView;
+    // Notify on the next cycle.
     dispatch_async(dispatch_get_main_queue(), ^{
-      [self.consumer keyboardWillChangeToState:self.keyboardState];
+      for (id<KeyboardObserverHelperConsumer> consumer in self.consumers) {
+        [consumer keyboardWillChangeToState:self.keyboardState];
+      }
     });
   }
 }
diff --git a/ios/chrome/browser/ui/util/keyboard_observer_helper_app_interface.mm b/ios/chrome/browser/ui/util/keyboard_observer_helper_app_interface.mm
index c2ab7569..f9ff057 100644
--- a/ios/chrome/browser/ui/util/keyboard_observer_helper_app_interface.mm
+++ b/ios/chrome/browser/ui/util/keyboard_observer_helper_app_interface.mm
@@ -13,12 +13,7 @@
 @implementation KeyboardObserverHelperAppInterface
 
 + (KeyboardObserverHelper*)appSharedInstance {
-  static KeyboardObserverHelper* sharedInstance;
-  static dispatch_once_t onceToken;
-  dispatch_once(&onceToken, ^{
-    sharedInstance = [[KeyboardObserverHelper alloc] init];
-  });
-  return sharedInstance;
+  return [KeyboardObserverHelper sharedKeyboardObserver];
 }
 
 @end
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 5f816ba5..4b517bb0 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 @@
-02c756e9b50ec56c9a3d00ee17d735b57695955e
\ No newline at end of file
+100565ad0afa6954a255575b57d3b8a9d0993461
\ 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 f69b908a..fcea66c 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 @@
-a0a9fda72f4a19815b06c8fc996921ad7a8bea4f
\ No newline at end of file
+27684947d3a44af681a8f9ad03c144fe4417513c
\ 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 337dd02..99e717a 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 @@
-edec3dbec2c7c33ad90616f5ff100ca423e5b58b
\ No newline at end of file
+effbc01232f9b43bac8840fb8a8cbed9be70c3b4
\ 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 1937b7a..3b34c58 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 @@
-cfa959bcaf3a44e7568507d5cfbd482f64d55fb8
\ No newline at end of file
+c4fd94c85adcec24c26946af5568f34e320bf90e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index 69b9ebce..97f8815 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e40954173493c7a1be91e5225d7a0ee1af8487a5
\ No newline at end of file
+72e3edc89c5cf42b7c25c665606e6de4e9cc268b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 5ad5946..7ae97791 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a45f0f2864fe5a1ff5c69947a09152befb61e559
\ No newline at end of file
+fcf0298443aa781e6b0e06ab7639ba318ffa7b6f
\ 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 0aead53..1da40d4 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 @@
-8e8d7cf0c6b33b0d891a11e8ad1203bb8fc5074e
\ No newline at end of file
+02b9c1b4b8e0dad8cf24f3396ee6195b06c991da
\ 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 e9347378..b62792d 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 @@
-b1584a5db43609ee4725cd61c2d2fe2664bc80fc
\ No newline at end of file
+8f4ebce5d7eb22ddfbc116fdcb1d850e501163b0
\ 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 c3f9aae..e20f2837 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 @@
-2d1d7bafafae33131f23f05a2e2041f40661818c
\ No newline at end of file
+42611eda077089a275437ead73e944b6d8aa25f3
\ 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 40de4d9..b5ce33e 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 @@
-ca6dbbf828a86fe0dffbd9e34379f3a57807cf15
\ No newline at end of file
+14e7d13c9a275285dc4fd830017efce5b7538096
\ No newline at end of file
diff --git a/net/reporting/reporting_header_parser.cc b/net/reporting/reporting_header_parser.cc
index ceb5879..2f81c43 100644
--- a/net/reporting/reporting_header_parser.cc
+++ b/net/reporting/reporting_header_parser.cc
@@ -277,16 +277,15 @@
     ReportingContext* context,
     const NetworkIsolationKey& network_isolation_key,
     const url::Origin& origin,
-    std::unique_ptr<base::Value> value) {
+    const base::Value::List& list) {
   DCHECK(GURL::SchemeIsCryptographic(origin.scheme()));
-  DCHECK(value->is_list());
 
   ReportingDelegate* delegate = context->delegate();
   ReportingCache* cache = context->cache();
 
   std::vector<ReportingEndpointGroup> parsed_header;
 
-  for (const auto& group_value : value->GetList()) {
+  for (const auto& group_value : list) {
     ReportingEndpointGroup parsed_endpoint_group;
     if (ProcessEndpointGroup(delegate, cache, network_isolation_key, origin,
                              group_value, &parsed_endpoint_group)) {
@@ -294,7 +293,7 @@
     }
   }
 
-  if (parsed_header.empty() && value->GetList().size() > 0) {
+  if (parsed_header.empty() && list.size() > 0) {
     RecordReportingHeaderType(ReportingHeaderType::kReportToInvalid);
   }
 
diff --git a/net/reporting/reporting_header_parser.h b/net/reporting/reporting_header_parser.h
index d18475c8..f2cb815 100644
--- a/net/reporting/reporting_header_parser.h
+++ b/net/reporting/reporting_header_parser.h
@@ -8,16 +8,13 @@
 #include <memory>
 
 #include "base/containers/flat_map.h"
+#include "base/values.h"
 #include "net/base/net_export.h"
 #include "net/http/structured_headers.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
-namespace base {
-class Value;
-}  // namespace base
-
 namespace net {
 
 class IsolationInfo;
@@ -53,7 +50,7 @@
       ReportingContext* context,
       const NetworkIsolationKey& network_isolation_key,
       const url::Origin& origin,
-      std::unique_ptr<base::Value> value);
+      const base::Value::List& list);
 
   // `isolation_info` here will be stored in the cache, associated with the
   // `reporting_source`. `network_isolation_key` is the NIK which will be
diff --git a/net/reporting/reporting_header_parser_fuzzer.cc b/net/reporting/reporting_header_parser_fuzzer.cc
index 7ad24814..1b7ece7c 100644
--- a/net/reporting/reporting_header_parser_fuzzer.cc
+++ b/net/reporting/reporting_header_parser_fuzzer.cc
@@ -15,6 +15,7 @@
 #include "net/reporting/reporting_header_parser.h"
 #include "net/reporting/reporting_policy.pb.h"
 #include "net/reporting/reporting_test_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -33,8 +34,8 @@
                                     policy);
   // Emulate what ReportingService::OnHeader does before calling
   // ReportingHeaderParser::ParseHeader.
-  std::unique_ptr<base::Value> data_value =
-      base::JSONReader::ReadDeprecated("[" + data_json + "]");
+  absl::optional<base::Value> data_value =
+      base::JSONReader::Read("[" + data_json + "]");
   if (!data_value)
     return;
 
@@ -42,7 +43,7 @@
   // testing/libfuzzer/proto and creating a separate converter.
   net::ReportingHeaderParser::ParseReportToHeader(
       &context, net::NetworkIsolationKey(),
-      url::Origin::Create(GURL("https://origin/")), std::move(data_value));
+      url::Origin::Create(GURL("https://origin/")), data_value->GetList());
   if (context.cache()->GetEndpointCount() == 0) {
     return;
   }
diff --git a/net/reporting/reporting_header_parser_unittest.cc b/net/reporting/reporting_header_parser_unittest.cc
index 475c565c..80260ea 100644
--- a/net/reporting/reporting_header_parser_unittest.cc
+++ b/net/reporting/reporting_header_parser_unittest.cc
@@ -25,6 +25,7 @@
 #include "net/reporting/reporting_endpoint.h"
 #include "net/reporting/reporting_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -186,11 +187,11 @@
   void ParseHeader(const NetworkIsolationKey& network_isolation_key,
                    const url::Origin& origin,
                    const std::string& json) {
-    std::unique_ptr<base::Value> value =
-        base::JSONReader::ReadDeprecated("[" + json + "]");
+    absl::optional<base::Value> value =
+        base::JSONReader::Read("[" + json + "]");
     if (value) {
       ReportingHeaderParser::ParseReportToHeader(
-          context(), network_isolation_key, origin, std::move(value));
+          context(), network_isolation_key, origin, value->GetList());
     }
   }
 };
diff --git a/net/reporting/reporting_service.cc b/net/reporting/reporting_service.cc
index b8665609..8c9caf6 100644
--- a/net/reporting/reporting_service.cc
+++ b/net/reporting/reporting_service.cc
@@ -119,9 +119,8 @@
     if (header_string.size() > kMaxJsonSize)
       return;
 
-    std::unique_ptr<base::Value> header_value =
-        base::JSONReader::ReadDeprecated("[" + header_string + "]",
-                                         base::JSON_PARSE_RFC, kMaxJsonDepth);
+    absl::optional<base::Value> header_value = base::JSONReader::Read(
+        "[" + header_string + "]", base::JSON_PARSE_RFC, kMaxJsonDepth);
     if (!header_value)
       return;
 
@@ -129,7 +128,7 @@
     DoOrBacklogTask(base::BindOnce(
         &ReportingServiceImpl::DoProcessReportToHeader, base::Unretained(this),
         FixupNetworkIsolationKey(network_isolation_key), origin,
-        std::move(header_value)));
+        std::move(header_value).value()));
   }
 
   void RemoveBrowsingData(
@@ -220,10 +219,11 @@
 
   void DoProcessReportToHeader(const NetworkIsolationKey& network_isolation_key,
                                const url::Origin& origin,
-                               std::unique_ptr<base::Value> header_value) {
+                               const base::Value& header_value) {
     DCHECK(initialized_);
+    DCHECK(header_value.is_list());
     ReportingHeaderParser::ParseReportToHeader(
-        context_.get(), network_isolation_key, origin, std::move(header_value));
+        context_.get(), network_isolation_key, origin, header_value.GetList());
   }
 
   void DoSetDocumentReportingEndpoints(
diff --git a/net/reporting/reporting_test_util.cc b/net/reporting/reporting_test_util.cc
index fcdafd6..6130a320 100644
--- a/net/reporting/reporting_test_util.cc
+++ b/net/reporting/reporting_test_util.cc
@@ -28,6 +28,7 @@
 #include "net/reporting/reporting_policy.h"
 #include "net/reporting/reporting_uploader.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -56,8 +57,8 @@
   const url::Origin& report_origin() const override { return report_origin_; }
   const GURL& url() const override { return url_; }
   const std::string& json() const override { return json_; }
-  std::unique_ptr<base::Value> GetValue() const override {
-    return base::JSONReader::ReadDeprecated(json_);
+  absl::optional<base::Value> GetValue() const override {
+    return base::JSONReader::Read(json_);
   }
 
   void Complete(ReportingUploader::Outcome outcome) override {
diff --git a/net/reporting/reporting_test_util.h b/net/reporting/reporting_test_util.h
index 6593724..bb4f4cd 100644
--- a/net/reporting/reporting_test_util.h
+++ b/net/reporting/reporting_test_util.h
@@ -66,7 +66,7 @@
     virtual const url::Origin& report_origin() const = 0;
     virtual const GURL& url() const = 0;
     virtual const std::string& json() const = 0;
-    virtual std::unique_ptr<base::Value> GetValue() const = 0;
+    virtual absl::optional<base::Value> GetValue() const = 0;
 
     virtual void Complete(Outcome outcome) = 0;
 
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index c222e66..0b6282a2 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -172,24 +172,31 @@
   UsageAndQuotaInfoGatherer(QuotaManagerImpl* manager,
                             const StorageKey& storage_key,
                             StorageType type,
-                            bool is_unlimited,
-                            bool is_session_only,
                             bool is_incognito,
-                            absl::optional<int64_t> quota_override_size,
                             UsageAndQuotaForDevtoolsCallback callback)
       : QuotaTask(manager),
         storage_key_(storage_key),
         callback_(std::move(callback)),
         type_(type),
-        is_unlimited_(is_unlimited),
-        is_session_only_(is_session_only),
-        is_incognito_(is_incognito),
-        is_override_enabled_(quota_override_size.has_value()),
-        quota_override_size_(quota_override_size) {
+        is_unlimited_(manager->IsStorageUnlimited(storage_key_, type_)),
+        is_incognito_(is_incognito) {
     DCHECK(manager);
     DCHECK(callback_);
   }
 
+  UsageAndQuotaInfoGatherer(QuotaManagerImpl* manager,
+                            const BucketInfo& bucket_info,
+                            bool is_incognito,
+                            UsageAndQuotaForDevtoolsCallback callback)
+      : UsageAndQuotaInfoGatherer(manager,
+                                  bucket_info.storage_key,
+                                  bucket_info.type,
+                                  is_incognito,
+                                  std::move(callback)) {
+    bucket_info_ = bucket_info;
+    DCHECK_EQ(StorageType::kTemporary, type_);
+  }
+
  protected:
   void Run() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -206,10 +213,18 @@
     manager()->GetQuotaSettings(
         base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotSettings,
                        weak_factory_.GetWeakPtr(), barrier));
-    manager()->GetStorageKeyUsageWithBreakdown(
-        storage_key_, type_,
-        base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotStorageKeyUsage,
-                       weak_factory_.GetWeakPtr(), barrier));
+
+    if (bucket_info_) {
+      manager()->GetBucketUsageWithBreakdown(
+          bucket_info_->ToBucketLocator(),
+          base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotUsage,
+                         weak_factory_.GetWeakPtr(), barrier));
+    } else {
+      manager()->GetStorageKeyUsageWithBreakdown(
+          storage_key_, type_,
+          base::BindOnce(&UsageAndQuotaInfoGatherer::OnGotUsage,
+                         weak_factory_.GetWeakPtr(), barrier));
+    }
 
     // Determine host_quota differently depending on type.
     if (is_unlimited_) {
@@ -248,33 +263,36 @@
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     weak_factory_.InvalidateWeakPtrs();
 
-    int64_t storage_key_quota = quota_override_size_.has_value()
-                                    ? quota_override_size_.value()
-                                    : desired_storage_key_quota_;
+    int64_t quota = desired_storage_key_quota_;
+    absl::optional<int64_t> quota_override_size =
+        manager()->GetQuotaOverrideForStorageKey(storage_key_);
+    if (quota_override_size)
+      quota = *quota_override_size;
+
+    // For an individual bucket, the quota is the minimum of the requested quota
+    // and the host quota.
+    if (bucket_info_ && bucket_info_->quota > 0)
+      quota = std::min(quota, bucket_info_->quota);
 
     if (is_unlimited_) {
       int64_t temp_pool_free_space =
-          std::max(static_cast<int64_t>(0),
-                   available_space_ - settings_.must_remain_available);
-      // Constrain the desired |storage_key_quota| to something that fits.
-      if (storage_key_quota > temp_pool_free_space) {
-        storage_key_quota = available_space_ + storage_key_usage_;
-      }
+          available_space_ - settings_.must_remain_available;
+      // Constrain the desired quota to something that fits.
+      if (quota > temp_pool_free_space)
+        quota = available_space_ + usage_;
     }
 
-    std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk,
-                             storage_key_usage_, storage_key_quota,
-                             is_override_enabled_,
-                             std::move(storage_key_usage_breakdown_));
-    if (type_ == StorageType::kTemporary && !is_incognito_ &&
-        !is_unlimited_) {
-      UMA_HISTOGRAM_MBYTES("Quota.QuotaForOrigin", storage_key_quota);
-      UMA_HISTOGRAM_MBYTES("Quota.UsageByOrigin", storage_key_usage_);
-      if (storage_key_quota > 0) {
+    std::move(callback_).Run(blink::mojom::QuotaStatusCode::kOk, usage_, quota,
+                             quota_override_size.has_value(),
+                             std::move(usage_breakdown_));
+    if (type_ == StorageType::kTemporary && !is_incognito_ && !is_unlimited_ &&
+        !bucket_info_) {
+      UMA_HISTOGRAM_MBYTES("Quota.QuotaForOrigin", quota);
+      UMA_HISTOGRAM_MBYTES("Quota.UsageByOrigin", usage_);
+      if (quota > 0) {
         UMA_HISTOGRAM_PERCENTAGE(
             "Quota.PercentUsedByOrigin",
-            std::min(100, static_cast<int>((storage_key_usage_ * 100) /
-                                           storage_key_quota)));
+            std::min(100, static_cast<int>((usage_ * 100) / quota)));
       }
     }
     DeleteSoon();
@@ -294,7 +312,7 @@
     settings_ = settings;
     barrier_closure.Run();
     if (type_ == StorageType::kTemporary && !is_unlimited_) {
-      int64_t storage_key_quota = is_session_only_
+      int64_t storage_key_quota = manager()->IsSessionOnly(storage_key_, type_)
                                       ? settings.session_only_per_host_quota
                                       : settings.per_host_quota;
       SetDesiredStorageKeyQuota(std::move(barrier_closure),
@@ -316,9 +334,9 @@
     std::move(barrier_closure).Run();
   }
 
-  void OnGotStorageKeyUsage(base::OnceClosure barrier_closure,
-                            int64_t usage,
-                            blink::mojom::UsageBreakdownPtr usage_breakdown) {
+  void OnGotUsage(base::OnceClosure barrier_closure,
+                  int64_t usage,
+                  blink::mojom::UsageBreakdownPtr usage_breakdown) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     DCHECK(barrier_closure);
     DCHECK_GE(usage, -1);
@@ -330,8 +348,8 @@
     DCHECK_GE(usage_breakdown->serviceWorkerCache, 0);
     DCHECK_GE(usage_breakdown->webSql, 0);
 
-    storage_key_usage_ = usage;
-    storage_key_usage_breakdown_ = std::move(usage_breakdown);
+    usage_ = usage;
+    usage_breakdown_ = std::move(usage_breakdown);
     std::move(barrier_closure).Run();
   }
 
@@ -348,20 +366,24 @@
 
   void OnBarrierComplete() { CallCompleted(); }
 
+  // These fields are passed at construction time.
   const StorageKey storage_key_;
+  // Non-null iff usage info is to be gathered for an individual bucket. If
+  // null, usage is gathered for all buckets in the given host/StorageKey.
+  absl::optional<BucketInfo> bucket_info_;
   QuotaManagerImpl::UsageAndQuotaForDevtoolsCallback callback_;
   const StorageType type_;
   const bool is_unlimited_;
-  const bool is_session_only_;
   const bool is_incognito_;
+
+  // Fields retrieved while running.
   int64_t available_space_ = 0;
   int64_t total_space_ = 0;
   int64_t desired_storage_key_quota_ = 0;
-  int64_t storage_key_usage_ = 0;
-  const bool is_override_enabled_;
-  absl::optional<int64_t> quota_override_size_;
-  blink::mojom::UsageBreakdownPtr storage_key_usage_breakdown_;
+  int64_t usage_ = 0;
+  blink::mojom::UsageBreakdownPtr usage_breakdown_;
   QuotaSettings settings_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // Weak pointers are used to support cancelling work.
@@ -1307,17 +1329,8 @@
   }
   EnsureDatabaseOpened();
 
-  bool is_session_only = type == StorageType::kTemporary &&
-                         special_storage_policy_ &&
-                         special_storage_policy_->IsStorageSessionOnly(
-                             storage_key.origin().GetURL());
-
-  absl::optional<int64_t> quota_override =
-      GetQuotaOverrideForStorageKey(storage_key);
-
   UsageAndQuotaInfoGatherer* helper = new UsageAndQuotaInfoGatherer(
-      this, storage_key, type, IsStorageUnlimited(storage_key, type),
-      is_session_only, is_incognito_, quota_override, std::move(callback));
+      this, storage_key, type, is_incognito_, std::move(callback));
   helper->Start();
 }
 
@@ -1334,27 +1347,16 @@
     return;
   }
 
-  if (!IsSupportedType(type) ||
-      (is_incognito_ && !IsSupportedIncognitoType(type))) {
-    std::move(callback).Run(
-        /*status*/ blink::mojom::QuotaStatusCode::kErrorNotSupported,
-        /*usage*/ 0,
-        /*quota*/ 0);
-    return;
-  }
-  EnsureDatabaseOpened();
+  GetUsageAndQuotaWithBreakdown(
+      storage_key, type,
+      base::BindOnce(&DidGetUsageAndQuotaStripBreakdown, std::move(callback)));
+}
 
-  bool is_session_only = type == StorageType::kTemporary &&
-                         special_storage_policy_ &&
-                         special_storage_policy_->IsStorageSessionOnly(
-                             storage_key.origin().GetURL());
-
-  absl::optional<int64_t> quota_override =
-      GetQuotaOverrideForStorageKey(storage_key);
-
+void QuotaManagerImpl::GetBucketUsageAndQuota(const BucketInfo& bucket,
+                                              UsageAndQuotaCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   UsageAndQuotaInfoGatherer* helper = new UsageAndQuotaInfoGatherer(
-      this, storage_key, type, IsStorageUnlimited(storage_key, type),
-      is_session_only, is_incognito_, quota_override,
+      this, bucket, is_incognito_,
       base::BindOnce(&DidGetUsageAndQuotaStripOverride,
                      base::BindOnce(&DidGetUsageAndQuotaStripBreakdown,
                                     std::move(callback))));
@@ -1743,6 +1745,13 @@
                            weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+bool QuotaManagerImpl::IsSessionOnly(const StorageKey& storage_key,
+                                     StorageType type) const {
+  return type == StorageType::kTemporary && special_storage_policy_ &&
+         special_storage_policy_->IsStorageSessionOnly(
+             storage_key.origin().GetURL());
+}
+
 bool QuotaManagerImpl::IsStorageUnlimited(const StorageKey& storage_key,
                                           StorageType type) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h
index 229a52a..437cc200 100644
--- a/storage/browser/quota/quota_manager_impl.h
+++ b/storage/browser/quota/quota_manager_impl.h
@@ -405,6 +405,11 @@
                                        UsageWithBreakdownCallback callback);
   void GetBucketUsageWithBreakdown(const BucketLocator& bucket,
                                    UsageWithBreakdownCallback callback);
+  void GetBucketUsageAndQuota(const BucketInfo& bucket,
+                              UsageAndQuotaCallback callback);
+
+  bool IsSessionOnly(const blink::StorageKey& storage_key,
+                     blink::mojom::StorageType type) const;
 
   bool IsStorageUnlimited(const blink::StorageKey& storage_key,
                           blink::mojom::StorageType type) const;
diff --git a/storage/browser/quota/quota_manager_proxy.cc b/storage/browser/quota/quota_manager_proxy.cc
index 437fbcbb..90a46f95 100644
--- a/storage/browser/quota/quota_manager_proxy.cc
+++ b/storage/browser/quota/quota_manager_proxy.cc
@@ -522,6 +522,33 @@
   quota_manager_impl_->GetUsageAndQuota(storage_key, type, std::move(respond));
 }
 
+void QuotaManagerProxy::GetBucketUsageAndQuota(
+    const BucketInfo& bucket,
+    scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
+    UsageAndQuotaCallback callback) {
+  DCHECK(callback_task_runner);
+  DCHECK(callback);
+
+  if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
+    quota_manager_impl_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&QuotaManagerProxy::GetBucketUsageAndQuota, this, bucket,
+                       std::move(callback_task_runner), std::move(callback)));
+    return;
+  }
+
+  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_manager_impl_sequence_checker_);
+
+  auto respond =
+      base::BindPostTask(std::move(callback_task_runner), std::move(callback));
+  if (!quota_manager_impl_) {
+    std::move(respond).Run(blink::mojom::QuotaStatusCode::kErrorAbort, 0, 0);
+    return;
+  }
+
+  quota_manager_impl_->GetBucketUsageAndQuota(bucket, std::move(respond));
+}
+
 void QuotaManagerProxy::IsStorageUnlimited(
     const StorageKey& storage_key,
     blink::mojom::StorageType type,
diff --git a/storage/browser/quota/quota_manager_proxy.h b/storage/browser/quota/quota_manager_proxy.h
index 4e542f2..c5ab07c 100644
--- a/storage/browser/quota/quota_manager_proxy.h
+++ b/storage/browser/quota/quota_manager_proxy.h
@@ -220,6 +220,11 @@
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
       UsageAndQuotaCallback callback);
 
+  void GetBucketUsageAndQuota(
+      const BucketInfo& bucket,
+      scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
+      UsageAndQuotaCallback callback);
+
   virtual void IsStorageUnlimited(
       const blink::StorageKey& storage_key,
       blink::mojom::StorageType type,
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc
index dc8bb5c..1b48eb0 100644
--- a/storage/browser/quota/quota_manager_unittest.cc
+++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -290,6 +290,13 @@
     return {future.Get<0>(), future.Get<1>(), future.Get<2>()};
   }
 
+  UsageAndQuotaResult GetUsageAndQuotaForBucket(const BucketInfo& bucket_info) {
+    base::test::TestFuture<QuotaStatusCode, int64_t, int64_t> future;
+    quota_manager_impl_->GetBucketUsageAndQuota(bucket_info,
+                                                future.GetCallback());
+    return {future.Get<0>(), future.Get<1>(), future.Get<2>()};
+  }
+
   void GetUsageAndQuotaWithBreakdown(const StorageKey& storage_key,
                                      StorageType type) {
     base::RunLoop run_loop;
@@ -1070,6 +1077,42 @@
   EXPECT_EQ(result.quota, quota_returned_for_foo);
 }
 
+TEST_F(QuotaManagerImplTest, GetUsageAndQuota_SingleBucket) {
+  static const ClientBucketData kData[] = {
+      {"http://foo.com/", "logs", kTemp, 10},
+      {"http://foo.com/", "inbox", kTemp, 60},
+  };
+  MockQuotaClient* fs_client =
+      CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm});
+
+  // Initialize the logs bucket with a non-default quota.
+  BucketInitParams params(ToStorageKey("http://foo.com/"), "logs");
+  params.quota = 117;
+  ASSERT_TRUE(UpdateOrCreateBucket(params).ok());
+
+  RegisterClientBucketData(fs_client, kData);
+
+  {
+    QuotaErrorOr<BucketInfo> bucket =
+        UpdateOrCreateBucket({ToStorageKey("http://foo.com/"), "logs"});
+    ASSERT_TRUE(bucket.ok());
+    auto result = GetUsageAndQuotaForBucket(bucket.value());
+    EXPECT_EQ(result.status, QuotaStatusCode::kOk);
+    EXPECT_EQ(result.usage, 10);
+    EXPECT_EQ(result.quota, params.quota);
+  }
+
+  {
+    QuotaErrorOr<BucketInfo> bucket =
+        UpdateOrCreateBucket({ToStorageKey("http://foo.com/"), "inbox"});
+    ASSERT_TRUE(bucket.ok());
+    auto result = GetUsageAndQuotaForBucket(bucket.value());
+    EXPECT_EQ(result.status, QuotaStatusCode::kOk);
+    EXPECT_EQ(result.usage, 60);
+    EXPECT_EQ(result.quota, kDefaultPerHostQuota);
+  }
+}
+
 TEST_F(QuotaManagerImplTest, GetUsage_NoClient) {
   auto result =
       GetUsageAndQuotaForWebApps(ToStorageKey("http://foo.com/"), kTemp);
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 71582597..59e5ffcb 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8274,7 +8274,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8359,7 +8359,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8784,7 +8784,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8869,7 +8869,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 6c13935..51a9584 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46248,7 +46248,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46333,7 +46333,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46758,7 +46758,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46843,7 +46843,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47272,7 +47272,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47357,7 +47357,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47782,7 +47782,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47867,7 +47867,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48364,7 +48364,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48449,7 +48449,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48874,7 +48874,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48959,7 +48959,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49456,7 +49456,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49541,7 +49541,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49966,7 +49966,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.90"
+              "revision": "version:102.0.5005.91"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -50051,7 +50051,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.30"
+              "revision": "version:103.0.5060.31"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 50a2d92..06b3cd2 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5865,21 +5865,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5093.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -5892,7 +5892,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "isolate_profile_data": true,
@@ -6030,21 +6030,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5093.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -6056,7 +6056,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "args": [
@@ -6176,21 +6176,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5093.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -6202,7 +6202,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index bdb33b50..8d5768c 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -22265,7 +22265,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22288,7 +22289,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22311,7 +22313,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22334,7 +22337,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22358,7 +22362,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22381,7 +22386,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22404,7 +22410,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22427,7 +22434,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22451,7 +22459,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22475,7 +22484,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22498,7 +22508,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22521,7 +22532,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22544,7 +22556,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22567,7 +22580,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22590,7 +22604,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22613,7 +22628,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22637,7 +22653,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22660,7 +22677,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22683,7 +22701,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22707,7 +22726,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22731,7 +22751,8 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_unittests.filter",
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22755,7 +22776,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22778,7 +22800,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22801,7 +22824,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22824,7 +22848,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22847,7 +22872,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22870,7 +22896,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22893,7 +22920,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22916,7 +22944,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22939,7 +22968,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22963,7 +22993,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -22986,7 +23017,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23009,7 +23041,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23033,7 +23066,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23056,7 +23090,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23080,7 +23115,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23103,7 +23139,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23126,7 +23163,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23149,7 +23187,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23173,7 +23212,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23196,7 +23236,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23219,7 +23260,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23242,7 +23284,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23266,7 +23309,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23290,7 +23334,8 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23315,7 +23360,8 @@
           "--child-arg=--ozone-platform=headless",
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23338,7 +23384,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23361,7 +23408,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23384,7 +23432,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23408,7 +23457,8 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter",
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23432,7 +23482,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23455,7 +23506,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23478,7 +23530,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23501,7 +23554,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23524,7 +23578,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23548,7 +23603,8 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.ui_base_unittests.filter",
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23571,7 +23627,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23594,7 +23651,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23618,7 +23676,8 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.views_examples_unittests.filter",
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23642,7 +23701,8 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.views_unittests.filter",
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23666,7 +23726,8 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.viz_unittests.filter",
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23689,7 +23750,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23714,7 +23776,8 @@
           "--child-arg=--vmodule=test_navigation_listener=1",
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23738,7 +23801,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23761,7 +23825,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23784,7 +23849,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23807,7 +23873,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23830,7 +23897,8 @@
         "args": [
           "--ram-size-mb=16384",
           "--code-coverage",
-          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+          "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -23857,6 +23925,7 @@
           "--ram-size-mb=16384",
           "--code-coverage",
           "--code-coverage-dir=${ISOLATED_OUTDIR}",
+          "--test-launcher-jobs=2",
           "--logs-dir=${ISOLATED_OUTDIR}/logs"
         ],
         "isolate_name": "angle_unittests",
@@ -88037,21 +88106,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5093.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -88059,7 +88128,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "isolate_profile_data": true,
@@ -88172,28 +88241,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5093.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "args": [
@@ -88293,28 +88362,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5093.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "isolate_profile_data": true,
@@ -89652,20 +89721,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5093.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -89679,7 +89748,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "merge": {
@@ -89817,20 +89886,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5093.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -89843,7 +89912,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "args": [
@@ -89963,20 +90032,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5093.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -89989,7 +90058,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "merge": {
@@ -91485,20 +91554,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5093.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -91512,7 +91581,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "merge": {
@@ -91650,20 +91719,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5093.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -91676,7 +91745,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "args": [
@@ -91796,20 +91865,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5093.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -91822,7 +91891,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       },
       {
         "merge": {
@@ -92557,20 +92626,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5093.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5094.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5093.0",
-              "revision": "version:104.0.5093.0"
+              "location": "lacros_version_skew_tests_v104.0.5094.0",
+              "revision": "version:104.0.5094.0"
             }
           ],
           "dimension_sets": [
@@ -92583,7 +92652,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5093.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5094.0"
       }
     ]
   },
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 13c4979b..094f266 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -443,7 +443,8 @@
     '$mixin_append': {
       'args': [
         '--code-coverage',
-        '--code-coverage-dir=${ISOLATED_OUTDIR}'
+        '--code-coverage-dir=${ISOLATED_OUTDIR}',
+        '--test-launcher-jobs=2'
       ],
     },
   },
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 602626b..4115dbb 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,15 +22,15 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5093.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5094.0/test_ash_chrome',
     ],
-    'identifier': 'Lacros version skew testing ash 104.0.5093.0',
+    'identifier': 'Lacros version skew testing ash 104.0.5094.0',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v104.0.5093.0',
-          'revision': 'version:104.0.5093.0',
+          'location': 'lacros_version_skew_tests_v104.0.5094.0',
+          'revision': 'version:104.0.5094.0',
         },
       ],
     },
@@ -479,7 +479,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.30'
+          'revision': 'version:103.0.5060.31'
         }
       ]
     }
@@ -503,7 +503,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.90'
+          'revision': 'version:102.0.5005.91'
         }
       ]
     }
@@ -623,7 +623,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.30'
+          'revision': 'version:103.0.5060.31'
         }
       ]
     }
@@ -647,7 +647,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.90'
+          'revision': 'version:102.0.5005.91'
         }
       ]
     }
@@ -767,7 +767,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.30'
+          'revision': 'version:103.0.5060.31'
         }
       ]
     }
@@ -791,7 +791,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.90'
+          'revision': 'version:102.0.5005.91'
         }
       ]
     }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 030493d..eea9b8c 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1724,6 +1724,21 @@
             ]
         }
     ],
+    "BackForwardCache_NoMemoryLimit_Trial": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "BackForwardCache_NoMemoryLimit_Trial"
+                    ]
+                }
+            ]
+        }
+    ],
     "BatchImageDecoding": [
         {
             "platforms": [
diff --git a/third_party/blink/renderer/core/css/css_container_values.cc b/third_party/blink/renderer/core/css/css_container_values.cc
index 6128db22..be0f2ae2 100644
--- a/third_party/blink/renderer/core/css/css_container_values.cc
+++ b/third_party/blink/renderer/core/css/css_container_values.cc
@@ -23,19 +23,19 @@
                       document.documentElement()->GetComputedStyle())
                       .Unzoomed()) {}
 
-float CSSContainerValues::EmSize() const {
+float CSSContainerValues::EmFontSize() const {
   return font_sizes_.Em();
 }
 
-float CSSContainerValues::RemSize() const {
+float CSSContainerValues::RemFontSize() const {
   return font_sizes_.Rem();
 }
 
-float CSSContainerValues::ExSize() const {
+float CSSContainerValues::ExFontSize() const {
   return font_sizes_.Ex();
 }
 
-float CSSContainerValues::ChSize() const {
+float CSSContainerValues::ChFontSize() const {
   return font_sizes_.Ch();
 }
 
diff --git a/third_party/blink/renderer/core/css/css_container_values.h b/third_party/blink/renderer/core/css/css_container_values.h
index 67c5978..59b8366 100644
--- a/third_party/blink/renderer/core/css/css_container_values.h
+++ b/third_party/blink/renderer/core/css/css_container_values.h
@@ -24,10 +24,10 @@
   absl::optional<double> Height() const override { return height_; }
 
  protected:
-  float EmSize() const override;
-  float RemSize() const override;
-  float ExSize() const override;
-  float ChSize() const override;
+  float EmFontSize() const override;
+  float RemFontSize() const override;
+  float ExFontSize() const override;
+  float ChFontSize() const override;
   WritingMode GetWritingMode() const override { return writing_mode_; }
 
  private:
diff --git a/third_party/blink/renderer/core/css/css_length_resolver.cc b/third_party/blink/renderer/core/css/css_length_resolver.cc
index 393a1117..adab5b3 100644
--- a/third_party/blink/renderer/core/css/css_length_resolver.cc
+++ b/third_party/blink/renderer/core/css/css_length_resolver.cc
@@ -145,10 +145,6 @@
 double CSSLengthResolver::ZoomedComputedPixels(
     double value,
     CSSPrimitiveValue::UnitType type) const {
-  // The logic in this function is duplicated in MediaValues::ComputeLength()
-  // because MediaValues::ComputeLength() needs nearly identical logic, but we
-  // haven't found a way to make ZoomedComputedPixels() more generic (to solve
-  // both cases) without hurting performance.
   switch (type) {
     case CSSPrimitiveValue::UnitType::kPixels:
     case CSSPrimitiveValue::UnitType::kUserUnits:
diff --git a/third_party/blink/renderer/core/css/media_values.cc b/third_party/blink/renderer/core/css/media_values.cc
index 90d91347..9559c1a8 100644
--- a/third_party/blink/renderer/core/css/media_values.cc
+++ b/third_party/blink/renderer/core/css/media_values.cc
@@ -71,13 +71,13 @@
 }
 
 absl::optional<double> MediaValues::InlineSize() const {
-  if (IsHorizontalWritingMode(GetWritingMode()))
+  if (blink::IsHorizontalWritingMode(GetWritingMode()))
     return Width();
   return Height();
 }
 
 absl::optional<double> MediaValues::BlockSize() const {
-  if (IsHorizontalWritingMode(GetWritingMode()))
+  if (blink::IsHorizontalWritingMode(GetWritingMode()))
     return Height();
   return Width();
 }
@@ -88,46 +88,6 @@
   return MakeGarbageCollected<MediaValuesCached>();
 }
 
-double MediaValues::ViewportInlineSize() const {
-  return IsHorizontalWritingMode(GetWritingMode()) ? ViewportWidth()
-                                                   : ViewportHeight();
-}
-
-double MediaValues::ViewportBlockSize() const {
-  return IsHorizontalWritingMode(GetWritingMode()) ? ViewportHeight()
-                                                   : ViewportWidth();
-}
-
-double MediaValues::SmallViewportInlineSize() const {
-  return IsHorizontalWritingMode(GetWritingMode()) ? SmallViewportWidth()
-                                                   : SmallViewportHeight();
-}
-
-double MediaValues::SmallViewportBlockSize() const {
-  return IsHorizontalWritingMode(GetWritingMode()) ? SmallViewportHeight()
-                                                   : SmallViewportWidth();
-}
-
-double MediaValues::LargeViewportInlineSize() const {
-  return IsHorizontalWritingMode(GetWritingMode()) ? LargeViewportWidth()
-                                                   : LargeViewportHeight();
-}
-
-double MediaValues::LargeViewportBlockSize() const {
-  return IsHorizontalWritingMode(GetWritingMode()) ? LargeViewportHeight()
-                                                   : LargeViewportWidth();
-}
-
-double MediaValues::DynamicViewportInlineSize() const {
-  return IsHorizontalWritingMode(GetWritingMode()) ? DynamicViewportWidth()
-                                                   : DynamicViewportHeight();
-}
-
-double MediaValues::DynamicViewportBlockSize() const {
-  return IsHorizontalWritingMode(GetWritingMode()) ? DynamicViewportHeight()
-                                                   : DynamicViewportWidth();
-}
-
 double MediaValues::CalculateViewportWidth(LocalFrame* frame) {
   DCHECK(frame);
   DCHECK(frame->View());
@@ -459,137 +419,10 @@
 bool MediaValues::ComputeLengthImpl(double value,
                                     CSSPrimitiveValue::UnitType type,
                                     double& result) const {
-  // The logic in this function is duplicated from
-  // CSSToLengthConversionData::ZoomedComputedPixels() because
-  // MediaValues::ComputeLength() needs nearly identical logic, but we haven't
-  // found a way to make CSSToLengthConversionData::ZoomedComputedPixels() more
-  // generic (to solve both cases) without hurting performance.
-  // TODO: Unite the logic here with CSSToLengthConversionData in a performant
-  // way.
-  switch (type) {
-    case CSSPrimitiveValue::UnitType::kEms:
-      result = value * EmSize();
-      return true;
-    case CSSPrimitiveValue::UnitType::kRems:
-      result = value * RemSize();
-      return true;
-    case CSSPrimitiveValue::UnitType::kPixels:
-    case CSSPrimitiveValue::UnitType::kUserUnits:
-      result = value;
-      return true;
-    case CSSPrimitiveValue::UnitType::kExs:
-      result = value * ExSize();
-      return true;
-    case CSSPrimitiveValue::UnitType::kChs:
-      result = value * ChSize();
-      return true;
-    case CSSPrimitiveValue::UnitType::kViewportWidth:
-      result = (value * ViewportWidth()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kViewportHeight:
-      result = (value * ViewportHeight()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kViewportInlineSize:
-      result = (value * ViewportInlineSize()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kViewportBlockSize:
-      result = (value * ViewportBlockSize()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kViewportMin:
-      result = (value * std::min(ViewportWidth(), ViewportHeight())) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kViewportMax:
-      result = (value * std::max(ViewportWidth(), ViewportHeight())) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kSmallViewportWidth:
-    case CSSPrimitiveValue::UnitType::kContainerWidth:
-      result = (value * SmallViewportWidth()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kSmallViewportHeight:
-    case CSSPrimitiveValue::UnitType::kContainerHeight:
-      result = (value * SmallViewportHeight()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kSmallViewportInlineSize:
-    case CSSPrimitiveValue::UnitType::kContainerInlineSize:
-      result = (value * SmallViewportInlineSize()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kSmallViewportBlockSize:
-    case CSSPrimitiveValue::UnitType::kContainerBlockSize:
-      result = (value * SmallViewportBlockSize()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kSmallViewportMin:
-    case CSSPrimitiveValue::UnitType::kContainerMin:
-      result = (value * std::min(SmallViewportWidth(), SmallViewportHeight())) /
-               100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kSmallViewportMax:
-    case CSSPrimitiveValue::UnitType::kContainerMax:
-      result = (value * std::max(SmallViewportWidth(), SmallViewportHeight())) /
-               100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kLargeViewportWidth:
-      result = (value * LargeViewportWidth()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kLargeViewportHeight:
-      result = (value * LargeViewportHeight()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kLargeViewportInlineSize:
-      result = (value * LargeViewportInlineSize()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kLargeViewportBlockSize:
-      result = (value * LargeViewportBlockSize()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kLargeViewportMin:
-      result = (value * std::min(LargeViewportWidth(), LargeViewportHeight())) /
-               100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kLargeViewportMax:
-      result = (value * std::max(LargeViewportWidth(), LargeViewportHeight())) /
-               100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kDynamicViewportWidth:
-      result = (value * DynamicViewportWidth()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kDynamicViewportHeight:
-      result = (value * DynamicViewportHeight()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kDynamicViewportInlineSize:
-      result = (value * DynamicViewportInlineSize()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kDynamicViewportBlockSize:
-      result = (value * DynamicViewportBlockSize()) / 100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kDynamicViewportMin:
-      result =
-          (value * std::min(DynamicViewportWidth(), DynamicViewportHeight())) /
-          100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kDynamicViewportMax:
-      result =
-          (value * std::max(DynamicViewportWidth(), DynamicViewportHeight())) /
-          100.0;
-      return true;
-    case CSSPrimitiveValue::UnitType::kCentimeters:
-      result = value * kCssPixelsPerCentimeter;
-      return true;
-    case CSSPrimitiveValue::UnitType::kMillimeters:
-      result = value * kCssPixelsPerMillimeter;
-      return true;
-    case CSSPrimitiveValue::UnitType::kQuarterMillimeters:
-      result = value * kCssPixelsPerQuarterMillimeter;
-      return true;
-    case CSSPrimitiveValue::UnitType::kInches:
-      result = value * kCssPixelsPerInch;
-      return true;
-    case CSSPrimitiveValue::UnitType::kPoints:
-      result = value * kCssPixelsPerPoint;
-      return true;
-    case CSSPrimitiveValue::UnitType::kPicas:
-      result = value * kCssPixelsPerPica;
-      return true;
-    default:
-      return false;
-  }
+  if (!CSSPrimitiveValue::IsLength(type))
+    return false;
+  result = ZoomedComputedPixels(value, type);
+  return true;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/media_values.h b/third_party/blink/renderer/core/css/media_values.h
index ca2783b8..3abad40 100644
--- a/third_party/blink/renderer/core/css/media_values.h
+++ b/third_party/blink/renderer/core/css/media_values.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/css_length_resolver.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/text/writing_mode.h"
@@ -31,8 +32,10 @@
     CSSValueID id);
 ForcedColors CSSValueIDToForcedColors(CSSValueID id);
 
-class CORE_EXPORT MediaValues : public GarbageCollected<MediaValues> {
+class CORE_EXPORT MediaValues : public GarbageCollected<MediaValues>,
+                                public CSSLengthResolver {
  public:
+  MediaValues() : CSSLengthResolver(1.0f /* zoom */) {}
   virtual ~MediaValues() = default;
   virtual void Trace(Visitor* visitor) const {}
 
@@ -84,29 +87,6 @@
   virtual device::mojom::blink::DevicePostureType GetDevicePosture() const = 0;
 
  protected:
-  virtual double ViewportWidth() const = 0;
-  virtual double ViewportHeight() const = 0;
-  virtual double SmallViewportWidth() const = 0;
-  virtual double SmallViewportHeight() const = 0;
-  virtual double LargeViewportWidth() const = 0;
-  virtual double LargeViewportHeight() const = 0;
-  virtual double DynamicViewportWidth() const = 0;
-  virtual double DynamicViewportHeight() const = 0;
-  virtual float EmSize() const = 0;
-  virtual float RemSize() const = 0;
-  virtual float ExSize() const = 0;
-  virtual float ChSize() const = 0;
-  virtual WritingMode GetWritingMode() const = 0;
-
-  double ViewportInlineSize() const;
-  double ViewportBlockSize() const;
-  double SmallViewportInlineSize() const;
-  double SmallViewportBlockSize() const;
-  double LargeViewportInlineSize() const;
-  double LargeViewportBlockSize() const;
-  double DynamicViewportInlineSize() const;
-  double DynamicViewportBlockSize() const;
-
   static double CalculateViewportWidth(LocalFrame*);
   static double CalculateViewportHeight(LocalFrame*);
   static double CalculateSmallViewportWidth(LocalFrame*);
diff --git a/third_party/blink/renderer/core/css/media_values_cached.cc b/third_party/blink/renderer/core/css/media_values_cached.cc
index 47e9e4a7..089d627 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.cc
+++ b/third_party/blink/renderer/core/css/media_values_cached.cc
@@ -90,6 +90,23 @@
   return MakeGarbageCollected<MediaValuesCached>(data_);
 }
 
+float MediaValuesCached::EmFontSize() const {
+  return data_.em_size;
+}
+
+float MediaValuesCached::RemFontSize() const {
+  // For media queries rem and em units are both based on the initial font.
+  return data_.em_size;
+}
+
+float MediaValuesCached::ExFontSize() const {
+  return data_.ex_size;
+}
+
+float MediaValuesCached::ChFontSize() const {
+  return data_.ch_size;
+}
+
 double MediaValuesCached::ViewportWidth() const {
   return data_.viewport_width;
 }
@@ -122,21 +139,12 @@
   return data_.dynamic_viewport_height;
 }
 
-float MediaValuesCached::EmSize() const {
-  return data_.em_size;
+double MediaValuesCached::ContainerWidth() const {
+  return SmallViewportWidth();
 }
 
-float MediaValuesCached::RemSize() const {
-  // For media queries rem and em units are both based on the initial font.
-  return data_.em_size;
-}
-
-float MediaValuesCached::ExSize() const {
-  return data_.ex_size;
-}
-
-float MediaValuesCached::ChSize() const {
-  return data_.ch_size;
+double MediaValuesCached::ContainerHeight() const {
+  return SmallViewportHeight();
 }
 
 int MediaValuesCached::DeviceWidth() const {
diff --git a/third_party/blink/renderer/core/css/media_values_cached.h b/third_party/blink/renderer/core/css/media_values_cached.h
index 8e72a24..8f38620d 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.h
+++ b/third_party/blink/renderer/core/css/media_values_cached.h
@@ -148,6 +148,11 @@
   void OverrideViewportDimensions(double width, double height);
 
  protected:
+  // CSSLengthResolver
+  float EmFontSize() const override;
+  float RemFontSize() const override;
+  float ExFontSize() const override;
+  float ChFontSize() const override;
   double ViewportWidth() const override;
   double ViewportHeight() const override;
   double SmallViewportWidth() const override;
@@ -156,10 +161,8 @@
   double LargeViewportHeight() const override;
   double DynamicViewportWidth() const override;
   double DynamicViewportHeight() const override;
-  float EmSize() const override;
-  float RemSize() const override;
-  float ExSize() const override;
-  float ChSize() const override;
+  double ContainerWidth() const override;
+  double ContainerHeight() const override;
   WritingMode GetWritingMode() const override {
     return WritingMode::kHorizontalTb;
   }
diff --git a/third_party/blink/renderer/core/css/media_values_dynamic.cc b/third_party/blink/renderer/core/css/media_values_dynamic.cc
index 5cf6982..838bbfbe 100644
--- a/third_party/blink/renderer/core/css/media_values_dynamic.cc
+++ b/third_party/blink/renderer/core/css/media_values_dynamic.cc
@@ -45,6 +45,23 @@
   DCHECK(frame_);
 }
 
+float MediaValuesDynamic::EmFontSize() const {
+  return CalculateEmSize(frame_);
+}
+
+float MediaValuesDynamic::RemFontSize() const {
+  // For media queries rem and em units are both based on the initial font.
+  return CalculateEmSize(frame_);
+}
+
+float MediaValuesDynamic::ExFontSize() const {
+  return CalculateExSize(frame_);
+}
+
+float MediaValuesDynamic::ChFontSize() const {
+  return CalculateChSize(frame_);
+}
+
 double MediaValuesDynamic::ViewportWidth() const {
   if (viewport_dimensions_overridden_)
     return viewport_width_override_;
@@ -81,21 +98,12 @@
   return CalculateDynamicViewportHeight(frame_);
 }
 
-float MediaValuesDynamic::EmSize() const {
-  return CalculateEmSize(frame_);
+double MediaValuesDynamic::ContainerWidth() const {
+  return SmallViewportWidth();
 }
 
-float MediaValuesDynamic::RemSize() const {
-  // For media queries rem and em units are both based on the initial font.
-  return CalculateEmSize(frame_);
-}
-
-float MediaValuesDynamic::ExSize() const {
-  return CalculateExSize(frame_);
-}
-
-float MediaValuesDynamic::ChSize() const {
-  return CalculateChSize(frame_);
+double MediaValuesDynamic::ContainerHeight() const {
+  return SmallViewportHeight();
 }
 
 int MediaValuesDynamic::DeviceWidth() const {
diff --git a/third_party/blink/renderer/core/css/media_values_dynamic.h b/third_party/blink/renderer/core/css/media_values_dynamic.h
index 48117b19..075eea9 100644
--- a/third_party/blink/renderer/core/css/media_values_dynamic.h
+++ b/third_party/blink/renderer/core/css/media_values_dynamic.h
@@ -54,6 +54,11 @@
   void Trace(Visitor*) const override;
 
  protected:
+  // CSSLengthResolver
+  float EmFontSize() const override;
+  float RemFontSize() const override;
+  float ExFontSize() const override;
+  float ChFontSize() const override;
   double ViewportWidth() const override;
   double ViewportHeight() const override;
   double SmallViewportWidth() const override;
@@ -62,10 +67,8 @@
   double LargeViewportHeight() const override;
   double DynamicViewportWidth() const override;
   double DynamicViewportHeight() const override;
-  float EmSize() const override;
-  float RemSize() const override;
-  float ExSize() const override;
-  float ChSize() const override;
+  double ContainerWidth() const override;
+  double ContainerHeight() const override;
   WritingMode GetWritingMode() const override {
     return WritingMode::kHorizontalTb;
   }
diff --git a/third_party/blink/renderer/core/exported/web_input_element.cc b/third_party/blink/renderer/core/exported/web_input_element.cc
index f3ec96b..9099f3ee 100644
--- a/third_party/blink/renderer/core/exported/web_input_element.cc
+++ b/third_party/blink/renderer/core/exported/web_input_element.cc
@@ -111,7 +111,7 @@
 void WebInputElement::SetChecked(bool now_checked,
                                  bool send_events,
                                  WebAutofillState autofill_state) {
-  Unwrap<HTMLInputElement>()->setChecked(
+  Unwrap<HTMLInputElement>()->SetChecked(
       now_checked,
       send_events ? TextFieldEventBehavior::kDispatchInputAndChangeEvent
                   : TextFieldEventBehavior::kDispatchNoEvent,
@@ -119,7 +119,7 @@
 }
 
 bool WebInputElement::IsChecked() const {
-  return ConstUnwrap<HTMLInputElement>()->checked();
+  return ConstUnwrap<HTMLInputElement>()->Checked();
 }
 
 bool WebInputElement::IsMultiple() const {
diff --git a/third_party/blink/renderer/core/exported/web_searchable_form_data.cc b/third_party/blink/renderer/core/exported/web_searchable_form_data.cc
index f738dc5..077afb8 100644
--- a/third_party/blink/renderer/core/exported/web_searchable_form_data.cc
+++ b/third_party/blink/renderer/core/exported/web_searchable_form_data.cc
@@ -120,7 +120,7 @@
   if (auto* input = DynamicTo<HTMLInputElement>(form_element)) {
     if (input->type() == input_type_names::kCheckbox ||
         input->type() == input_type_names::kRadio) {
-      return input->checked() ==
+      return input->Checked() ==
              input->FastHasAttribute(html_names::kCheckedAttr);
     }
   } else if (auto* select = DynamicTo<HTMLSelectElement>(form_element)) {
diff --git a/third_party/blink/renderer/core/html/forms/base_checkable_input_type.cc b/third_party/blink/renderer/core/html/forms/base_checkable_input_type.cc
index 77694b5..4db7e93b 100644
--- a/third_party/blink/renderer/core/html/forms/base_checkable_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/base_checkable_input_type.cc
@@ -53,16 +53,16 @@
 }
 
 FormControlState BaseCheckableInputType::SaveFormControlState() const {
-  return FormControlState(GetElement().checked() ? "on" : "off");
+  return FormControlState(GetElement().Checked() ? "on" : "off");
 }
 
 void BaseCheckableInputType::RestoreFormControlState(
     const FormControlState& state) {
-  GetElement().setChecked(state[0] == "on");
+  GetElement().SetChecked(state[0] == "on");
 }
 
 void BaseCheckableInputType::AppendToFormData(FormData& form_data) const {
-  if (GetElement().checked())
+  if (GetElement().Checked())
     form_data.AppendFromElement(GetElement().GetName(), GetElement().Value());
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc b/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc
index 8e67cb19..5686c40 100644
--- a/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc
@@ -51,7 +51,7 @@
 }
 
 bool CheckboxInputType::ValueMissing(const String&) const {
-  return GetElement().IsRequired() && !GetElement().checked();
+  return GetElement().IsRequired() && !GetElement().Checked();
 }
 
 String CheckboxInputType::ValueMissingText() const {
@@ -75,13 +75,13 @@
 
   ClickHandlingState* state = MakeGarbageCollected<ClickHandlingState>();
 
-  state->checked = GetElement().checked();
+  state->checked = GetElement().Checked();
   state->indeterminate = GetElement().indeterminate();
 
   if (state->indeterminate)
     GetElement().setIndeterminate(false);
 
-  GetElement().setChecked(!state->checked,
+  GetElement().SetChecked(!state->checked,
                           TextFieldEventBehavior::kDispatchChangeEvent);
   is_in_click_handler_ = true;
   return state;
@@ -91,7 +91,7 @@
                                          const ClickHandlingState& state) {
   if (event.defaultPrevented() || event.DefaultHandled()) {
     GetElement().setIndeterminate(state.indeterminate);
-    GetElement().setChecked(state.checked);
+    GetElement().SetChecked(state.checked);
   } else {
     GetElement().DispatchInputAndChangeEventIfNeeded();
   }
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.cc b/third_party/blink/renderer/core/html/forms/html_input_element.cc
index bfec761..30d1447 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -839,13 +839,13 @@
     input_type_view_->ValueAttributeChanged();
   } else if (name == html_names::kCheckedAttr) {
     // Another radio button in the same group might be checked by state
-    // restore. We shouldn't call setChecked() even if this has the checked
-    // attribute. So, delay the setChecked() call until
+    // restore. We shouldn't call SetChecked() even if this has the checked
+    // attribute. So, delay the SetChecked() call until
     // finishParsingChildren() is called if parsing is in progress.
     if ((!parsing_in_progress_ ||
          !GetDocument().GetFormController().HasControlStates()) &&
         !dirty_checkedness_) {
-      setChecked(!value.IsNull());
+      SetChecked(!value.IsNull());
       dirty_checkedness_ = false;
     }
     PseudoStateChanged(CSSSelector::kPseudoDefault);
@@ -937,7 +937,7 @@
   if (!state_restored_) {
     bool checked = FastHasAttribute(html_names::kCheckedAttr);
     if (checked)
-      setChecked(checked);
+      SetChecked(checked);
     dirty_checkedness_ = false;
   }
 }
@@ -1010,7 +1010,7 @@
     SetNeedsValidityCheck();
   }
 
-  setChecked(FastHasAttribute(html_names::kCheckedAttr));
+  SetChecked(FastHasAttribute(html_names::kCheckedAttr));
   dirty_checkedness_ = false;
   HTMLFormControlElementWithState::ResetImpl();
 }
@@ -1040,17 +1040,17 @@
   return input_type_->IsCheckable();
 }
 
-bool HTMLInputElement::checked() const {
+bool HTMLInputElement::Checked() const {
   input_type_->ReadingChecked();
   return is_checked_;
 }
 
 void HTMLInputElement::setCheckedForBinding(bool now_checked) {
   if (GetAutofillState() != WebAutofillState::kAutofilled) {
-    setChecked(now_checked);
+    SetChecked(now_checked);
   } else {
-    bool old_value = this->checked();
-    setChecked(now_checked, TextFieldEventBehavior::kDispatchNoEvent,
+    bool old_value = this->Checked();
+    SetChecked(now_checked, TextFieldEventBehavior::kDispatchNoEvent,
                old_value == now_checked ? WebAutofillState::kAutofilled
                                         : WebAutofillState::kNotFilled);
     if (Page* page = GetDocument().GetPage()) {
@@ -1060,13 +1060,13 @@
   }
 }
 
-void HTMLInputElement::setChecked(bool now_checked,
+void HTMLInputElement::SetChecked(bool now_checked,
                                   TextFieldEventBehavior event_behavior,
                                   WebAutofillState autofill_state) {
   SetAutofillState(autofill_state);
 
   dirty_checkedness_ = true;
-  if (checked() == now_checked)
+  if (Checked() == now_checked)
     return;
 
   input_type_->WillUpdateCheckedness(now_checked);
@@ -1136,7 +1136,7 @@
 
   non_attribute_value_ = source_element.non_attribute_value_;
   has_dirty_value_ = source_element.has_dirty_value_;
-  setChecked(source_element.is_checked_);
+  SetChecked(source_element.is_checked_);
   dirty_checkedness_ = source_element.dirty_checkedness_;
   is_indeterminate_ = source_element.is_indeterminate_;
   input_type_->CopyNonAttributeProperties(source_element);
@@ -1952,7 +1952,7 @@
 }
 
 bool HTMLInputElement::ShouldAppearChecked() const {
-  return checked() && IsCheckable();
+  return Checked() && IsCheckable();
 }
 
 void HTMLInputElement::SetPlaceholderVisibility(bool visible) {
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.h b/third_party/blink/renderer/core/html/forms/html_input_element.h
index 2ef83d48..9018bb5 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.h
@@ -114,12 +114,10 @@
   bool HasBeenPasswordField() const;
 
   bool IsCheckable() const;
-  bool checkedForBinding() const { return checked(); }
+  bool checkedForBinding() const { return Checked(); }
   void setCheckedForBinding(bool);
-  // TODO(crbug.com/1314360) Capitalize checked() and setChecked() because they
-  // are not called by v8 anymore.
-  bool checked() const;
-  void setChecked(
+  bool Checked() const;
+  void SetChecked(
       bool,
       TextFieldEventBehavior = TextFieldEventBehavior::kDispatchNoEvent,
       WebAutofillState = WebAutofillState::kNotFilled);
diff --git a/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc b/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc
index d733ac0..ed73a4d 100644
--- a/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc
+++ b/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc
@@ -73,7 +73,7 @@
     return;
   checked_button_ = button;
   if (old_checked_button)
-    old_checked_button->setChecked(false);
+    old_checked_button->SetChecked(false);
 }
 
 void RadioButtonGroup::UpdateRequiredButton(MemberKeyValue& it,
@@ -97,7 +97,7 @@
     return;
   bool group_was_valid = IsValid();
   UpdateRequiredButton(*add_result.stored_value, button->IsRequired());
-  if (button->checked())
+  if (button->Checked())
     SetCheckedButton(button);
 
   bool group_is_valid = IsValid();
@@ -114,7 +114,7 @@
   DCHECK_EQ(button->type(), input_type_names::kRadio);
   DCHECK(members_.Contains(button));
   bool was_valid = IsValid();
-  if (button->checked()) {
+  if (button->Checked()) {
     SetCheckedButton(button);
   } else {
     if (checked_button_ == button)
diff --git a/third_party/blink/renderer/core/html/forms/radio_input_type.cc b/third_party/blink/renderer/core/html/forms/radio_input_type.cc
index 0514ac5..d2ee69d 100644
--- a/third_party/blink/renderer/core/html/forms/radio_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/radio_input_type.cc
@@ -77,7 +77,7 @@
     if (another->type() != input_type_names::kRadio ||
         another->GetName() != name || another->formOwner())
       continue;
-    if (another->checked())
+    if (another->Checked())
       is_checked = true;
     if (another->FastHasAttribute(html_names::kRequiredAttr))
       is_required = true;
@@ -173,7 +173,7 @@
     // If an unselected radio is tabbed into (because the entire group has
     // nothing checked, or because of some explicit .focus() call), then allow
     // space to check it.
-    if (GetElement().checked()) {
+    if (GetElement().Checked()) {
       // If we are going to skip DispatchSimulatedClick, then at least call
       // SetActive(false) to prevent the radio from being stuck in the active
       // state.
@@ -206,13 +206,13 @@
 
   // Allow keyboard focus if we're checked or if nothing in the group is
   // checked.
-  return GetElement().checked() || !CheckedRadioButtonForGroup();
+  return GetElement().Checked() || !CheckedRadioButtonForGroup();
 }
 
 bool RadioInputType::ShouldSendChangeEventAfterCheckedChanged() {
   // Don't send a change event for a radio button that's getting unchecked.
   // This was done to match the behavior of other browsers.
-  return GetElement().checked();
+  return GetElement().Checked();
 }
 
 ClickHandlingState* RadioInputType::WillDispatchClick() {
@@ -227,9 +227,9 @@
 
   ClickHandlingState* state = MakeGarbageCollected<ClickHandlingState>();
 
-  state->checked = GetElement().checked();
+  state->checked = GetElement().Checked();
   state->checked_radio_button = CheckedRadioButtonForGroup();
-  GetElement().setChecked(true, TextFieldEventBehavior::kDispatchChangeEvent);
+  GetElement().SetChecked(true, TextFieldEventBehavior::kDispatchChangeEvent);
   is_in_click_handler_ = true;
   return state;
 }
@@ -242,12 +242,12 @@
     // still belongs to our group.
     HTMLInputElement* checked_radio_button = state.checked_radio_button.Get();
     if (!checked_radio_button)
-      GetElement().setChecked(false);
+      GetElement().SetChecked(false);
     else if (checked_radio_button->type() == input_type_names::kRadio &&
              checked_radio_button->Form() == GetElement().Form() &&
              checked_radio_button->GetName() == GetElement().GetName())
-      checked_radio_button->setChecked(true);
-  } else if (state.checked != GetElement().checked()) {
+      checked_radio_button->SetChecked(true);
+  } else if (state.checked != GetElement().Checked()) {
     GetElement().DispatchInputAndChangeEventIfNeeded();
   }
   is_in_click_handler_ = false;
@@ -280,7 +280,7 @@
 
 HTMLInputElement* RadioInputType::CheckedRadioButtonForGroup() const {
   HTMLInputElement& input = GetElement();
-  if (input.checked())
+  if (input.Checked())
     return &input;
   if (auto* scope = input.GetRadioButtonGroupScope())
     return scope->CheckedButtonForGroup(input.GetName());
@@ -298,7 +298,7 @@
     if (another->type() != input_type_names::kRadio ||
         another->GetName() != name || another->formOwner())
       continue;
-    if (another->checked())
+    if (another->Checked())
       return another;
   }
   return nullptr;
@@ -309,11 +309,11 @@
     return;
   if (GetElement().GetRadioButtonGroupScope()) {
     // Buttons in RadioButtonGroupScope are handled in
-    // HTMLInputElement::setChecked().
+    // HTMLInputElement::SetChecked().
     return;
   }
   if (auto* input = CheckedRadioButtonForGroup())
-    input->setChecked(false);
+    input->SetChecked(false);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/radio_node_list.cc b/third_party/blink/renderer/core/html/forms/radio_node_list.cc
index 3617535..e114564c 100644
--- a/third_party/blink/renderer/core/html/forms/radio_node_list.cc
+++ b/third_party/blink/renderer/core/html/forms/radio_node_list.cc
@@ -68,7 +68,7 @@
   unsigned length = this->length();
   for (unsigned i = 0; i < length; ++i) {
     const HTMLInputElement* input_element = ToRadioButtonInputElement(*item(i));
-    if (!input_element || !input_element->checked())
+    if (!input_element || !input_element->Checked())
       continue;
     return input_element->Value();
   }
@@ -83,7 +83,7 @@
     HTMLInputElement* input_element = ToRadioButtonInputElement(*item(i));
     if (!input_element || input_element->Value() != value)
       continue;
-    input_element->setChecked(true);
+    input_element->SetChecked(true);
     return;
   }
 }
diff --git a/third_party/blink/renderer/core/html/html_view_source_document.cc b/third_party/blink/renderer/core/html/html_view_source_document.cc
index f0fc58e..581712d 100644
--- a/third_party/blink/renderer/core/html/html_view_source_document.cc
+++ b/third_party/blink/renderer/core/html/html_view_source_document.cc
@@ -61,7 +61,7 @@
   void Invoke(ExecutionContext*, Event* event) override {
     DCHECK_EQ(event->type(), event_type_names::kChange);
     table_->setAttribute(html_names::kClassAttr,
-                         checkbox_->checked() ? "line-wrap" : "");
+                         checkbox_->Checked() ? "line-wrap" : "");
   }
 
   void Trace(Visitor* visitor) const override {
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
index 97a7564..6580200 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
@@ -549,7 +549,7 @@
       SetRare(nodes->getInputValue(nullptr), index, input_element->Value());
       if ((input_element->type() == input_type_names::kRadio) ||
           (input_element->type() == input_type_names::kCheckbox)) {
-        if (input_element->checked()) {
+        if (input_element->Checked()) {
           SetRare(nodes->getInputChecked(nullptr), index);
         }
       }
diff --git a/third_party/blink/renderer/core/inspector/legacy_dom_snapshot_agent.cc b/third_party/blink/renderer/core/inspector/legacy_dom_snapshot_agent.cc
index 91d73314..42f050a 100644
--- a/third_party/blink/renderer/core/inspector/legacy_dom_snapshot_agent.cc
+++ b/third_party/blink/renderer/core/inspector/legacy_dom_snapshot_agent.cc
@@ -256,7 +256,7 @@
       value->setInputValue(input_element->Value());
       if ((input_element->type() == input_type_names::kRadio) ||
           (input_element->type() == input_type_names::kCheckbox)) {
-        value->setInputChecked(input_element->checked());
+        value->setInputChecked(input_element->Checked());
       }
     }
 
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index cb983012..ca9aa7b 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3287,8 +3287,7 @@
       // TODO(layout-dev): Other solutions to handling interactions between OOFs
       // and spanner breaks may need to be considered.
       if (!box_fragment.BreakToken() ||
-          To<NGBlockBreakToken>(box_fragment.BreakToken())
-              ->IsCausedByColumnSpanner() ||
+          box_fragment.BreakToken()->IsCausedByColumnSpanner() ||
           box_fragment.IsFragmentationContextRoot()) {
         // Before forgetting any old fragments and their items, we need to clear
         // associations.
@@ -6899,8 +6898,7 @@
       layout_overflow->UniteEvenIfEmpty(fragment_layout_overflow);
 
     if (const auto* break_token = fragment.BreakToken()) {
-      consumed_block_size =
-          To<NGBlockBreakToken>(break_token)->ConsumedBlockSize();
+      consumed_block_size = break_token->ConsumedBlockSize();
     }
   }
 
@@ -7151,8 +7149,7 @@
     // writing mode, or to the right in vertical. Flipped blocks is handled
     // later, after the loop.
     if (last_fragment) {
-      const auto* break_token =
-          To<NGBlockBreakToken>(last_fragment->BreakToken());
+      const NGBlockBreakToken* break_token = last_fragment->BreakToken();
       DCHECK(break_token);
       const LayoutUnit block_offset = break_token->ConsumedBlockSize();
       if (blink::IsHorizontalWritingMode(writing_mode)) {
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
index dbb3a8e..7968c573 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -1636,7 +1636,7 @@
                            physical_fragment);
 
     if (physical_fragment.BreakToken() &&
-        !To<NGBlockBreakToken>(physical_fragment.BreakToken())->IsAtBlockEnd())
+        !physical_fragment.BreakToken()->IsAtBlockEnd())
       has_inflow_child_break_inside_line[flex_line_idx] = true;
 
     // This item may have expanded due to fragmentation. Record how large the
@@ -1660,8 +1660,7 @@
           previously_consumed_block_size != LayoutUnit::Max()) {
         LayoutUnit item_expansion;
         if (physical_fragment.BreakToken() &&
-            !To<NGBlockBreakToken>(physical_fragment.BreakToken())
-                 ->IsAtBlockEnd()) {
+            !physical_fragment.BreakToken()->IsAtBlockEnd()) {
           // We can't use the size of the fragment, as we don't
           // know how large the subsequent fragments will be (and how much
           // they'll expand the row).
diff --git a/third_party/blink/renderer/core/layout/ng/inline/inline_containing_block_utils.cc b/third_party/blink/renderer/core/layout/ng/inline/inline_containing_block_utils.cc
index 6c91fe8a..9e8c571d 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/inline_containing_block_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/inline_containing_block_utils.cc
@@ -224,8 +224,7 @@
             &current_fragment_converter, &containing_block_converter);
       }
     }
-    if (auto* break_token =
-            To<NGBlockBreakToken>(physical_fragment.BreakToken()))
+    if (const NGBlockBreakToken* break_token = physical_fragment.BreakToken())
       current_block_offset = break_token->ConsumedBlockSize();
   }
 }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
index b30ab5f..b57919e8 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
@@ -453,10 +453,6 @@
   return false;
 }
 
-NGFragmentItem::BoxItem::BoxItem(const BoxItem& other)
-    : box_fragment(other.box_fragment->PostLayout()),
-      descendants_count(other.descendants_count) {}
-
 NGFragmentItem::BoxItem::BoxItem(const NGPhysicalBoxFragment* box_fragment,
                                  wtf_size_t descendants_count)
     : box_fragment(box_fragment), descendants_count(descendants_count) {}
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
index 0b2e130..30b797b 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -82,8 +82,6 @@
   // (e.g., <span>text</span>) and atomic inlines.
   struct BoxItem {
     DISALLOW_NEW();
-    // This copy constructor looks up the "post-layout" fragment.
-    BoxItem(const BoxItem&);
     BoxItem(const NGPhysicalBoxFragment*, wtf_size_t descendants_count);
 
     // If this item is an inline box, its children are stored as following
@@ -333,9 +331,7 @@
     return MutableForPainting(*this);
   }
 
-  // Out-of-flow in nested block fragmentation may require us to replace a
-  // fragment on a line.
-  class MutableForOOFFragmentation {
+  class MutableForCloning {
     STACK_ALLOCATED();
 
    public:
@@ -346,14 +342,14 @@
 
    private:
     friend class NGFragmentItem;
-    explicit MutableForOOFFragmentation(const NGFragmentItem& item)
+    explicit MutableForCloning(const NGFragmentItem& item)
         : item_(const_cast<NGFragmentItem&>(item)) {}
 
     NGFragmentItem& item_;
   };
 
-  MutableForOOFFragmentation GetMutableForOOFFragmentation() const {
-    return MutableForOOFFragmentation(*this);
+  MutableForCloning GetMutableForCloning() const {
+    return MutableForCloning(*this);
   }
 
   bool IsHorizontal() const {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
index 7fcf03db1..579bfa7 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
@@ -471,7 +471,7 @@
     const NGFragmentItem* item = cursor.Current().Item();
     if (item->BoxFragment() != &old_fragment)
       continue;
-    item->GetMutableForOOFFragmentation().ReplaceBoxFragment(new_fragment);
+    item->GetMutableForCloning().ReplaceBoxFragment(new_fragment);
     return true;
   }
   return false;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
index 3b31d02..b3e05dc52 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -1657,8 +1657,7 @@
   // Note: |LayoutBox::GetPhysicalFragment(wtf_size_t)| is O(1).
   const auto& root_box_fragment =
       *root_block_flow_->GetPhysicalFragment(fragment_index_ - 1);
-  if (const auto* break_token =
-          To<NGBlockBreakToken>(root_box_fragment.BreakToken()))
+  if (const NGBlockBreakToken* break_token = root_box_fragment.BreakToken())
     previously_consumed_block_size_ = break_token->ConsumedBlockSize();
 }
 
@@ -1667,8 +1666,7 @@
   fragment_index_++;
   if (!root_box_fragment_)
     return;
-  if (const auto* break_token =
-          To<NGBlockBreakToken>(root_box_fragment_->BreakToken()))
+  if (const NGBlockBreakToken* break_token = root_box_fragment_->BreakToken())
     previously_consumed_block_size_ = break_token->ConsumedBlockSize();
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc b/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
index c05dcab..ab23f647 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
@@ -202,10 +202,9 @@
   }
 
   if (num_fragments > 1) {
-    total_block_size +=
-        To<NGBlockBreakToken>(
-            box.GetPhysicalFragment(num_fragments - 2)->BreakToken())
-            ->ConsumedBlockSize();
+    total_block_size += box.GetPhysicalFragment(num_fragments - 2)
+                            ->BreakToken()
+                            ->ConsumedBlockSize();
   }
   return total_block_size;
 }
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index 7001029..dc94694 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -272,11 +272,13 @@
   Base::CheckIsNotDestroyed();
 
   auto* css_container = To<LayoutBoxModelObject>(Base::Container());
-  LayoutBox* container = css_container->IsBox() ? To<LayoutBox>(css_container)
-                                                : Base::ContainingBlock();
+  DCHECK(!css_container->IsBox() || css_container->IsLayoutBlock());
+  auto* container = DynamicTo<LayoutBlock>(css_container);
+  if (!container)
+    container = Base::ContainingBlock();
   const ComputedStyle* container_style = container->Style();
   NGConstraintSpace constraint_space =
-      NGConstraintSpace::CreateFromLayoutObject(*this);
+      NGConstraintSpace::CreateFromLayoutObject(*container);
 
   // As this is part of the Legacy->NG bridge, the container_builder is used
   // for indicating the resolved size of the OOF-positioned containing-block
@@ -352,9 +354,8 @@
   // should get laid out by the actual containing block.
   NGOutOfFlowLayoutPart(css_container->CanContainAbsolutePositionObjects(),
                         css_container->CanContainFixedPositionObjects(),
-                        css_container->IsLayoutGrid(), *container_style,
-                        constraint_space, &container_builder,
-                        initial_containing_block_fixed_size)
+                        css_container->IsLayoutGrid(), constraint_space,
+                        &container_builder, initial_containing_block_fixed_size)
       .Run(/* only_layout */ this);
   const NGLayoutResult* result = container_builder.ToBoxFragment();
 
@@ -395,6 +396,11 @@
 const NGLayoutResult* LayoutNGMixin<Base>::UpdateInFlowBlockLayout() {
   Base::CheckIsNotDestroyed();
 
+  // This is an entry-point for LayoutNG from the legacy engine. This means that
+  // we need to be at a formatting context boundary, since NG and legacy don't
+  // cooperate on e.g. margin collapsing.
+  DCHECK(this->CreatesNewFormattingContext());
+
   const NGLayoutResult* previous_result = Base::GetCachedLayoutResult();
   bool is_layout_root = !Base::View()->GetLayoutState()->Next();
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index 4ec2a0d..decd35c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -2242,8 +2242,7 @@
     // initial column balancing pass.
     if (const auto* physical_fragment = DynamicTo<NGPhysicalBoxFragment>(
             &layout_result.PhysicalFragment())) {
-      if (const auto* token =
-              To<NGBlockBreakToken>(physical_fragment->BreakToken())) {
+      if (const NGBlockBreakToken* token = physical_fragment->BreakToken()) {
         // TODO(mstensho): Don't apply the margin to all overflowing fragments
         // (if any). It should only be applied after the fragment where we
         // reached the block-end of the node.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index af4ffd0..bec81d2c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -1226,8 +1226,7 @@
              box_->PhysicalFragments()) {
           PlaceChildrenInFlowThread(flow_thread, constraint_space,
                                     multicol_fragment, incoming_break_token);
-          incoming_break_token =
-              To<NGBlockBreakToken>(multicol_fragment.BreakToken());
+          incoming_break_token = multicol_fragment.BreakToken();
         }
       }
     } else {
@@ -1515,8 +1514,7 @@
     }
 
     flow_thread_offset += logical_size.block_size;
-    previous_column_break_token =
-        To<NGBlockBreakToken>(child_fragment.BreakToken());
+    previous_column_break_token = child_fragment.BreakToken();
   }
 
   if (!physical_fragment.BreakToken())
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index c09e6f0..ff86d55e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -396,12 +396,11 @@
   if (!child_box_fragment)
     return;
 
-  if (const auto* token = child_box_fragment->BreakToken()) {
+  if (const NGBlockBreakToken* token = child_box_fragment->BreakToken()) {
     // Figure out if this child break is in the same flow as this parent. If
     // it's an out-of-flow positioned box, it's not. If it's in a parallel flow,
     // it's also not.
-    if (!token->IsBlockType() ||
-        !To<NGBlockBreakToken>(token)->IsAtBlockEnd()) {
+    if (!token->IsAtBlockEnd()) {
       if (child_box_fragment->IsFloating())
         has_float_break_inside_ = true;
       else if (!child_box_fragment->IsOutOfFlowPositioned())
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index 2ac1593..f6cc475 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -759,7 +759,7 @@
       if (result->HasForcedBreak())
         forced_break_count++;
 
-      column_break_token = To<NGBlockBreakToken>(column.BreakToken());
+      column_break_token = column.BreakToken();
 
       // If we're participating in an outer fragmentation context, we'll only
       // allow as many columns as the used value of column-count, so that we
@@ -1238,7 +1238,7 @@
     if (result->HasForcedBreak())
       forced_break_count++;
 
-    break_token = To<NGBlockBreakToken>(fragment.BreakToken());
+    break_token = fragment.BreakToken();
   } while (break_token);
 
   if (ConstraintSpace().IsInitialColumnBalancingPass()) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
index 16e85f2..de38958d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
@@ -38,9 +38,6 @@
 
 NGConstraintSpace NGConstraintSpace::CreateFromLayoutObject(
     const LayoutBlock& block) {
-  // We should only ever create a constraint space from legacy layout if the
-  // object is a new formatting context.
-  DCHECK(block.CreatesNewFormattingContext());
   DCHECK(!block.IsTableCell());
 
   const LayoutBlock* cb = block.ContainingBlock();
@@ -79,13 +76,23 @@
         block.HasDefiniteLogicalHeight();
   }
 
+  // We cannot enter NG layout at an object that isn't a formatting context
+  // root. However, even though we're creating a constraint space for an object
+  // here, that doesn't have to mean that we're going to lay it out. For
+  // instance, if we're laying out an out-of-flow positioned NG object contained
+  // by a legacy object, |block| here will be the container of the OOF, not the
+  // OOF itself. It's perfectly fine if that one isn't a formatting context
+  // root, since it's being laid out by the legacy engine anyway. As for the OOF
+  // that we're actually going to lay out, it will always establish a new
+  // formatting context, since it's out-of-flow.
+  bool is_new_fc = block.CreatesNewFormattingContext();
+
   const ComputedStyle& style = block.StyleRef();
   const auto writing_mode = style.GetWritingMode();
   bool parallel_containing_block = IsParallelWritingMode(
       cb ? cb->StyleRef().GetWritingMode() : writing_mode, writing_mode);
   NGConstraintSpaceBuilder builder(writing_mode, style.GetWritingDirection(),
-                                   /* is_new_fc */ true,
-                                   !parallel_containing_block);
+                                   is_new_fc, !parallel_containing_block);
 
   if (!block.IsWritingModeRoot() || block.IsGridItem()) {
     // We don't know if the parent layout will require our baseline, so always
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_test.cc b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_test.cc
index 36b0e4ab..f6e6d3d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_test.cc
@@ -206,33 +206,32 @@
   const LayoutBox* ifc = GetLayoutBoxByElementId("ifc");
   ASSERT_EQ(ifc->PhysicalFragmentCount(), 6u);
   const NGPhysicalBoxFragment* fragment = ifc->GetPhysicalFragment(0);
-  const NGBlockBreakToken* break_token =
-      DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+  const NGBlockBreakToken* break_token = fragment->BreakToken();
   ASSERT_TRUE(break_token);
   EXPECT_FALSE(break_token->HasSeenAllChildren());
 
   fragment = ifc->GetPhysicalFragment(1);
-  break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+  break_token = fragment->BreakToken();
   ASSERT_TRUE(break_token);
   EXPECT_FALSE(break_token->HasSeenAllChildren());
 
   fragment = ifc->GetPhysicalFragment(2);
-  break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+  break_token = fragment->BreakToken();
   ASSERT_TRUE(break_token);
   EXPECT_FALSE(break_token->HasSeenAllChildren());
 
   fragment = ifc->GetPhysicalFragment(3);
-  break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+  break_token = fragment->BreakToken();
   ASSERT_TRUE(break_token);
   EXPECT_TRUE(break_token->HasSeenAllChildren());
 
   fragment = ifc->GetPhysicalFragment(4);
-  break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+  break_token = fragment->BreakToken();
   ASSERT_TRUE(break_token);
   EXPECT_TRUE(break_token->HasSeenAllChildren());
 
   fragment = ifc->GetPhysicalFragment(5);
-  break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+  break_token = fragment->BreakToken();
   EXPECT_FALSE(break_token);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
index 169a27f..8556131 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
@@ -1127,7 +1127,7 @@
   DCHECK_GT(box->PhysicalFragmentCount(), 1u);
 
   const NGPhysicalBoxFragment* previous_fragment;
-  if (const auto* break_token = To<NGBlockBreakToken>(fragment.BreakToken())) {
+  if (const NGBlockBreakToken* break_token = fragment.BreakToken()) {
     // The sequence number of the outgoing break token is the same as the index
     // of this fragment.
     DCHECK_GE(break_token->SequenceNumber(), 1u);
@@ -1139,7 +1139,7 @@
     previous_fragment =
         box->GetPhysicalFragment(box->PhysicalFragmentCount() - 2);
   }
-  return To<NGBlockBreakToken>(previous_fragment->BreakToken());
+  return previous_fragment->BreakToken();
 }
 
 wtf_size_t PreviousInnerFragmentainerIndex(
@@ -1160,23 +1160,15 @@
   for (const NGPhysicalBoxFragment& walker : box->PhysicalFragments()) {
     if (&walker == &fragment)
       return idx;
-    const auto* break_token = To<NGBlockBreakToken>(walker.BreakToken());
-
     // Find the last fragmentainer inside this fragment.
-    const auto children = break_token->ChildBreakTokens();
-    for (auto& child_token : base::Reversed(children)) {
-      DCHECK(child_token->IsBlockType());
-      if (child_token->InputNode() != break_token->InputNode()) {
-        // Not a fragmentainer (probably a spanner)
+    auto children = walker.Children();
+    for (auto& child : base::Reversed(children)) {
+      if (!child->IsFragmentainerBox()) {
+        // Not a fragmentainer (could be a spanner, OOF, etc.)
         continue;
       }
-      const auto& block_child_token = To<NGBlockBreakToken>(*child_token);
-      // There may be a break before the first column, if we had to break
-      // between the block-start border/padding of the multicol container and
-      // its contents due to space shortage.
-      if (block_child_token.IsBreakBefore())
-        continue;
-      idx = block_child_token.SequenceNumber() + 1;
+      const auto* token = To<NGBlockBreakToken>(child->BreakToken());
+      idx = token->SequenceNumber() + 1;
       break;
     }
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 8544927..6b9260b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -84,7 +84,6 @@
     : NGOutOfFlowLayoutPart(container_node.IsAbsoluteContainer(),
                             container_node.IsFixedContainer(),
                             container_node.IsGrid(),
-                            container_node.Style(),
                             container_space,
                             container_builder) {}
 
@@ -92,13 +91,10 @@
     bool is_absolute_container,
     bool is_fixed_container,
     bool is_grid_container,
-    const ComputedStyle& container_style,
     const NGConstraintSpace& container_space,
     NGBoxFragmentBuilder* container_builder,
     absl::optional<LogicalSize> initial_containing_block_fixed_size)
     : container_builder_(container_builder),
-      writing_mode_(container_style.GetWritingMode()),
-      default_writing_direction_(container_style.GetWritingDirection()),
       is_absolute_container_(is_absolute_container),
       is_fixed_container_(is_fixed_container),
       has_block_fragmentation_(container_space.HasBlockFragmentation()) {
@@ -119,9 +115,9 @@
   allow_first_tier_oof_cache_ = border_scrollbar.IsEmpty() &&
                                 !is_grid_container && !has_block_fragmentation_;
   default_containing_block_info_for_absolute_.writing_direction =
-      default_writing_direction_;
+      ConstraintSpace().GetWritingDirection();
   default_containing_block_info_for_fixed_.writing_direction =
-      default_writing_direction_;
+      ConstraintSpace().GetWritingDirection();
   if (container_builder_->HasBlockSize()) {
     default_containing_block_info_for_absolute_.rect.size =
         ShrinkLogicalSize(container_builder_->Size(), border_scrollbar);
@@ -518,8 +514,8 @@
       &inline_container_fragments, container_builder_);
 
   LogicalSize container_builder_size = container_builder_->Size();
-  PhysicalSize container_builder_physical_size =
-      ToPhysicalSize(container_builder_size, writing_mode_);
+  PhysicalSize container_builder_physical_size = ToPhysicalSize(
+      container_builder_size, ConstraintSpace().GetWritingMode());
   AddInlineContainingBlockInfo(
       inline_container_fragments,
       default_containing_block_info_for_absolute_.writing_direction,
@@ -714,12 +710,12 @@
       // current builder. Thus, we need to adjust the start offset to take the
       // writing mode of the builder into account.
       PhysicalSize physical_size =
-          ToPhysicalSize(inline_cb_size, writing_mode_);
+          ToPhysicalSize(inline_cb_size, ConstraintSpace().GetWritingMode());
       start_offset =
           start_offset
               .ConvertToPhysical(container_writing_direction,
                                  container_builder_size, physical_size)
-              .ConvertToLogical(default_writing_direction_,
+              .ConvertToLogical(ConstraintSpace().GetWritingDirection(),
                                 container_builder_size, physical_size);
     }
 
@@ -920,8 +916,7 @@
     // (representing the last column laid out in that fragment). Thus, search
     // for |current_column_break_token| in |multicol_box_fragment|'s list of
     // child break tokens and update the stored MulticolChildInfo if found.
-    const NGBlockBreakToken* break_token =
-        To<NGBlockBreakToken>(multicol_box_fragment->BreakToken());
+    const NGBlockBreakToken* break_token = multicol_box_fragment->BreakToken();
     if (current_column_index != kNotFound && break_token &&
         break_token->ChildBreakTokens().size()) {
       // If there is a column break token, it will be the last item in its
@@ -1269,7 +1264,7 @@
 
   LogicalSize container_content_size = container_info.rect.size;
   PhysicalSize container_physical_content_size = ToPhysicalSize(
-      container_content_size, default_writing_direction_.GetWritingMode());
+      container_content_size, ConstraintSpace().GetWritingMode());
 
   bool requires_content_before_breaking = false;
 
@@ -1290,14 +1285,13 @@
 
   NGLogicalStaticPosition oof_static_position =
       static_position
-          .ConvertToPhysical(
-              {default_writing_direction_, container_physical_content_size})
+          .ConvertToPhysical({ConstraintSpace().GetWritingDirection(),
+                              container_physical_content_size})
           .ConvertToLogical(
               {oof_writing_direction, container_physical_content_size});
 
   // Need a constraint space to resolve offsets.
-  NGConstraintSpaceBuilder builder(default_writing_direction_.GetWritingMode(),
-                                   oof_writing_direction,
+  NGConstraintSpaceBuilder builder(ConstraintSpace(), oof_writing_direction,
                                    /* is_new_fc */ true);
   builder.SetAvailableSize(container_content_size);
   builder.SetPercentageResolutionSize(container_content_size);
@@ -1306,7 +1300,7 @@
     // The |fragmentainer_offset_delta| will not make a difference in the
     // initial column balancing pass.
     SetupSpaceBuilderForFragmentation(
-        container_builder_->ConstraintSpace(), node,
+        ConstraintSpace(), node,
         /* fragmentainer_offset_delta */ LayoutUnit(), &builder,
         /* is_new_fc */ true,
         /* requires_content_before_breaking */ false);
@@ -1323,7 +1317,7 @@
 
   return NodeInfo(node, builder.ToConstraintSpace(), oof_static_position,
                   container_physical_content_size, container_info,
-                  default_writing_direction_,
+                  ConstraintSpace().GetWritingDirection(),
                   /* is_fragmentainer_descendant */ containing_block_fragment,
                   fixedpos_containing_block, fixedpos_inline_container,
                   oof_node.inline_container.container,
@@ -1361,8 +1355,7 @@
     bool freeze_horizontal = false, freeze_vertical = false;
     // If we're in a measure pass, freeze both scrollbars right away, to avoid
     // quadratic time complexity for deeply nested flexboxes.
-    if (container_builder_->ConstraintSpace().CacheSlot() ==
-        NGCacheSlot::kMeasure)
+    if (ConstraintSpace().CacheSlot() == NGCacheSlot::kMeasure)
       freeze_horizontal = freeze_vertical = true;
     do {
       // Freeze any scrollbars that appeared, and relayout. Repeat until both
@@ -1603,12 +1596,16 @@
   LayoutUnit inline_size = node_dimensions.size.inline_size;
   LayoutUnit block_size = block_estimate.value_or(
       container_content_size_in_candidate_writing_mode.block_size);
+  LogicalSize logical_size(inline_size, block_size);
+  // Convert from logical size in the writing mode of the child to the logical
+  // size in the writing mode of the container. That's what the constraint space
+  // builder expects.
+  PhysicalSize physical_size =
+      ToPhysicalSize(logical_size, style.GetWritingMode());
+  LogicalSize available_size =
+      physical_size.ConvertToLogical(ConstraintSpace().GetWritingMode());
 
-  LogicalSize available_size(inline_size, block_size);
-
-  // As the |block_estimate| is always in the node's writing mode, we build the
-  // constraint space in the node's writing mode.
-  NGConstraintSpaceBuilder builder(style.GetWritingMode(),
+  NGConstraintSpaceBuilder builder(ConstraintSpace(),
                                    style.GetWritingDirection(),
                                    /* is_new_fc */ true);
   builder.SetAvailableSize(available_size);
@@ -1623,8 +1620,7 @@
         /* is_new_fc */ true, requires_content_before_breaking);
   } else if (container_builder_->IsInitialColumnBalancingPass()) {
     SetupSpaceBuilderForFragmentation(
-        container_builder_->ConstraintSpace(), node, block_offset, &builder,
-        /* is_new_fc */ true,
+        ConstraintSpace(), node, block_offset, &builder, /* is_new_fc */ true,
         /* requires_content_before_breaking */ false);
   }
   DeferredShapingMinimumTopScope minimum_top_scope(node, block_offset);
@@ -1819,8 +1815,7 @@
         *container, /* previous_container_break_token */ nullptr);
   }
 
-  const NGBlockBreakToken* break_token =
-      To<NGBlockBreakToken>(physical_fragment.BreakToken());
+  const NGBlockBreakToken* break_token = physical_fragment.BreakToken();
   if (break_token) {
     // We must continue layout in the next fragmentainer. Update any information
     // in NodeToLayout, and add the node to |fragmented_descendants|.
@@ -1944,9 +1939,9 @@
   // TODO(bebeaudr): Need to handle different fragmentation types. It won't
   // always be multi-column.
   return CreateConstraintSpaceForColumns(
-      container_builder_->ConstraintSpace(), column_size,
-      percentage_resolution_size, allow_discard_start_margin,
-      /* balance_columns */ false, min_break_appeal);
+      ConstraintSpace(), column_size, percentage_resolution_size,
+      allow_discard_start_margin, /* balance_columns */ false,
+      min_break_appeal);
 }
 
 // Compute in which fragmentainer the OOF element will start its layout and
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 00e3114..a22b1f1 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -22,7 +22,6 @@
 
 namespace blink {
 
-class ComputedStyle;
 class LayoutBox;
 class LayoutObject;
 class NGBlockBreakToken;
@@ -57,7 +56,6 @@
       bool is_absolute_container,
       bool is_fixed_container,
       bool is_grid_container,
-      const ComputedStyle& container_style,
       const NGConstraintSpace& container_space,
       NGBoxFragmentBuilder* container_builder,
       absl::optional<LogicalSize> initial_containing_block_fixed_size =
@@ -224,6 +222,9 @@
       return kFragmentPage;
     return kFragmentColumn;
   }
+  const NGConstraintSpace& ConstraintSpace() const {
+    return container_builder_->ConstraintSpace();
+  }
 
   void ComputeInlineContainingBlocks(
       const HeapVector<NGLogicalOutOfFlowPositionedNode>&);
@@ -344,8 +345,6 @@
   ContainingBlockInfo default_containing_block_info_for_fixed_;
   HeapHashMap<Member<const LayoutObject>, ContainingBlockInfo>
       containing_blocks_map_;
-  const WritingMode writing_mode_;
-  const WritingDirectionMode default_writing_direction_;
 
   // Out-of-flow positioned nodes that we should lay out at a later time. For
   // example, if the containing block has not finished layout.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
index 300acf7..de750665 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
@@ -1625,8 +1625,7 @@
   ASSERT_EQ(children.size(), 5u);
 
   const auto& column1 = To<NGPhysicalBoxFragment>(*children[0]);
-  const NGBlockBreakToken* break_token =
-      To<NGBlockBreakToken>(column1.BreakToken());
+  const NGBlockBreakToken* break_token = column1.BreakToken();
   EXPECT_TRUE(break_token);
   EXPECT_EQ(break_token->SequenceNumber(), 0u);
   EXPECT_EQ(break_token->ConsumedBlockSize(), 100);
@@ -1634,7 +1633,7 @@
   EXPECT_FALSE(break_token->IsCausedByColumnSpanner());
 
   const auto& column2 = To<NGPhysicalBoxFragment>(*children[1]);
-  break_token = To<NGBlockBreakToken>(column2.BreakToken());
+  break_token = column2.BreakToken();
   EXPECT_TRUE(break_token);
   EXPECT_EQ(break_token->SequenceNumber(), 1u);
   EXPECT_EQ(break_token->ConsumedBlockSize(), 200);
@@ -1645,7 +1644,7 @@
   EXPECT_TRUE(spanner.IsColumnSpanAll());
 
   const auto& column3 = To<NGPhysicalBoxFragment>(*children[3]);
-  break_token = To<NGBlockBreakToken>(column3.BreakToken());
+  break_token = column3.BreakToken();
   EXPECT_TRUE(break_token);
   EXPECT_EQ(break_token->SequenceNumber(), 2u);
   EXPECT_EQ(break_token->ConsumedBlockSize(), 250);
@@ -1686,8 +1685,7 @@
   ASSERT_EQ(children.size(), 5u);
 
   const auto& column1 = To<NGPhysicalBoxFragment>(*children[0]);
-  const NGBlockBreakToken* break_token =
-      To<NGBlockBreakToken>(column1.BreakToken());
+  const NGBlockBreakToken* break_token = column1.BreakToken();
   EXPECT_TRUE(break_token);
   EXPECT_EQ(break_token->SequenceNumber(), 0u);
   EXPECT_EQ(break_token->ConsumedBlockSize(), 100);
@@ -1695,7 +1693,7 @@
   EXPECT_TRUE(break_token->IsCausedByColumnSpanner());
 
   const auto& column2 = To<NGPhysicalBoxFragment>(*children[1]);
-  break_token = To<NGBlockBreakToken>(column2.BreakToken());
+  break_token = column2.BreakToken();
   EXPECT_TRUE(break_token);
   EXPECT_EQ(break_token->SequenceNumber(), 1u);
   EXPECT_EQ(break_token->ConsumedBlockSize(), 200);
@@ -1706,7 +1704,7 @@
   EXPECT_TRUE(spanner.IsColumnSpanAll());
 
   const auto& column3 = To<NGPhysicalBoxFragment>(*children[3]);
-  break_token = To<NGBlockBreakToken>(column3.BreakToken());
+  break_token = column3.BreakToken();
   EXPECT_TRUE(break_token);
   EXPECT_EQ(break_token->SequenceNumber(), 2u);
   EXPECT_EQ(break_token->ConsumedBlockSize(), 250);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 1fc575f7..9cc42c8b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -238,9 +238,48 @@
       other.has_borders_, other.has_padding_, other.has_inflow_bounds_,
       other.const_has_rare_data_);
 
-  return MakeGarbageCollected<NGPhysicalBoxFragment>(
+  const auto* cloned_fragment = MakeGarbageCollected<NGPhysicalBoxFragment>(
       AdditionalBytes(byte_size), PassKey(), other, has_layout_overflow,
       layout_overflow);
+
+  // To ensure the fragment tree is consistent, use the post-layout fragment.
+#if DCHECK_IS_ON()
+  AllowPostLayoutScope allow_post_layout_scope;
+#endif
+
+  for (NGLink& child : cloned_fragment->GetMutableForCloning().Children()) {
+    child.fragment = child->PostLayout();
+    DCHECK(child.fragment);
+
+    if (!child->IsFragmentainerBox())
+      continue;
+
+    // Fragmentainers don't have the concept of post-layout fragments, so if
+    // this is a fragmentation context root (such as a multicol container), we
+    // need to not only update its children, but also the children of the
+    // children that are fragmentainers.
+    auto& fragmentainer = *To<NGPhysicalBoxFragment>(child.fragment.Get());
+    for (NGLink& fragmentainer_child :
+         fragmentainer.GetMutableForCloning().Children()) {
+      auto& old_child =
+          *To<NGPhysicalBoxFragment>(fragmentainer_child.fragment.Get());
+      fragmentainer_child.fragment = old_child.PostLayout();
+    }
+  }
+
+  if (cloned_fragment->HasItems()) {
+    // Replace box fragment items with post layout fragments.
+    for (const auto& cloned_item : cloned_fragment->Items()->Items()) {
+      const NGPhysicalBoxFragment* box = cloned_item.BoxFragment();
+      if (!box)
+        continue;
+      box = box->PostLayout();
+      DCHECK(box);
+      cloned_item.GetMutableForCloning().ReplaceBoxFragment(*box);
+    }
+  }
+
+  return cloned_fragment;
 }
 
 // static
@@ -284,6 +323,7 @@
       const_num_children_(builder->children_.size()) {
   DCHECK(layout_object_);
   DCHECK(layout_object_->IsBoxModelObject());
+  DCHECK(!builder->break_token_ || builder->break_token_->IsBlockType());
 
   PhysicalSize size = Size();
   const WritingModeConverter converter(
@@ -390,32 +430,9 @@
       baseline_(other.baseline_),
       last_baseline_(other.last_baseline_),
       ink_overflow_(other.InkOverflowType(), other.ink_overflow_) {
-  // To ensure the fragment tree is consistent, use the post-layout fragment.
-#if DCHECK_IS_ON()
-  AllowPostLayoutScope allow_post_layout_scope;
-#endif
-  for (wtf_size_t i = 0; i < const_num_children_; ++i) {
-    children_[i].offset = other.children_[i].offset;
-    const NGPhysicalFragment& other_child_fragment = *other.children_[i];
-    const NGPhysicalFragment* post_layout = other_child_fragment.PostLayout();
-    DCHECK(post_layout);
-    new (&children_[i].fragment) Member<const NGPhysicalFragment>(post_layout);
-
-    if (!children_[i]->IsFragmentainerBox())
-      continue;
-
-    // Fragmentainers don't have the concept of post-layout fragments, so if
-    // this is a fragmentation context root (such as a multicol container), we
-    // need to not only update its children, but also the children of the
-    // children that are fragmentainers.
-    auto* fragmentainer = const_cast<NGPhysicalBoxFragment*>(
-        To<NGPhysicalBoxFragment>(children_[i].fragment.Get()));
-    for (wtf_size_t j = 0; j < fragmentainer->const_num_children_; ++j) {
-      NGLink& link = fragmentainer->children_[j];
-      auto& old_child = *To<NGPhysicalBoxFragment>(link.fragment.Get());
-      link.fragment = old_child.PostLayout();
-    }
-  }
+  // Shallow-clone the children.
+  for (wtf_size_t i = 0; i < const_num_children_; ++i)
+    children_[i] = other.children_[i];
 
   ink_overflow_type_ = other.ink_overflow_type_;
   if (const_has_fragment_items_) {
@@ -671,15 +688,13 @@
   if (fragment_count == 1) {
     post_layout = box->GetPhysicalFragment(0);
     DCHECK(post_layout);
-  } else if (const auto* break_token = To<NGBlockBreakToken>(BreakToken())) {
+  } else if (const auto* break_token = BreakToken()) {
     const unsigned index = break_token->SequenceNumber();
     if (index < fragment_count) {
       post_layout = box->GetPhysicalFragment(index);
       DCHECK(post_layout);
-      DCHECK(
-          !post_layout->BreakToken() ||
-          To<NGBlockBreakToken>(post_layout->BreakToken())->SequenceNumber() ==
-              index);
+      DCHECK(!post_layout->BreakToken() ||
+             post_layout->BreakToken()->SequenceNumber() == index);
     }
   } else {
     post_layout = &box->PhysicalFragments().back();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
index 9e89f48..baf7443 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
 #include "third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_borders.h"
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h"
@@ -22,7 +23,6 @@
 
 namespace blink {
 
-class NGBlockBreakToken;
 class NGBoxFragmentBuilder;
 enum class NGOutlineType;
 
@@ -52,6 +52,10 @@
                         bool has_rare_data,
                         WritingMode block_or_line_writing_mode);
 
+  // Make a shallow copy. The child fragment pointers are just shallowly
+  // copied. Fragment *items* are cloned (but not box fragments associated with
+  // items), though. Additionally, the copy will set new overflow information,
+  // based on the parameters, rather than copying it from the original fragment.
   NGPhysicalBoxFragment(PassKey,
                         const NGPhysicalBoxFragment& other,
                         bool has_layout_overflow,
@@ -345,6 +349,10 @@
                             include_border_bottom_, include_border_left_);
   }
 
+  const NGBlockBreakToken* BreakToken() const {
+    return To<NGBlockBreakToken>(NGPhysicalFragment::BreakToken());
+  }
+
   // Return true if this is the first fragment generated from a node.
   bool IsFirstForNode() const { return is_first_for_node_; }
 
@@ -403,6 +411,28 @@
     return MutableForPainting(*this);
   }
 
+  class MutableForCloning {
+    STACK_ALLOCATED();
+    friend class NGPhysicalBoxFragment;
+
+   public:
+    base::span<NGLink> Children() const {
+      DCHECK(fragment_.children_valid_);
+      return base::make_span(fragment_.children_,
+                             fragment_.const_num_children_);
+    }
+
+   private:
+    explicit MutableForCloning(const NGPhysicalBoxFragment& fragment)
+        : fragment_(const_cast<NGPhysicalBoxFragment&>(fragment)) {}
+
+    NGPhysicalBoxFragment& fragment_;
+  };
+  friend class MutableForCloning;
+  MutableForCloning GetMutableForCloning() const {
+    return MutableForCloning(*this);
+  }
+
   // Returns if this fragment can compute ink overflow.
   bool CanUseFragmentsForInkOverflow() const { return !IsLegacyLayoutRoot(); }
   // Recalculates and updates |*InkOverflow|.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_simplified_oof_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_simplified_oof_layout_algorithm.cc
index c356eb4..57d1147 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_simplified_oof_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_simplified_oof_layout_algorithm.cc
@@ -28,8 +28,8 @@
   container_builder_.SetDisableOOFDescendantsPropagation();
   container_builder_.SetHasOutOfFlowFragmentChild(true);
 
-  const auto* old_fragment_break_token =
-      To<NGBlockBreakToken>(previous_fragment.BreakToken());
+  const NGBlockBreakToken* old_fragment_break_token =
+      previous_fragment.BreakToken();
   if (old_fragment_break_token) {
     container_builder_.SetHasColumnSpanner(
         old_fragment_break_token->IsCausedByColumnSpanner());
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index d3278b5..f7fc51b5 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -889,8 +889,7 @@
                                              LayoutUnit block_offset,
                                              wtf_size_t section_index) {
     NGConstraintSpaceBuilder section_space_builder(
-        table_writing_direction.GetWritingMode(), table_writing_direction,
-        /* is_new_fc */ true);
+        ConstraintSpace(), table_writing_direction, /* is_new_fc */ true);
 
     LogicalSize available_size = {section_available_inline_size,
                                   kIndefiniteSize};
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
index 81e0f771..79aefdc 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -195,17 +195,21 @@
         cell, row_index, colspan_cell_tabulator->CurrentColumn(), section_index,
         table_writing_direction);
 
+    NGConstraintSpaceBuilder space_builder(
+        table_writing_direction.GetWritingMode(),
+        cell.Style().GetWritingDirection(), /* is_new_fc */ true);
+
     // We want these values to match the "layout" pass as close as possible.
-    const auto cell_space =
-        NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder(
-            table_writing_direction, cell, cell_borders, column_locations,
-            /* cell_block_size */ kIndefiniteSize, cell_percentage_inline_size,
-            /* alignment_baseline */ absl::nullopt,
-            colspan_cell_tabulator->CurrentColumn(),
-            /* is_initial_block_size_indefinite */ true,
-            is_table_block_size_specified, has_collapsed_borders,
-            NGCacheSlot::kMeasure)
-            .ToConstraintSpace();
+    NGTableAlgorithmUtils::SetupTableCellConstraintSpaceBuilder(
+        table_writing_direction, cell, cell_borders, column_locations,
+        /* cell_block_size */ kIndefiniteSize, cell_percentage_inline_size,
+        /* alignment_baseline */ absl::nullopt,
+        colspan_cell_tabulator->CurrentColumn(),
+        /* is_initial_block_size_indefinite */ true,
+        is_table_block_size_specified, has_collapsed_borders,
+        NGCacheSlot::kMeasure, &space_builder);
+
+    const auto cell_space = space_builder.ToConstraintSpace();
     const NGLayoutResult* layout_result = cell.Layout(cell_space);
 
     const NGBoxFragment fragment(
@@ -476,8 +480,7 @@
 }
 
 // static
-NGConstraintSpaceBuilder
-NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder(
+void NGTableAlgorithmUtils::SetupTableCellConstraintSpaceBuilder(
     const WritingDirectionMode table_writing_direction,
     const NGBlockNode cell,
     const NGBoxStrut& cell_borders,
@@ -489,7 +492,8 @@
     bool is_initial_block_size_indefinite,
     bool is_table_block_size_specified,
     bool has_collapsed_borders,
-    NGCacheSlot cache_slot) {
+    NGCacheSlot cache_slot,
+    NGConstraintSpaceBuilder* builder) {
   const auto& cell_style = cell.Style();
   const auto table_writing_mode = table_writing_direction.GetWritingMode();
   const wtf_size_t end_column = std::min(
@@ -507,41 +511,36 @@
     return true;
   }();
 
-  NGConstraintSpaceBuilder builder(table_writing_mode,
-                                   cell_style.GetWritingDirection(),
-                                   /* is_new_fc */ true);
-  builder.SetIsTableCell(true);
+  builder->SetIsTableCell(true);
 
   if (!IsParallelWritingMode(table_writing_mode, cell_style.GetWritingMode())) {
     const PhysicalSize icb_size = cell.InitialContainingBlockSize();
-    builder.SetOrthogonalFallbackInlineSize(
+    builder->SetOrthogonalFallbackInlineSize(
         table_writing_direction.IsHorizontal() ? icb_size.height
                                                : icb_size.width);
   }
 
-  builder.SetAvailableSize({cell_inline_size, cell_block_size});
-  builder.SetIsFixedInlineSize(true);
+  builder->SetAvailableSize({cell_inline_size, cell_block_size});
+  builder->SetIsFixedInlineSize(true);
   if (cell_block_size != kIndefiniteSize)
-    builder.SetIsFixedBlockSize(true);
-  builder.SetIsInitialBlockSizeIndefinite(is_initial_block_size_indefinite);
+    builder->SetIsFixedBlockSize(true);
+  builder->SetIsInitialBlockSizeIndefinite(is_initial_block_size_indefinite);
 
   // https://www.w3.org/TR/css-tables-3/#computing-the-table-height
   // "the computed height (if definite, percentages being considered 0px)"
-  builder.SetPercentageResolutionSize(
+  builder->SetPercentageResolutionSize(
       {percentage_inline_size, kIndefiniteSize});
 
-  builder.SetTableCellBorders(cell_borders);
-  builder.SetTableCellAlignmentBaseline(alignment_baseline);
-  builder.SetTableCellColumnIndex(start_column);
-  builder.SetIsRestrictedBlockSizeTableCell(
+  builder->SetTableCellBorders(cell_borders);
+  builder->SetTableCellAlignmentBaseline(alignment_baseline);
+  builder->SetTableCellColumnIndex(start_column);
+  builder->SetIsRestrictedBlockSizeTableCell(
       is_table_block_size_specified || cell_style.LogicalHeight().IsFixed());
-  builder.SetIsTableCellHiddenForPaint(is_hidden_for_paint);
-  builder.SetIsTableCellWithCollapsedBorders(has_collapsed_borders);
-  builder.SetHideTableCellIfEmpty(
+  builder->SetIsTableCellHiddenForPaint(is_hidden_for_paint);
+  builder->SetIsTableCellWithCollapsedBorders(has_collapsed_borders);
+  builder->SetHideTableCellIfEmpty(
       !has_collapsed_borders && cell_style.EmptyCells() == EEmptyCells::kHide);
-  builder.SetCacheSlot(cache_slot);
-
-  return builder;
+  builder->SetCacheSlot(cache_slot);
 }
 
 // Computes maximum possible number of non-mergeable columns.
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
index 4d4a88a..83a4c96 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
@@ -42,11 +42,11 @@
       const LogicalSize& border_spacing,
       bool is_table_block_size_specified);
 
-  // Creates a constraint space builder for a table-cell.
+  // Sets up a constraint space builder for a table-cell.
   //
   // In order to make the cache as effective as possible, we try and keep
   // creating the constraint-space for table-cells as consistent as possible.
-  static NGConstraintSpaceBuilder CreateTableCellConstraintSpaceBuilder(
+  static void SetupTableCellConstraintSpaceBuilder(
       const WritingDirectionMode table_writing_direction,
       const NGBlockNode cell,
       const NGBoxStrut& cell_borders,
@@ -58,7 +58,8 @@
       bool is_initial_block_size_indefinite,
       bool is_restricted_block_size_table,
       bool has_collapsed_borders,
-      NGCacheSlot);
+      NGCacheSlot,
+      NGConstraintSpaceBuilder*);
 
   static wtf_size_t ComputeMaximumNonMergeableColumnCount(
       const HeapVector<NGBlockNode>& columns,
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
index da67708..9a7fb30 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
@@ -48,15 +48,20 @@
                 ? cell_data.rowspan_block_size
                 : row_block_size;
 
-        NGConstraintSpaceBuilder builder =
-            NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder(
-                table_data.table_writing_direction, cell, cell_data.borders,
-                table_data.column_locations, cell_block_size,
-                container_builder_.InlineSize(), row_baseline,
-                cell_data.start_column,
-                cell_data.is_initial_block_size_indefinite,
-                table_data.is_table_block_size_specified,
-                table_data.has_collapsed_borders, NGCacheSlot::kLayout);
+        DCHECK_EQ(table_data.table_writing_direction.GetWritingMode(),
+                  ConstraintSpace().GetWritingMode());
+
+        NGConstraintSpaceBuilder builder(ConstraintSpace(),
+                                         cell.Style().GetWritingDirection(),
+                                         /* is_new_fc */ true);
+
+        NGTableAlgorithmUtils::SetupTableCellConstraintSpaceBuilder(
+            table_data.table_writing_direction, cell, cell_data.borders,
+            table_data.column_locations, cell_block_size,
+            container_builder_.InlineSize(), row_baseline,
+            cell_data.start_column, cell_data.is_initial_block_size_indefinite,
+            table_data.is_table_block_size_specified,
+            table_data.has_collapsed_borders, NGCacheSlot::kLayout, &builder);
 
         if (ConstraintSpace().HasBlockFragmentation()) {
           SetupSpaceBuilderForFragmentation(
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc
index d2c1cd1..668d553 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc
@@ -74,9 +74,11 @@
     if (!is_first_non_collapsed_row && !is_row_collapsed)
       offset.block_offset += table_data.table_border_spacing.block_size;
 
+    DCHECK_EQ(table_data.table_writing_direction.GetWritingMode(),
+              ConstraintSpace().GetWritingMode());
+
     NGConstraintSpaceBuilder row_space_builder(
-        table_data.table_writing_direction.GetWritingMode(),
-        table_data.table_writing_direction,
+        ConstraintSpace(), table_data.table_writing_direction,
         /* is_new_fc */ true);
     row_space_builder.SetAvailableSize(available_size);
     row_space_builder.SetPercentageResolutionSize(available_size);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 191d8e3..45b405fe 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -336,7 +336,7 @@
 // context. We need to provide this ID when block-fragmenting, so that we can
 // cache the painting of each individual fragment.
 unsigned FragmentainerUniqueIdentifier(const NGPhysicalBoxFragment& fragment) {
-  if (const auto* break_token = To<NGBlockBreakToken>(fragment.BreakToken()))
+  if (const auto* break_token = fragment.BreakToken())
     return break_token->SequenceNumber() + 1;
   return 0;
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc
index 00a99fdd..7efd4d0 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc
@@ -173,7 +173,7 @@
   playback_speed_item_input->SetFloatingPointAttribute(PlaybackRateAttrName(),
                                                        playback_rate);
   if (playback_rate == MediaElement().playbackRate()) {
-    playback_speed_item_input->setChecked(true);
+    playback_speed_item_input->SetChecked(true);
     playback_speed_item->setAttribute(html_names::kAriaCheckedAttr, "true");
     checked_item_ = playback_speed_item;
   }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
index 6079316..48353f8 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
@@ -123,14 +123,14 @@
   track_item_input->SetIntegralAttribute(TrackIndexAttrName(), track_index);
   if (!MediaElement().TextTracksVisible()) {
     if (!track) {
-      track_item_input->setChecked(true);
+      track_item_input->SetChecked(true);
       track_item->setAttribute(html_names::kAriaCheckedAttr, "true");
     }
   } else {
     // If there are multiple text tracks set to showing, they must all have
     // checkmarks displayed.
     if (track && track->mode() == TextTrack::ShowingKeyword()) {
-      track_item_input->setChecked(true);
+      track_item_input->SetChecked(true);
       track_item->setAttribute(html_names::kAriaCheckedAttr, "true");
     } else {
       track_item->setAttribute(html_names::kAriaCheckedAttr, "false");
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
index 9f3c9c3..0fbcb0f 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
@@ -365,7 +365,6 @@
   if (!mailbox_texture_)
     return;
 
-  GetProcs().textureDestroy(mailbox_texture_->GetTexture());
   mailbox_texture_.reset();
 }
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 0063f07..c9cdd98 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1569,6 +1569,7 @@
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-avoidance-004.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-avoidance-005.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-avoidance-006.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-avoidance-in-table-in-grid.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-before-expansion-001.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-before-expansion-002.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-before-table-cell.html [ Pass ]
@@ -3908,6 +3909,7 @@
 crbug.com/1078927 external/wpt/css/css-break/table/break-avoidance-004.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/break-avoidance-005.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/break-avoidance-006.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/break-avoidance-in-table-in-grid.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/break-before-expansion-001.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/break-before-expansion-002.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/break-before-table-cell.html [ Failure ]
@@ -7019,3 +7021,6 @@
 
 # Sheriff 2022-05-30
 crbug.com/1330238 [ Mac ] http/tests/devtools/elements/styles-3/styles-computed-trace.js [ Failure Pass Timeout ]
+
+# Sheriff 2022-05-31
+crbug.com/1330300 fast/css-grid-layout/grid-auto-repeat-huge-grid-006.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 5a51b044..d1bc72e 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 333c51b8d345b00b05854d9774b56bc95098f1c3
+Version: fecd856ffe33aac9d46231b70d1768feebc87dfe
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 b4c9ea9..ded511cb 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
@@ -295018,14 +295018,6 @@
       []
      ]
     },
-    "tools": {
-     "_virtualenv": {
-      ".gitignore": [
-       "ede80b85f0f30f7d19ce1a46d6b421fac3c1f82f",
-       []
-      ]
-     }
-    },
     "vendor-imports": {
      "mozilla": {
       "mozilla-central-reftests": {
@@ -331861,7 +331853,7 @@
        []
       ],
       "fixtures_bidi.py": [
-       "4e900bb55b9b40418e69d97e22d16c0476c3cfd8",
+       "3bcebb95f4b5be91054219dd669274d3c8a3ff2a",
        []
       ],
       "fixtures_http.py": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/break-avoidance-in-table-in-grid.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/break-avoidance-in-table-in-grid.html
new file mode 100644
index 0000000..6250ea7e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/break-avoidance-in-table-in-grid.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#break-between">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="width:100px; height:100px; background:red;">
+  <div style="columns:2; gap:0; column-fill:auto; height:150px;">
+    <div style="display:grid;">
+      <div style="height:100px; background:green;"></div>
+      <div>
+        <div style="contain:size; height:40px; background:green;"></div>
+      </div>
+      <div>
+        <div style="display:table; width:100%;">
+          <div style="break-before:avoid; contain:size; height:60px; background:green;"></div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures_bidi.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures_bidi.py
index 4e900bb..3bcebb9 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures_bidi.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures_bidi.py
@@ -6,6 +6,23 @@
 
 
 @pytest.fixture
+async def new_tab(bidi_session, current_session):
+    # Open and focus a new tab to run the test in a foreground tab.
+    context_id = current_session.new_window(type_hint="tab")
+    initial_window = current_session.window_handle
+    current_session.window_handle = context_id
+
+    # Retrieve the browsing context info for the new tab
+    contexts = await bidi_session.browsing_context.get_tree(root=context_id, max_depth=0)
+    yield contexts[0]
+
+    # Restore the focus and current window for the WebDriver session before
+    # closing the tab.
+    current_session.window_handle = initial_window
+    await bidi_session.browsing_context.close(context=contexts[0]["context"])
+
+
+@pytest.fixture
 def send_blocking_command(bidi_session):
     """Send a blocking command that awaits until the BiDi response has been received."""
     async def send_blocking_command(command: str, params: Mapping[str, Any]) -> Mapping[str, Any]:
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/notification.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/notification.https.html
new file mode 100644
index 0000000..5b49a3c
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/notification.https.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Test Notification</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/utils.js"></script>
+
+<body>
+<script>
+promise_test(async () => {
+  // Notification permission must be allowed or we cannot confirm that
+  // "new Notification" failed in a fenced frame.
+  await test_driver.set_permission({name: 'notifications'}, 'granted', true);
+  assert_equals(Notification.permission, 'granted');
+
+  const frame = attachFencedFrameContext();
+  try {
+    await frame.execute(() => {
+      const notification = new Notification('Test notification');
+      return new Promise((resolve, reject) => {
+        // "new Notification" inside the fenced frame should fail even if it is
+        // allowed in the primary main frame.
+        notification.addEventListener('error', resolve);
+        notification.addEventListener('show', reject);
+      });
+    });
+  } catch(e) {
+    assert_unreached('Notification was shown; want not to be shown.' + e);
+  }
+}, 'new Notification should fail inside a fenced frame');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html b/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html
index 8baefb3..83cae8a 100644
--- a/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html
+++ b/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html
@@ -2587,6 +2587,7 @@
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,buffer,in_pass_encoder:subresources,buffer_usage_in_one_compute_pass_with_no_dispatch:*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,buffer,in_pass_encoder:subresources,buffer_usage_in_one_compute_pass_with_one_dispatch:*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,buffer,in_pass_encoder:subresources,buffer_usage_in_compute_pass_with_two_dispatches:*'>
+<meta name=variant content='?q=webgpu:api,validation,resource_usages,buffer,in_pass_encoder:subresources,buffer_usage_in_one_render_pass_with_no_draw:*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_color:*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:compute=false;binding0InBundle=false;*'>
 <meta name=variant content='?q=webgpu:api,validation,resource_usages,texture,in_pass_encoder:subresources_and_binding_types_combination_for_aspect:compute=false;binding0InBundle=true;*'>
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index ca58b86..ee7483e 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -41,6 +41,8 @@
 }
 
 source_set("zlib_common_headers") {
+  visibility = [ ":*" ]
+
   sources = [
     "chromeconf.h",
     "deflate.h",
diff --git a/third_party/zlib/README.chromium b/third_party/zlib/README.chromium
index ea32e52..f0faeae 100644
--- a/third_party/zlib/README.chromium
+++ b/third_party/zlib/README.chromium
@@ -28,3 +28,4 @@
  - Code in contrib/ other than contrib/minizip was added to match zlib's
    contributor layout.
  - In sync with 1.2.12 release plus a few fixes from 'develop' branch.
+ - ZIP reader modified to allow for progress callbacks during extraction.
diff --git a/third_party/zlib/google/zip.cc b/third_party/zlib/google/zip.cc
index 1a43196e..7d5404b4 100644
--- a/third_party/zlib/google/zip.cc
+++ b/third_party/zlib/google/zip.cc
@@ -229,7 +229,10 @@
 
     // It's a file.
     std::unique_ptr<WriterDelegate> writer = writer_factory.Run(entry->path);
-    if (!writer || !reader.ExtractCurrentEntry(writer.get())) {
+    if (!writer ||
+        (options.progress ? !reader.ExtractCurrentEntryWithListener(
+                                writer.get(), options.progress)
+                          : !reader.ExtractCurrentEntry(writer.get()))) {
       LOG(ERROR) << "Cannot extract file " << Redact(entry->path)
                  << " from ZIP";
       if (!options.continue_on_error)
diff --git a/third_party/zlib/google/zip.h b/third_party/zlib/google/zip.h
index 25ec655..0d9af0c 100644
--- a/third_party/zlib/google/zip.h
+++ b/third_party/zlib/google/zip.h
@@ -170,6 +170,9 @@
               int dest_fd);
 #endif  // defined(OS_POSIX) || defined(OS_FUCHSIA)
 
+// Callback reporting the number of bytes written during Unzip.
+using UnzipProgressCallback = base::RepeatingCallback<void(uint64_t bytes)>;
+
 // Options of the Unzip function, with valid default values.
 struct UnzipOptions {
   // Encoding of entry paths in the ZIP archive. By default, paths are assumed
@@ -180,6 +183,9 @@
   // everything gets extracted.
   FilterCallback filter;
 
+  // Callback to report bytes extracted from the ZIP.
+  UnzipProgressCallback progress;
+
   // Password to decrypt the encrypted files.
   std::string password;
 
diff --git a/third_party/zlib/google/zip_reader.cc b/third_party/zlib/google/zip_reader.cc
index 2cc101c7..2aa736a2 100644
--- a/third_party/zlib/google/zip_reader.cc
+++ b/third_party/zlib/google/zip_reader.cc
@@ -328,7 +328,21 @@
   DCHECK(!entry_.path.IsAbsolute()) << entry_.path;
 }
 
+void ZipReader::ReportProgress(ListenerCallback listener_callback,
+                               uint64_t bytes) const {
+  delta_bytes_read_ += bytes;
+
+  const base::TimeTicks now = base::TimeTicks::Now();
+  if (next_progress_report_time_ > now)
+    return;
+
+  next_progress_report_time_ = now + progress_period_;
+  listener_callback.Run(delta_bytes_read_);
+  delta_bytes_read_ = 0;
+}
+
 bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
+                                    ListenerCallback listener_callback,
                                     uint64_t num_bytes_to_extract) const {
   DCHECK(zip_file_);
   DCHECK_LT(0, next_index_);
@@ -369,6 +383,10 @@
       break;
     }
 
+    if (listener_callback) {
+      ReportProgress(listener_callback, num_bytes_read);
+    }
+
     DCHECK_LT(0, num_bytes_read);
     CHECK_LE(num_bytes_read, internal::kZipBufSize);
 
@@ -404,9 +422,26 @@
     delegate->OnError();
   }
 
+  if (listener_callback) {
+    listener_callback.Run(delta_bytes_read_);
+    delta_bytes_read_ = 0;
+  }
+
   return entire_file_extracted;
 }
 
+bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate,
+                                    uint64_t num_bytes_to_extract) const {
+  return ExtractCurrentEntry(delegate, ListenerCallback(),
+                             num_bytes_to_extract);
+}
+
+bool ZipReader::ExtractCurrentEntryWithListener(
+    WriterDelegate* delegate,
+    ListenerCallback listener_callback) const {
+  return ExtractCurrentEntry(delegate, listener_callback);
+}
+
 void ZipReader::ExtractCurrentEntryToFilePathAsync(
     const base::FilePath& output_file_path,
     SuccessCallback success_callback,
@@ -518,6 +553,7 @@
   next_index_ = 0;
   reached_end_ = true;
   ok_ = false;
+  delta_bytes_read_ = 0;
   entry_ = {};
 }
 
diff --git a/third_party/zlib/google/zip_reader.h b/third_party/zlib/google/zip_reader.h
index 703b74b..eb0a76a 100644
--- a/third_party/zlib/google/zip_reader.h
+++ b/third_party/zlib/google/zip_reader.h
@@ -85,6 +85,9 @@
   // A callback that is called periodically during the operation with the number
   // of bytes that have been processed so far.
   using ProgressCallback = base::RepeatingCallback<void(int64_t)>;
+  // A callback that is called periodically during the operation with the number
+  // of bytes that have been processed since the previous call (i.e. delta).
+  using ListenerCallback = base::RepeatingCallback<void(uint64_t)>;
 
   // Information of an entry (file or directory) in a ZIP archive.
   struct Entry {
@@ -205,6 +208,17 @@
                            uint64_t num_bytes_to_extract =
                                std::numeric_limits<uint64_t>::max()) const;
 
+  // Extracts the current entry to |delegate|, starting from the beginning
+  // of the entry, calling |listener_callback| regularly with the number of
+  // bytes extracted.
+  //
+  // Returns true if the entire file was extracted without error.
+  //
+  // Precondition: Next() returned a non-null Entry.
+  bool ExtractCurrentEntryWithListener(
+      WriterDelegate* delegate,
+      ListenerCallback listener_callback) const;
+
   // Asynchronously extracts the current entry to the given output file path. If
   // the current entry is a directory it just creates the directory
   // synchronously instead.
@@ -266,6 +280,21 @@
   // entry_.is_directory and entry_.is_unsafe.
   void Normalize(base::StringPiece16 in);
 
+  // Runs the ListenerCallback at a throttled rate.
+  void ReportProgress(ListenerCallback listener_callback, uint64_t bytes) const;
+
+  // Extracts |num_bytes_to_extract| bytes of the current entry to |delegate|,
+  // starting from the beginning of the entry calling |listener_callback| if
+  // its supplied.
+  //
+  // Returns true if the entire file was extracted without error.
+  //
+  // Precondition: Next() returned a non-null Entry.
+  bool ExtractCurrentEntry(WriterDelegate* delegate,
+                           ListenerCallback listener_callback,
+                           uint64_t num_bytes_to_extract =
+                               std::numeric_limits<uint64_t>::max()) const;
+
   // Extracts a chunk of the file to the target.  Will post a task for the next
   // chunk and success/failure/progress callbacks as necessary.
   void ExtractChunk(base::File target_file,
@@ -283,6 +312,16 @@
   bool ok_;
   Entry entry_;
 
+  // Next time to report progress.
+  mutable base::TimeTicks next_progress_report_time_ = base::TimeTicks::Now();
+
+  // Progress time delta.
+  // TODO(crbug.com/953256) Add this as parameter to the unzip options.
+  base::TimeDelta progress_period_ = base::Milliseconds(1000);
+
+  // Number of bytes read since last progress report callback executed.
+  mutable uint64_t delta_bytes_read_ = 0;
+
   base::WeakPtrFactory<ZipReader> weak_ptr_factory_{this};
 };
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 11279e1..595ef5b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -52117,7 +52117,7 @@
   <int value="3" label="Successfully stopped"/>
   <int value="4" label="Plugin crashed"/>
   <int value="5" label="Plugin hung"/>
-  <int value="6" label="Web kiosk with lacros started"/>
+  <int value="6" label="Web kiosk with lacros started (obsolete)"/>
   <int value="7" label="Restored session"/>
 </enum>
 
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index e73884a..ae12f8cc4 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -190,7 +190,7 @@
 </histogram>
 
 <histogram name="Accessibility.Android.OnDemand.OneHundredPercentEventsDropped"
-    units="count" expires_after="2022-11-06">
+    units="count" expires_after="2022-11-27">
   <owner>mschillaci@google.com</owner>
   <owner>abigailbklein@google.com</owner>
   <summary>
@@ -221,7 +221,7 @@
 </histogram>
 
 <histogram name="Accessibility.Android.OnDemand.PercentageDropped" units="%"
-    expires_after="2022-11-06">
+    expires_after="2022-11-27">
   <owner>mschillaci@google.com</owner>
   <owner>abigailbklein@google.com</owner>
   <summary>
@@ -1109,7 +1109,7 @@
 </histogram>
 
 <histogram name="Accessibility.LiveCaption" enum="BooleanEnabled"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>katie@chromium.org</owner>
   <owner>abigailbklein@google.com</owner>
   <owner>evliu@google.com</owner>
@@ -1180,7 +1180,7 @@
 </histogram>
 
 <histogram name="Accessibility.LiveCaption.FeatureEnabled"
-    enum="BooleanEnabled" expires_after="2022-09-25">
+    enum="BooleanEnabled" expires_after="2022-11-27">
   <owner>katie@chromium.org</owner>
   <owner>abigailbklein@google.com</owner>
   <owner>evliu@google.com</owner>
@@ -1220,7 +1220,7 @@
 </histogram>
 
 <histogram name="Accessibility.LiveCaption.LoadSodaResult"
-    enum="LoadSodaResult" expires_after="2022-09-25">
+    enum="LoadSodaResult" expires_after="2022-11-27">
   <owner>abigailbklein@google.com</owner>
   <owner>evliu@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
@@ -1231,7 +1231,7 @@
 </histogram>
 
 <histogram name="Accessibility.LiveCaption.Session"
-    enum="LiveCaptionSessionEvent" expires_after="2022-09-25">
+    enum="LiveCaptionSessionEvent" expires_after="2022-11-27">
   <owner>katie@chromium.org</owner>
   <owner>abigailbklein@google.com</owner>
   <owner>evliu@google.com</owner>
@@ -1743,7 +1743,7 @@
 </histogram>
 
 <histogram name="DomDistiller.ReaderMode.EntryPoint"
-    enum="ReaderModeEntryPoint" expires_after="2022-09-25">
+    enum="ReaderModeEntryPoint" expires_after="2022-11-27">
   <owner>katie@chromium.org</owner>
   <owner>gilmanmh@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index ec38f3a..b9bb8dca 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -1680,7 +1680,7 @@
 </histogram>
 
 <histogram name="Apps.AppListSearchResultOpenTypeV2" enum="AppListSearchResult"
-    expires_after="2022-09-01">
+    expires_after="2022-11-27">
 <!-- Name completed by histogram_suffixes name="TabletOrClamshellMode" -->
 
   <owner>tbarzic@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index d201779..6d53898 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -314,7 +314,7 @@
   </summary>
 </histogram>
 
-<histogram name="Arc.AppCount" units="units" expires_after="2022-04-24">
+<histogram name="Arc.AppCount" units="units" expires_after="2022-11-27">
   <owner>elijahtaylor@google.com</owner>
   <owner>fahdi@google.com</owner>
   <summary>
@@ -325,7 +325,7 @@
 </histogram>
 
 <histogram name="Arc.AppCount.HasInstalledOrUnknownApp" enum="Boolean"
-    expires_after="2022-09-23">
+    expires_after="2022-11-27">
   <owner>yusukes@google.com</owner>
   <owner>arcvm-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 6a0411a..d2e8890d 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -729,7 +729,7 @@
 
 <histogram name="Autofill.CardUnmaskAuthenticationSelectionDialog.Result"
     enum="AutofillCardUnmaskAuthenticationSelectionDialogResult"
-    expires_after="2022-07-17">
+    expires_after="2022-11-27">
   <owner>siyua@chromium.org</owner>
   <owner>jsaul@google.com</owner>
   <owner>siashah@chromium.org</owner>
@@ -742,7 +742,7 @@
 </histogram>
 
 <histogram name="Autofill.CardUnmaskAuthenticationSelectionDialog.Shown"
-    enum="BooleanShown" expires_after="2022-07-17">
+    enum="BooleanShown" expires_after="2022-11-27">
   <owner>siyua@chromium.org</owner>
   <owner>jsaul@google.com</owner>
   <owner>siashah@chromium.org</owner>
@@ -2465,7 +2465,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImport.PhoneNumberParsingResult"
-    enum="PhoneNumberImportParsingResult" expires_after="2022-08-31">
+    enum="PhoneNumberImportParsingResult" expires_after="2022-11-27">
   <owner>fleimgruber@google.com</owner>
   <owner>chrome-autofill-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index f689025..12081da 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -2589,7 +2589,7 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.FeaturePolicy.Header"
-    enum="FeaturePolicyFeature" expires_after="2022-08-21">
+    enum="FeaturePolicyFeature" expires_after="2022-11-27">
   <owner>iclelland@chromium.org</owner>
   <owner>feature-control@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
index ac989e85..e167cdfd 100644
--- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml
+++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -261,7 +261,7 @@
 
 <histogram
     name="Bluetooth.ChromeOS.FastPair.DeviceMetadataFetcher.Get.NetError"
-    enum="NetErrorCodes" expires_after="2022-09-20">
+    enum="NetErrorCodes" expires_after="2022-11-27">
   <owner>shanefitz@google.com</owner>
   <owner>julietlevesque@google.com</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
@@ -1024,7 +1024,7 @@
 </histogram>
 
 <histogram name="Bluetooth.ChromeOS.PoweredState" enum="BooleanEnabled"
-    expires_after="2022-11-20">
+    expires_after="2022-11-27">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/commerce/histograms.xml b/tools/metrics/histograms/metadata/commerce/histograms.xml
index 048eb04..f5b5c0b 100644
--- a/tools/metrics/histograms/metadata/commerce/histograms.xml
+++ b/tools/metrics/histograms/metadata/commerce/histograms.xml
@@ -496,7 +496,7 @@
 </histogram>
 
 <histogram name="MerchantTrust.MessageImpact.BrowsingTime" units="ms"
-    expires_after="2022-11-20">
+    expires_after="2022-11-27">
   <owner>zhiyuancai@chromium.org</owner>
   <owner>ayman@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
@@ -523,7 +523,7 @@
 </histogram>
 
 <histogram name="MerchantTrust.MessageImpact.NavigationCount"
-    units="navigations" expires_after="2022-11-20">
+    units="navigations" expires_after="2022-11-27">
   <owner>zhiyuancai@chromium.org</owner>
   <owner>ayman@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index d7dbfef..945b7583 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -943,7 +943,7 @@
 </histogram>
 
 <histogram name="Graphics.Smoothness.Checkerboarding" units="%"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>sadrul@chromium.org</owner>
   <owner>graphics-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cookie/histograms.xml b/tools/metrics/histograms/metadata/cookie/histograms.xml
index 601b2e4f..1b7ed99 100644
--- a/tools/metrics/histograms/metadata/cookie/histograms.xml
+++ b/tools/metrics/histograms/metadata/cookie/histograms.xml
@@ -853,7 +853,7 @@
 </histogram>
 
 <histogram name="Cookie.TimeDatabaseMigrationToV18" units="ms"
-    expires_after="2022-09-01">
+    expires_after="2022-11-27">
   <owner>arichiv@chromium.org</owner>
   <owner>bingler@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cros_ml/histograms.xml b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
index b04c36c..f79f808 100644
--- a/tools/metrics/histograms/metadata/cros_ml/histograms.xml
+++ b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
@@ -34,7 +34,7 @@
 </histogram>
 
 <histogram name="MachineLearningService.CpuUsageMilliPercent"
-    units="1/1000ths of %" expires_after="2022-09-25">
+    units="1/1000ths of %" expires_after="2022-11-27">
   <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
index cf72dd3..58cff8e 100644
--- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -356,7 +356,7 @@
 </histogram>
 
 <histogram name="Cryptohome.Errors" enum="CryptohomeError"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>apronin@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>Cryptohome errors.</summary>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index 72470bf..398a225 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -53,7 +53,7 @@
 </histogram>
 
 <histogram name="CustomTabs.ClientAppId" enum="ClientAppId"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>yusufo@chromium.org</owner>
   <summary>
     Android: AppId declared by the launching application in EXTRA_APPLICATION_ID
diff --git a/tools/metrics/histograms/metadata/download/histograms.xml b/tools/metrics/histograms/metadata/download/histograms.xml
index ea23efb..7a8e039 100644
--- a/tools/metrics/histograms/metadata/download/histograms.xml
+++ b/tools/metrics/histograms/metadata/download/histograms.xml
@@ -191,7 +191,7 @@
 </histogram>
 
 <histogram base="true" name="Download.Counts" enum="DownloadCountType"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
 <!-- Name completed by histogram_suffixes name="DownloadSource" -->
 
   <owner>shaktisahu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/event/histograms.xml b/tools/metrics/histograms/metadata/event/histograms.xml
index 1ac4a99..2fd509d8 100644
--- a/tools/metrics/histograms/metadata/event/histograms.xml
+++ b/tools/metrics/histograms/metadata/event/histograms.xml
@@ -255,7 +255,7 @@
 </histogram>
 
 <histogram name="Event.Latency.EndToEnd.KeyPress" units="microseconds"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>flackr@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
@@ -536,7 +536,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollBegin.TimeToScrollUpdateSwapBegin2"
-    units="microseconds" expires_after="2022-09-25">
+    units="microseconds" expires_after="2022-11-27">
   <owner>nzolghadr@chromium.org</owner>
   <summary>
     Time between initial creation of a wheel/touch event and start of the frame
@@ -947,7 +947,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.JankyDuration" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>ddrone@google.com</owner>
   <owner>chrometto-team@google.com</owner>
   <summary>
@@ -968,7 +968,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.JankyEvents" units="counts"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>ddrone@google.com</owner>
   <owner>chrometto-team@google.com</owner>
   <summary>
@@ -1123,7 +1123,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.TotalDuration" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>ddrone@google.com</owner>
   <owner>chrometto-team@google.com</owner>
   <summary>
@@ -1133,7 +1133,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.TotalEvents" units="counts"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>ddrone@google.com</owner>
   <owner>chrometto-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index 8ad0bc5..5530598 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -3377,7 +3377,7 @@
 </histogram>
 
 <histogram name="Extensions.WebRequest.KeepaliveRequestState"
-    enum="ExtensionInProgressRequestState" expires_after="2022-09-25">
+    enum="ExtensionInProgressRequestState" expires_after="2022-11-27">
   <owner>yhirano@chromium.org</owner>
   <owner>kinuko@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml
index 31cde45b..ab3533cb 100644
--- a/tools/metrics/histograms/metadata/file/histograms.xml
+++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -646,7 +646,7 @@
 </histogram>
 
 <histogram name="FileBrowser.Recent.FilterByType"
-    enum="FileManagerRecentFilterType" expires_after="2022-07-20">
+    enum="FileManagerRecentFilterType" expires_after="2022-11-27">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/gcm/histograms.xml b/tools/metrics/histograms/metadata/gcm/histograms.xml
index 69bfd95..cd48daf2 100644
--- a/tools/metrics/histograms/metadata/gcm/histograms.xml
+++ b/tools/metrics/histograms/metadata/gcm/histograms.xml
@@ -306,7 +306,7 @@
 </histogram>
 
 <histogram name="GCM.RegistrationRequestStatus"
-    enum="GCMRegistrationRequestStatus" expires_after="2022-09-25">
+    enum="GCMRegistrationRequestStatus" expires_after="2022-11-27">
   <owner>peter@chromium.org</owner>
   <summary>
     Status code of the outcome of a GCM registration request. The Unknown error
diff --git a/tools/metrics/histograms/metadata/geolocation/histograms.xml b/tools/metrics/histograms/metadata/geolocation/histograms.xml
index f653bfd5..9d80bd7 100644
--- a/tools/metrics/histograms/metadata/geolocation/histograms.xml
+++ b/tools/metrics/histograms/metadata/geolocation/histograms.xml
@@ -105,14 +105,14 @@
 </histogram>
 
 <histogram name="Geolocation.NetworkLocationRequest.NetError"
-    enum="NetErrorCodes" expires_after="2022-09-25">
+    enum="NetErrorCodes" expires_after="2022-11-27">
   <owner>JamesHollyer@chromium.org</owner>
   <owner>device-dev@chromium.org</owner>
   <summary>Network errors in NetworkLocationRequest.</summary>
 </histogram>
 
 <histogram name="Geolocation.NetworkLocationRequest.ResponseCode"
-    enum="HttpResponseCode" expires_after="2022-09-25">
+    enum="HttpResponseCode" expires_after="2022-11-27">
   <owner>mattreynolds@chromium.org</owner>
   <owner>device-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index a89f5343..5be92fa 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -831,7 +831,7 @@
 </histogram>
 
 <histogram name="History.Clusters.Backend.QueryAnnotatedVisits.ThreadTime"
-    units="ms" expires_after="2022-09-25">
+    units="ms" expires_after="2022-11-27">
   <owner>sophiechang@chromium.org</owner>
   <owner>chrome-journeys@google.com</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -894,7 +894,7 @@
 </histogram>
 
 <histogram name="History.Clusters.KeywordCache.ThreadTime" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>tommycli@chromium.org</owner>
   <owner>chrome-journeys@google.com</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -907,7 +907,7 @@
 </histogram>
 
 <histogram name="History.Clusters.ProcessClustersDuration" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>olivierli@chromium.org</owner>
   <owner>tommycli@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -1572,6 +1572,30 @@
   </summary>
 </histogram>
 
+<histogram name="History.TopSites.SearchTermsExtractedCount" units="counts"
+    expires_after="2022-10-16">
+  <owner>mahmadi@chromium.org</owner>
+  <owner>chrome-omnibox-team@google.com</owner>
+  <summary>
+    The number of search terms extracted from the in-memory URLDatabase to
+    produce the most repeated search suggestions. Emitted on NTP load and on
+    omnibox popup shown on Android NTP when repeatable queries are requested to
+    be shown in the Most Visited tiles.
+  </summary>
+</histogram>
+
+<histogram name="History.TopSites.SearchTermsExtractionTime" units="ms"
+    expires_after="2022-10-16">
+  <owner>mahmadi@chromium.org</owner>
+  <owner>chrome-omnibox-team@google.com</owner>
+  <summary>
+    The length of time it takes to extract the most repeated search terms from
+    the in-memory URLDatabase and sort by frecency scores. Emitted on NTP load
+    and on omnibox popup shown on Android NTP when repeatable queries are
+    requested to be shown in the Most Visited tiles.
+  </summary>
+</histogram>
+
 <histogram name="History.TopSitesRecoveredPercentage" units="%"
     expires_after="M85">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index fb3e56b..d54f7b4 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -1213,7 +1213,7 @@
 </histogram>
 
 <histogram name="IOS.Process.ActivePrewarm" enum="Boolean"
-    expires_after="2022-09-12">
+    expires_after="2022-11-27">
   <owner>justincohen@google.com</owner>
   <owner>olivierrobin@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 11d9b43..838c9d5 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -1225,7 +1225,7 @@
 </histogram>
 
 <histogram name="Media.AudioCapturerDroppedData" units="%"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>olka@chromium.org</owner>
   <owner>gustaf@chromium.org</owner>
   <summary>
@@ -1490,7 +1490,7 @@
 </histogram>
 
 <histogram name="Media.AudioRendererAudioGlitches" enum="AudioGlitchResult"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>henrika@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -1533,7 +1533,7 @@
 </histogram>
 
 <histogram name="Media.AudioRendererMissedDeadline" units="%"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>dalecurtis@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -1647,7 +1647,7 @@
 </histogram>
 
 <histogram name="Media.BreakoutBox.Usage" enum="BreakoutBoxUsage"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>guidou@chromium.org</owner>
   <owner>hta@chromium.org</owner>
   <owner>tguilbert@chromium.org</owner>
@@ -2616,7 +2616,7 @@
 </histogram>
 
 <histogram name="Media.GlobalMediaControls.HasDefaultPresentationRequest"
-    enum="Boolean" expires_after="2022-09-25">
+    enum="Boolean" expires_after="2022-11-27">
   <owner>takumif@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <owner>media-dev@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index ffd3abce..816962a 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -2045,7 +2045,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Compression.SizeKb" units="KB"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>lizeb@chromium.org</owner>
   <owner>thiabaud@google.com</owner>
   <summary>
@@ -2118,7 +2118,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.DiskReadTime.5min" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2262,7 +2262,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.TotalSizeKb.5min" units="KB"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2548,7 +2548,7 @@
 </histogram>
 
 <histogram name="Memory.RenderProcessHost.Count.OriginAgentClusterOverhead"
-    units="processes" expires_after="2022-09-28">
+    units="processes" expires_after="2022-11-27">
   <owner>wjmaclean@chromium.org</owner>
   <owner>alexmos@chromium.org</owner>
   <owner>creis@chromium.org</owner>
@@ -2578,7 +2578,7 @@
 </histogram>
 
 <histogram name="Memory.RenderProcessHost.Percent.OriginAgentClusterOverhead"
-    units="%" expires_after="2022-09-28">
+    units="%" expires_after="2022-11-27">
   <owner>wjmaclean@chromium.org</owner>
   <owner>alexmos@chromium.org</owner>
   <owner>creis@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index 4ad88f3..2a69d98 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -771,7 +771,7 @@
 </histogram>
 
 <histogram name="Navigation.MainFrame.ThirdPartyCookieBlockingEnabled"
-    enum="ThirdPartyCookieBlockState" expires_after="2022-09-25">
+    enum="ThirdPartyCookieBlockState" expires_after="2022-11-27">
   <owner>dullweber@chromium.org</owner>
   <owner>feuunk@chromium.org</owner>
   <summary>
@@ -1519,7 +1519,7 @@
 
 <histogram
     name="Prerender.Experimental.Search.FirstCorrectPrerenderHintReceivedToRealSearchNavigationStartedDuration"
-    units="ms" expires_after="2022-09-01">
+    units="ms" expires_after="2022-11-27">
   <owner>lingqi@chromium.org</owner>
   <owner>chrome-prerendering@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 0015e0a79d..4216fc2 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -414,7 +414,7 @@
 </histogram>
 
 <histogram name="Net.Cors.AccessCheckResult.NotSecureRequestor"
-    enum="CorsAccessCheckResult" expires_after="2022-09-18">
+    enum="CorsAccessCheckResult" expires_after="2022-11-27">
   <owner>carlosil@chromium.org</owner>
   <owner>estark@chromium.org</owner>
   <summary>
@@ -520,7 +520,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Nsswitch.Compatible" enum="BooleanValid"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -532,7 +532,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Nsswitch.FileChange" enum="BooleanChanged"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -542,7 +542,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Nsswitch.HostsFound" enum="BooleanFound"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -564,7 +564,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Nsswitch.IncompatibleService"
-    enum="NsswitchService" expires_after="2022-09-25">
+    enum="NsswitchService" expires_after="2022-11-27">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -588,7 +588,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Nsswitch.Read" enum="BooleanReceived"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -600,7 +600,7 @@
 </histogram>
 
 <histogram name="Net.DNS.DnsConfig.Nsswitch.TooLarge" enum="BooleanExceeded"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>ericorth@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -3412,7 +3412,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.PendingStreamsWaitTime" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
@@ -3996,7 +3996,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.WriteError" enum="NetErrorCodes"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>dschinazi@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index 17a5657..382580b 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -2832,7 +2832,7 @@
 </histogram>
 
 <histogram name="NetworkService.CorsPreflightMethodAllowed"
-    enum="NetworkServiceCorsPreflightMethodAllowed" expires_after="2022-09-25">
+    enum="NetworkServiceCorsPreflightMethodAllowed" expires_after="2022-11-27">
   <owner>hiroshige@chromium.org</owner>
   <owner>toyoshim@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index d55618f..bb52880 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -172,7 +172,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Carts.DiscountConsentStatusAtLoad"
-    enum="CartDiscountConsentStatus" expires_after="2022-11-20">
+    enum="CartDiscountConsentStatus" expires_after="2022-11-27">
   <owner>yuezhanggg@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/omnibox/OWNERS b/tools/metrics/histograms/metadata/omnibox/OWNERS
index db72ff3..efb2c62 100644
--- a/tools/metrics/histograms/metadata/omnibox/OWNERS
+++ b/tools/metrics/histograms/metadata/omnibox/OWNERS
@@ -4,3 +4,4 @@
 # Use chromium-metrics-reviews@google.com as a backup.
 ender@google.com
 manukh@chromium.org
+toyoshim@chromium.org
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index db932b02..4b3327b 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -179,7 +179,7 @@
 </histogram>
 
 <histogram name="Omnibox.ClipboardSuggestionShownAge" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>gangwu@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <summary>
@@ -1339,7 +1339,7 @@
 
 <histogram
     name="Omnibox.SuggestionUsed.Search.NavigationToLargestContentfulPaint2"
-    units="ms" expires_after="2022-11-13">
+    units="ms" expires_after="2022-11-27">
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-prerendering@google.com</owner>
   <owner>chrome-omnibox-team@google.com</owner>
@@ -1421,7 +1421,7 @@
 
 <histogram
     name="Omnibox.SuggestionUsed.URL.NavigationToLargestContentfulPaint2"
-    units="ms" expires_after="2022-07-11">
+    units="ms" expires_after="2022-11-27">
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-prerendering@google.com</owner>
   <owner>chrome-omnibox-team@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index 1b50a48c4..ceb9094 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -144,7 +144,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.ClearFetchedHints.StoreAvailable"
-    enum="BooleanAvailable" expires_after="M106">
+    enum="BooleanAvailable" expires_after="2022-11-27">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -189,7 +189,7 @@
 
 <histogram name="OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult"
     enum="OptimizationGuideHintCacheLevelDBStoreLoadMetadataResult"
-    expires_after="M106">
+    expires_after="2022-11-27">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -199,7 +199,8 @@
 </histogram>
 
 <histogram name="OptimizationGuide.HintCacheLevelDBStore.Status"
-    enum="OptimizationGuideHintCacheLevelDBStoreStatus" expires_after="M106">
+    enum="OptimizationGuideHintCacheLevelDBStoreStatus"
+    expires_after="2022-11-27">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -705,7 +706,7 @@
 <histogram
     name="OptimizationGuide.PageContentAnnotationsService.ContentAnnotationsStorageStatus"
     enum="OptimizationGuidePageContentAnnotationsStorageStatus"
-    expires_after="2022-11-20">
+    expires_after="2022-11-27">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -1117,7 +1118,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelFetcher.GetModelsResponse.NetErrorCode"
-    enum="NetErrorCodes" expires_after="2022-11-20">
+    enum="NetErrorCodes" expires_after="2022-11-27">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1147,7 +1148,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelFetcher.GetModelsResponse.Status"
-    enum="HttpResponseCode" expires_after="2022-11-20">
+    enum="HttpResponseCode" expires_after="2022-11-27">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 0aff374..e6b4358 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -3910,7 +3910,7 @@
 </histogram>
 
 <histogram name="Conversions.TriggerQueueDelay" units="ms"
-    expires_after="2022-09-17">
+    expires_after="2022-11-27">
   <owner>apaseltiner@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
@@ -5605,7 +5605,7 @@
 </histogram>
 
 <histogram name="Feedback.Duration.FormOpenToSubmit" units="s"
-    expires_after="2022-08-21">
+    expires_after="2022-11-27">
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
   <summary>
@@ -6425,7 +6425,7 @@
   </summary>
 </histogram>
 
-<histogram name="Hyphenation.Open" units="ms" expires_after="2022-09-25">
+<histogram name="Hyphenation.Open" units="ms" expires_after="2022-11-27">
   <owner>kojii@chromium.org</owner>
   <owner>layout-dev@chromium.org</owner>
   <summary>The time it takes to open a hyphenation dictionary.</summary>
@@ -8629,7 +8629,7 @@
 </histogram>
 
 <histogram name="OAuth2Login.SessionRestore" enum="GaiaSessionRestoreOutcome"
-    expires_after="2022-08-01">
+    expires_after="2022-11-27">
   <owner>anastasiian@chromium.org</owner>
   <owner>sinhak@chromium.org</owner>
   <summary>Outcome of Chrome OS GAIA cookie session restore process.</summary>
@@ -11146,7 +11146,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.CanCheckUrl" enum="BooleanCanCheckUrl"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -12098,7 +12098,17 @@
   </summary>
 </histogram>
 
-<histogram name="SiteIsolatedCodeCache.JS.Hit" enum="Boolean"
+<histogram name="SiteIsolatedCodeCache.JS.FetchCodeCache" units="ms"
+    expires_after="2022-11-25">
+  <owner>yhirano@chromium.org</owner>
+  <owner>loading-dev@google.com</owner>
+  <summary>
+    The amount of time spent to fetch a code cache. Recorded only for a JS code
+    cache, and recorded only when a non-empty code cache is found.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolatedCodeCache.JS.Hit" units="Boolean"
     expires_after="2022-11-10">
   <owner>yhirano@chromium.org</owner>
   <owner>loading-dev@chromium.org</owner>
@@ -13494,7 +13504,7 @@
   </summary>
 </histogram>
 
-<histogram name="UI.DeviceScale" units="%" expires_after="2022-09-11">
+<histogram name="UI.DeviceScale" units="%" expires_after="2022-11-27">
   <owner>bsep@chromium.org</owner>
   <summary>
     The device scales available on the system at startup. A system may report
@@ -14450,7 +14460,7 @@
 </histogram>
 
 <histogram name="WebFont.DownloadTime.LoadError" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>kenjibaheux@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index fd3041b..ed0e3c7 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -857,7 +857,7 @@
 
 <histogram
     name="PageLoad.Clients.Prerender.LayoutInstability.MaxCumulativeShiftScore.SessionWindow.Gap1000ms.Max5000ms2{PrerenderTriggerType}"
-    units="ms" expires_after="2022-06-27">
+    units="ms" expires_after="2022-11-27">
   <owner>asamidoi@chromium.org</owner>
   <owner>src/content/browser/prerender/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index aed9775..31f5ef70 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -3392,7 +3392,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.PageInfoAction"
-    enum="PasswordProtectionWarningAction" expires_after="2022-08-15">
+    enum="PasswordProtectionWarningAction" expires_after="2022-11-27">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/payment/histograms.xml b/tools/metrics/histograms/metadata/payment/histograms.xml
index 0aabfb5..d747cde6 100644
--- a/tools/metrics/histograms/metadata/payment/histograms.xml
+++ b/tools/metrics/histograms/metadata/payment/histograms.xml
@@ -66,7 +66,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.EventResponse.CanMakePayment" enum="Boolean"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>rouslan@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
@@ -82,7 +82,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.EventResponse.IsReadyToPay" enum="Boolean"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>rouslan@chromium.org</owner>
   <owner>web-payments-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/permissions/histograms.xml b/tools/metrics/histograms/metadata/permissions/histograms.xml
index f6fa1a0..05ae7511 100644
--- a/tools/metrics/histograms/metadata/permissions/histograms.xml
+++ b/tools/metrics/histograms/metadata/permissions/histograms.xml
@@ -272,7 +272,7 @@
 </histogram>
 
 <histogram name="Permissions.CrowdDeny.PreloadData.VersionAtAbuseCheckTime"
-    units="date" expires_after="2022-08-21">
+    units="date" expires_after="2022-11-27">
   <owner>elklm@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
   <summary>
@@ -1028,7 +1028,7 @@
 </histogram>
 
 <histogram name="SiteEngagementService.EngagementType"
-    enum="SiteEngagementServiceEngagementType" expires_after="2022-09-25">
+    enum="SiteEngagementServiceEngagementType" expires_after="2022-11-27">
   <owner>calamity@chromium.org</owner>
   <owner>dominickn@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/platform/histograms.xml b/tools/metrics/histograms/metadata/platform/histograms.xml
index 24595242..92ca81f 100644
--- a/tools/metrics/histograms/metadata/platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/platform/histograms.xml
@@ -1247,7 +1247,7 @@
 </histogram>
 
 <histogram name="Platform.Trunks.TpmErrorCode" enum="TPMResponseCode"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>yich@google.com</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index 842a7e2..7dfed2ce 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -876,7 +876,7 @@
 </histogram>
 
 <histogram name="Power.CpuTimeSecondsPerProcessType" enum="ProcessType2"
-    expires_after="2022-11-20">
+    expires_after="2022-11-27">
   <owner>eseckler@chromium.org</owner>
   <owner>skyostil@chromium.org</owner>
   <owner>woa-performance@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/print/histograms.xml b/tools/metrics/histograms/metadata/print/histograms.xml
index 4c7deaf..d7ea69a9 100644
--- a/tools/metrics/histograms/metadata/print/histograms.xml
+++ b/tools/metrics/histograms/metadata/print/histograms.xml
@@ -283,7 +283,7 @@
 </histogram>
 
 <histogram name="PrintPreview.RendererError" enum="PrintPreviewFailureType"
-    expires_after="2022-09-11">
+    expires_after="2022-11-27">
   <owner>thestig@chromium.org</owner>
   <owner>dhoss@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/profile/histograms.xml b/tools/metrics/histograms/metadata/profile/histograms.xml
index ed82689..5231e90 100644
--- a/tools/metrics/histograms/metadata/profile/histograms.xml
+++ b/tools/metrics/histograms/metadata/profile/histograms.xml
@@ -221,7 +221,7 @@
 </histogram>
 
 <histogram name="Profile.Guest.TypeCreated" enum="GuestProfileCreatedType"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>dgn@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
@@ -723,7 +723,7 @@
 </histogram>
 
 <histogram name="ProfilePicker.FirstProfileTime.FirstWebContentsNonEmptyPaint"
-    units="ms" expires_after="2022-09-25">
+    units="ms" expires_after="2022-11-27">
   <owner>dgn@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/quota/histograms.xml b/tools/metrics/histograms/metadata/quota/histograms.xml
index 02cd1146..3bef5a2 100644
--- a/tools/metrics/histograms/metadata/quota/histograms.xml
+++ b/tools/metrics/histograms/metadata/quota/histograms.xml
@@ -150,7 +150,7 @@
 </histogram>
 
 <histogram name="Quota.PercentDiskAvailable" units="%"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
@@ -170,7 +170,7 @@
 </histogram>
 
 <histogram name="Quota.PercentUsedByOrigin" units="%"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>jarrydg@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index d89708ad..2b1cacbb 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -1301,7 +1301,7 @@
 
 <histogram
     name="SafeBrowsing.NavigationObserver.DroppedReferrerChainEntries.ClientRedirect"
-    units="units" expires_after="2022-09-25">
+    units="units" expires_after="2022-11-27">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1313,7 +1313,7 @@
 
 <histogram
     name="SafeBrowsing.NavigationObserver.DroppedReferrerChainEntries.RecentNavigation"
-    units="units" expires_after="2022-09-25">
+    units="units" expires_after="2022-11-27">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1347,7 +1347,7 @@
 
 <histogram
     name="SafeBrowsing.NavigationObserver.MissingInitiatorRenderFrameHostPortal"
-    enum="BooleanExists" expires_after="2022-07-23">
+    enum="BooleanExists" expires_after="2022-11-27">
   <owner>vollick@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>Logs the number of times we have a missing initiator RFH.</summary>
@@ -1699,7 +1699,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.GetToken.Time" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1732,7 +1732,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.IsLookupServiceAvailable"
-    enum="BooleanAvailable" expires_after="2022-09-25">
+    enum="BooleanAvailable" expires_after="2022-11-27">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1744,7 +1744,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.IsLookupSuccessful" enum="BooleanSuccess"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1781,7 +1781,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.LocalMatch.Result"
-    enum="SafeBrowsingAllowlistAsyncMatch" expires_after="2022-11-20">
+    enum="SafeBrowsingAllowlistAsyncMatch" expires_after="2022-11-27">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1938,7 +1938,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.ThreatInfoSize" units="verdicts"
-    expires_after="2022-08-21">
+    expires_after="2022-11-27">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -2092,7 +2092,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.Triggers.SuspiciousSite.Event"
-    enum="SuspiciousSiteTriggerEvent" expires_after="2022-09-11">
+    enum="SuspiciousSiteTriggerEvent" expires_after="2022-11-27">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -2102,7 +2102,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4Database.Size" units="KB"
-    expires_after="2022-11-20">
+    expires_after="2022-11-27">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index 8f4540c6..89d67897 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -483,7 +483,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.CacheDetectsPhishing"
-    enum="BooleanIsPhishing" expires_after="2022-09-17">
+    enum="BooleanIsPhishing" expires_after="2022-11-27">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -608,7 +608,7 @@
 </histogram>
 
 <histogram name="SBClientPhishing.MainFrameRemoteConnected"
-    enum="BooleanConnected" expires_after="2022-09-25">
+    enum="BooleanConnected" expires_after="2022-11-27">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index 20ca8d3..3557ec3 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -282,7 +282,7 @@
 </histogram>
 
 <histogram name="Security.PrivateNetworkAccess.CheckResult"
-    enum="PrivateNetworkAccessCheckResult" expires_after="M107">
+    enum="PrivateNetworkAccessCheckResult" expires_after="2022-11-27">
   <owner>titouan@chromium.org</owner>
   <owner>clamy@chromium.org</owner>
   <owner>mkwst@chromium.org</owner>
@@ -498,7 +498,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptOut.PopularSCTSkipped" enum="Boolean"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>cthomp@chromium.org</owner>
   <owner>nsatragno@chromium.org</owner>
   <owner>trusty-transport@chromium.org</owner>
@@ -549,7 +549,7 @@
 </histogram>
 
 <histogram name="Security.SecurityLevel.OnCommit" enum="SecurityLevel"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>cthomp@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
index ae873eb6..17cf760 100644
--- a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
@@ -90,7 +90,7 @@
 
 <histogram
     name="SegmentationPlatform.AdaptiveToolbar.SegmentSelection.Computed"
-    enum="AdaptiveToolbarButtonVariant" expires_after="2022-11-20">
+    enum="AdaptiveToolbarButtonVariant" expires_after="2022-11-27">
   <owner>shaktisahu@chromium.org</owner>
   <owner>chrome-segmentation-platform@google.com</owner>
   <summary>
@@ -431,7 +431,7 @@
 </histogram>
 
 <histogram name="SegmentationPlatform.SelectionFailedReason"
-    enum="SegmentationSelectionFailureReason" expires_after="2022-11-20">
+    enum="SegmentationSelectionFailureReason" expires_after="2022-11-27">
   <owner>ssid@chromium.org</owner>
   <owner>chrome-segmentation-platform@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/service/histograms.xml b/tools/metrics/histograms/metadata/service/histograms.xml
index 67759b6..4e49af61 100644
--- a/tools/metrics/histograms/metadata/service/histograms.xml
+++ b/tools/metrics/histograms/metadata/service/histograms.xml
@@ -69,7 +69,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.ActivateEventStatus"
-    enum="ServiceWorkerStatusCode" expires_after="2022-09-25">
+    enum="ServiceWorkerStatusCode" expires_after="2022-11-27">
   <owner>wanderview@chromium.org</owner>
   <owner>asamidoi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
@@ -167,7 +167,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.CacheStorageInstalledScript.Count" units="count"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>horo@chromium.org</owner>
   <owner>wanderview@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/settings/histograms.xml b/tools/metrics/histograms/metadata/settings/histograms.xml
index 38ac4a5..8c9ccfb 100644
--- a/tools/metrics/histograms/metadata/settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/settings/histograms.xml
@@ -275,7 +275,7 @@
 </histogram>
 
 <histogram name="Settings.PrivacySandbox.PrivacySandboxReferrer"
-    enum="PrivacySandboxReferrer" expires_after="2022-09-25">
+    enum="PrivacySandboxReferrer" expires_after="2022-11-27">
   <owner>andzaytsev@google.com</owner>
   <owner>harrisonsean@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
@@ -290,7 +290,7 @@
 </histogram>
 
 <histogram name="Settings.PrivacySandbox.StartupState"
-    enum="SettingsPrivacySandboxStartupStates" expires_after="2022-09-25">
+    enum="SettingsPrivacySandboxStartupStates" expires_after="2022-11-27">
   <owner>sauski@google.com</owner>
   <owner>harrisonsean@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/sharing/histograms.xml b/tools/metrics/histograms/metadata/sharing/histograms.xml
index 0be1597..19fe716f 100644
--- a/tools/metrics/histograms/metadata/sharing/histograms.xml
+++ b/tools/metrics/histograms/metadata/sharing/histograms.xml
@@ -521,7 +521,7 @@
 </histogram>
 
 <histogram name="Sharing.SharingHubAndroid.ThirdPartyAppUsage"
-    enum="SharingHubBottomRowIndex" expires_after="2022-08-21">
+    enum="SharingHubBottomRowIndex" expires_after="2022-11-27">
   <owner>sophey@chromium.org</owner>
   <owner>src/chrome/browser/share/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/startup/histograms.xml b/tools/metrics/histograms/metadata/startup/histograms.xml
index 176739f8..d764d31 100644
--- a/tools/metrics/histograms/metadata/startup/histograms.xml
+++ b/tools/metrics/histograms/metadata/startup/histograms.xml
@@ -90,7 +90,7 @@
 </histogram>
 
 <histogram name="Startup.Android.Cold.FirstSafeBrowsingResponseTime.Tabbed"
-    units="ms" expires_after="2022-09-25">
+    units="ms" expires_after="2022-11-27">
   <owner>pasko@chromium.org</owner>
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
@@ -102,7 +102,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.Cold.TimeToFirstContentfulPaint"
-    units="ms" expires_after="2022-09-25">
+    units="ms" expires_after="2022-11-27">
   <owner>pasko@chromium.org</owner>
   <owner>alexilin@chromium.org</owner>
   <summary>
@@ -115,7 +115,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.Cold.TimeToFirstNavigationCommit"
-    units="ms" expires_after="2022-09-25">
+    units="ms" expires_after="2022-11-27">
   <owner>pasko@chromium.org</owner>
   <owner>alexilin@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/storage/histograms.xml b/tools/metrics/histograms/metadata/storage/histograms.xml
index 2ab3088..cbd62fc 100644
--- a/tools/metrics/histograms/metadata/storage/histograms.xml
+++ b/tools/metrics/histograms/metadata/storage/histograms.xml
@@ -114,7 +114,7 @@
 </histogram>
 
 <histogram name="Clipboard.TimeIntervalBetweenCommitAndRead" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>dcheng@chromium.org</owner>
   <owner>src/ui/base/clipboard/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/subresource/histograms.xml b/tools/metrics/histograms/metadata/subresource/histograms.xml
index 459d032..857351c 100644
--- a/tools/metrics/histograms/metadata/subresource/histograms.xml
+++ b/tools/metrics/histograms/metadata/subresource/histograms.xml
@@ -413,7 +413,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.ActivationList"
-    enum="ActivationList" expires_after="2022-09-25">
+    enum="ActivationList" expires_after="2022-11-27">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -423,7 +423,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.ActivationState"
-    enum="SubresourceFilterActivationState" expires_after="2022-09-25">
+    enum="SubresourceFilterActivationState" expires_after="2022-11-27">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index a859fa5..a5a2300 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -880,7 +880,7 @@
 </histogram>
 
 <histogram name="Sync.PostedDataTypeCommitRequest" enum="SyncModelTypes"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>mastiz@chromium.org</owner>
   <owner>rushans@google.com</owner>
   <component>Services&gt;Sync</component>
@@ -894,7 +894,7 @@
 </histogram>
 
 <histogram name="Sync.PostedDataTypeGetUpdatesRequest" enum="SyncModelTypes"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>mastiz@chromium.org</owner>
   <owner>rushans@google.com</owner>
   <component>Services&gt;Sync</component>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index 4d85ae2..811a1752 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -489,7 +489,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.MemoryUsage.CompressedData.PerThumbnailKiB"
-    units="KB" expires_after="2022-09-18">
+    units="KB" expires_after="2022-11-27">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -853,7 +853,7 @@
 </histogram>
 
 <histogram name="TabGroups.UserGroupCountPerLoad" units="groups"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>connily@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -2241,7 +2241,7 @@
 </histogram>
 
 <histogram name="Tabs.TabCountInGroupPerLoad" units="tabs"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>connily@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -3176,7 +3176,7 @@
   </summary>
 </histogram>
 
-<histogram name="TabStrip.TimeToSwitch" units="ms" expires_after="2022-09-25">
+<histogram name="TabStrip.TimeToSwitch" units="ms" expires_after="2022-11-27">
   <owner>connily@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/translate/histograms.xml b/tools/metrics/histograms/metadata/translate/histograms.xml
index b04aec0..c7b81556a 100644
--- a/tools/metrics/histograms/metadata/translate/histograms.xml
+++ b/tools/metrics/histograms/metadata/translate/histograms.xml
@@ -449,7 +449,7 @@
 </histogram>
 
 <histogram name="Translate.PageLoad.FinalSourceLanguage"
-    enum="LocaleCodeISO639" expires_after="2022-09-11">
+    enum="LocaleCodeISO639" expires_after="2022-11-27">
   <owner>curranmax@google.com</owner>
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/uma/histograms.xml b/tools/metrics/histograms/metadata/uma/histograms.xml
index 8370b4061..0dddb2be 100644
--- a/tools/metrics/histograms/metadata/uma/histograms.xml
+++ b/tools/metrics/histograms/metadata/uma/histograms.xml
@@ -851,7 +851,7 @@
 </histogram>
 
 <histogram name="UMA.TruncatedEvents.UserAction" units="events"
-    expires_after="2022-11-20">
+    expires_after="2022-11-27">
   <owner>rkaplow@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/v8/histograms.xml b/tools/metrics/histograms/metadata/v8/histograms.xml
index 74a479fa..dbde1584 100644
--- a/tools/metrics/histograms/metadata/v8/histograms.xml
+++ b/tools/metrics/histograms/metadata/v8/histograms.xml
@@ -1895,7 +1895,7 @@
 </histogram>
 
 <histogram name="V8.WasmMemoryAllocationResult" enum="WasmAllocationResult"
-    expires_after="2022-09-01">
+    expires_after="2022-11-27">
   <owner>ecmziegler@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>ahaas@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/web_apk/histograms.xml b/tools/metrics/histograms/metadata/web_apk/histograms.xml
index b4c176d..787b1ef 100644
--- a/tools/metrics/histograms/metadata/web_apk/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_apk/histograms.xml
@@ -71,7 +71,7 @@
 </histogram>
 
 <histogram name="WebApk.Install.GooglePlayInstallResult"
-    enum="WebApkGooglePlayInstallResult" expires_after="2022-09-25">
+    enum="WebApkGooglePlayInstallResult" expires_after="2022-11-27">
   <owner>hartmanng@chromium.org</owner>
   <owner>
     src/chrome/android/java/src/org/chromium/chrome/browser/webapps/OWNERS
diff --git a/tools/metrics/histograms/metadata/web_audio/histograms.xml b/tools/metrics/histograms/metadata/web_audio/histograms.xml
index 5c5fb087..e216ef35 100644
--- a/tools/metrics/histograms/metadata/web_audio/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_audio/histograms.xml
@@ -161,7 +161,7 @@
 </histogram>
 
 <histogram name="WebAudio.AudioDestination.HardwareBufferSize" units="units"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>hongchan@chromium.org</owner>
   <owner>mjwilson@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/web_rtc/histograms.xml b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
index 4eae625..603e4c7 100644
--- a/tools/metrics/histograms/metadata/web_rtc/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
@@ -534,7 +534,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.DelayedPacketOutageEventMs" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>hlundin@chromium.org</owner>
   <summary>
     Measures the duration of each packet loss concealment (a.k.a. expand) event
@@ -799,7 +799,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.ExpandRatePercent" units="%"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>hlundin@chromium.org</owner>
   <summary>
     Measures the expand rate for an incoming WebRTC audio stream. The expand
@@ -1408,7 +1408,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.Duration.Network" units="microseconds"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>handellm@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
@@ -1424,7 +1424,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.Duration.Signaling" units="microseconds"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>handellm@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
@@ -1440,7 +1440,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.Duration.Worker" units="microseconds"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>handellm@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
@@ -1589,7 +1589,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.Latency.Worker" units="microseconds"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>handellm@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
@@ -1939,7 +1939,7 @@
 </histogram>
 
 <histogram name="WebRTC.ReceivedAudioTrackDuration" units="ms"
-    expires_after="2022-08-07">
+    expires_after="2022-11-27">
   <owner>perkj@chromium.org</owner>
   <summary>
     Durations of audio tracks received over a PeerConnection. The stopwatch
@@ -1949,7 +1949,7 @@
 </histogram>
 
 <histogram name="WebRTC.ReceivedVideoTrackDuration" units="ms"
-    expires_after="2022-08-07">
+    expires_after="2022-11-27">
   <owner>perkj@chromium.org</owner>
   <summary>
     Durations of video tracks received over a PeerConnection. The stopwatch
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index f0c956a0..22624ca 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -38,7 +38,7 @@
 </histogram>
 
 <histogram name="AppBanners.DismissEvent" enum="AppBannersDismissEvent"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>pjmclachlan@google.com</owner>
   <owner>pcovell@google.com</owner>
   <summary>
@@ -50,7 +50,7 @@
 </histogram>
 
 <histogram name="AppBanners.DisplayEvent" enum="AppBannersDisplayEvent"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>pjmclachlan@google.com</owner>
   <owner>pcovell@google.com</owner>
   <summary>
@@ -100,7 +100,7 @@
 </histogram>
 
 <histogram name="AppBanners.UserResponse" enum="AppBannersUserResponse"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>dominickn@chromium.org</owner>
   <owner>pjmclachlan@google.com</owner>
   <summary>
@@ -417,7 +417,7 @@
 </histogram>
 
 <histogram name="Webapp.Install.InstallEvent" enum="WebappInstallSource"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>dominickn@chromium.org</owner>
   <owner>loyso@chromium.org</owner>
   <owner>calamity@chromium.org</owner>
@@ -475,7 +475,7 @@
 </histogram>
 
 <histogram name="Webapp.InstallResult" enum="WebAppInstallResultCode"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
 <!-- Name completed by histogram_suffixes name="WebAppType" -->
 
   <owner>calamity@chromium.org</owner>
@@ -484,7 +484,7 @@
 </histogram>
 
 <histogram name="WebApp.Launcher.LaunchResult"
-    enum="WebAppLauncherLaunchResult" expires_after="2022-09-11">
+    enum="WebAppLauncherLaunchResult" expires_after="2022-11-27">
   <owner>davidbienvenu@chromium.org</owner>
   <owner>jessemckenna@google.com</owner>
   <summary>
@@ -525,7 +525,7 @@
 </histogram>
 
 <histogram name="WebApp.Preinstalled.AppDuplicationFixApplied" units="apps"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>alancutter@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
   <summary>
@@ -730,7 +730,7 @@
 </histogram>
 
 <histogram name="Webapp.SystemApps.FreshInstallDuration" units="ms"
-    expires_after="2022-09-25">
+    expires_after="2022-11-27">
   <owner>calamity@chromium.org</owner>
   <owner>ortuno@chromium.org</owner>
   <summary>
@@ -870,7 +870,7 @@
 </histogram>
 
 <histogram name="Webapp.WebAppUrlLoaderPrepareForLoadResult"
-    enum="WebAppUrlLoaderResult" expires_after="2022-09-25">
+    enum="WebAppUrlLoaderResult" expires_after="2022-11-27">
   <owner>qjw@chromium.org</owner>
   <owner>ortuno@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 866367a..a88ef7b 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -5430,6 +5430,9 @@
 </event>
 
 <event name="ContextualSearch">
+  <obsolete>
+    Deprecated as of 05/2022.
+  </obsolete>
   <owner>donnd@chromium.org</owner>
   <summary>
     Metrics related to a Contextual Search Tap event on a page, for use with
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 0b6af29e..b4647f2b 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -36,7 +36,6 @@
 components_perftests,csharrison@chromium.org,,,
 dawn_perf_tests,"enga@chromium.org, chrome-gpu-perf-owners@chromium.org",Internals>GPU>Dawn,https://dawn.googlesource.com/dawn/+/HEAD/src/tests/perf_tests/README.md,
 desktop_ui,"yuhengh@chromium.org, tluk@chromium.org, romanarora@chromium.org",UI>Browser,https://chromium.googlesource.com/chromium/src/+/main/docs/speed/benchmark/harnesses/desktop_ui.md,smoke_test
-dromaeo,"jbroman@chromium.org, yukishiino@chromium.org, haraken@chromium.org",Blink>Bindings,,all
 dummy_benchmark.noisy_benchmark_1,"johnchen@chromium.org, wenbinzhang@google.com",Test>Telemetry,,
 dummy_benchmark.stable_benchmark_1,"johnchen@chromium.org, wenbinzhang@google.com",Test>Telemetry,,
 jetstream,hablich@chromium.org,Blink>JavaScript,,all
diff --git a/tools/perf/benchmarks/dromaeo.py b/tools/perf/benchmarks/dromaeo.py
deleted file mode 100644
index f178ed0..0000000
--- a/tools/perf/benchmarks/dromaeo.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from benchmarks import press
-
-from telemetry import benchmark
-
-from page_sets import dromaeo_pages
-
-
-@benchmark.Info(component='Blink>Bindings',
-                emails=['jbroman@chromium.org',
-                         'yukishiino@chromium.org',
-                         'haraken@chromium.org'])
-# pylint: disable=protected-access
-class DromaeoBenchmark(press._PressBenchmark):
-
-  @classmethod
-  def Name(cls):
-    return 'dromaeo'
-
-  def CreateStorySet(self, options):
-    return dromaeo_pages.DromaeoStorySet()
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index dc84c80..4cd46c8 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -14,7 +14,7 @@
         },
         "mac": {
             "hash": "c596583f38124f15f97db8fef7b9a71dded377b5",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/992efc532f462258cc68585423ee612621f96754/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/feeb5c1043fc9fe022b6c720aae568fbac7b0f1a/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
diff --git a/tools/perf/core/shard_maps/android-pixel2-perf-calibration_map.json b/tools/perf/core/shard_maps/android-pixel2-perf-calibration_map.json
index 50465cde..908a786 100644
--- a/tools/perf/core/shard_maps/android-pixel2-perf-calibration_map.json
+++ b/tools/perf/core/shard_maps/android-pixel2-perf-calibration_map.json
@@ -152,9 +152,6 @@
             "blink_perf.webgl_fast_call": {
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
@@ -550,4 +547,4 @@
         "shard #40": 290,
         "shard #41": 280
     }
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android-pixel2-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel2-perf-pgo_map.json
index e7692e5..032a214 100644
--- a/tools/perf/core/shard_maps/android-pixel2-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/android-pixel2-perf-pgo_map.json
@@ -284,9 +284,6 @@
             "blink_perf.webgl_fast_call": {
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/android-pixel2-perf_map.json b/tools/perf/core/shard_maps/android-pixel2-perf_map.json
index e7692e5..032a214 100644
--- a/tools/perf/core/shard_maps/android-pixel2-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel2-perf_map.json
@@ -284,9 +284,6 @@
             "blink_perf.webgl_fast_call": {
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/android-pixel2_webview-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel2_webview-perf-pgo_map.json
index 9c61a1f..959eed5 100644
--- a/tools/perf/core/shard_maps/android-pixel2_webview-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/android-pixel2_webview-perf-pgo_map.json
@@ -57,19 +57,11 @@
             },
             "blink_perf.webaudio": {
                 "abridged": false
-            },
-            "dromaeo": {
-                "end": 3,
-                "abridged": false
             }
         }
     },
     "3": {
         "benchmarks": {
-            "dromaeo": {
-                "begin": 3,
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
@@ -331,4 +323,4 @@
         "shard #19": 1393.0,
         "shard #20": 1413.0
     }
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json b/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json
index 9c61a1f..959eed5 100644
--- a/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel2_webview-perf_map.json
@@ -57,19 +57,11 @@
             },
             "blink_perf.webaudio": {
                 "abridged": false
-            },
-            "dromaeo": {
-                "end": 3,
-                "abridged": false
             }
         }
     },
     "3": {
         "benchmarks": {
-            "dromaeo": {
-                "begin": 3,
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
@@ -331,4 +323,4 @@
         "shard #19": 1393.0,
         "shard #20": 1413.0
     }
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/android-pixel4-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel4-perf-pgo_map.json
index 9b5f5b84..951561fa6 100644
--- a/tools/perf/core/shard_maps/android-pixel4-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/android-pixel4-perf-pgo_map.json
@@ -94,10 +94,6 @@
             "blink_perf.webgl_fast_call": {
                 "abridged": false
             },
-            "dromaeo": {
-                "end": 1,
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false
             },
@@ -116,10 +112,6 @@
     },
     "4": {
         "benchmarks": {
-            "dromaeo": {
-                "begin": 1,
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/android-pixel4-perf_map.json b/tools/perf/core/shard_maps/android-pixel4-perf_map.json
index 9b5f5b84..951561fa6 100644
--- a/tools/perf/core/shard_maps/android-pixel4-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel4-perf_map.json
@@ -94,10 +94,6 @@
             "blink_perf.webgl_fast_call": {
                 "abridged": false
             },
-            "dromaeo": {
-                "end": 1,
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false
             },
@@ -116,10 +112,6 @@
     },
     "4": {
         "benchmarks": {
-            "dromaeo": {
-                "begin": 1,
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json b/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
index ac18cbb..5eff62b 100644
--- a/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
@@ -133,9 +133,6 @@
                 "begin": 2,
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/lacros-eve-perf_map.json b/tools/perf/core/shard_maps/lacros-eve-perf_map.json
index 5d2b5540..634695a 100644
--- a/tools/perf/core/shard_maps/lacros-eve-perf_map.json
+++ b/tools/perf/core/shard_maps/lacros-eve-perf_map.json
@@ -70,9 +70,6 @@
             "desktop_ui": {
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
@@ -213,4 +210,4 @@
         "shard #6": 1430,
         "shard #7": 1420
     }
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/linux-perf-calibration_map.json b/tools/perf/core/shard_maps/linux-perf-calibration_map.json
index 060e567..4a99d1c 100644
--- a/tools/perf/core/shard_maps/linux-perf-calibration_map.json
+++ b/tools/perf/core/shard_maps/linux-perf-calibration_map.json
@@ -226,9 +226,6 @@
                 "begin": 12,
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/linux-perf-pgo_map.json b/tools/perf/core/shard_maps/linux-perf-pgo_map.json
index 7fac07db..c81667b 100644
--- a/tools/perf/core/shard_maps/linux-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/linux-perf-pgo_map.json
@@ -192,10 +192,6 @@
             "desktop_ui": {
                 "abridged": false
             },
-            "dromaeo": {
-                "end": 2,
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false
             },
@@ -223,10 +219,6 @@
     },
     "5": {
         "benchmarks": {
-            "dromaeo": {
-                "begin": 2,
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/linux-perf_map.json b/tools/perf/core/shard_maps/linux-perf_map.json
index 7fac07db..c81667b 100644
--- a/tools/perf/core/shard_maps/linux-perf_map.json
+++ b/tools/perf/core/shard_maps/linux-perf_map.json
@@ -192,10 +192,6 @@
             "desktop_ui": {
                 "abridged": false
             },
-            "dromaeo": {
-                "end": 2,
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false
             },
@@ -223,10 +219,6 @@
     },
     "5": {
         "benchmarks": {
-            "dromaeo": {
-                "begin": 2,
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/mac-laptop_high_end-perf-pgo_map.json b/tools/perf/core/shard_maps/mac-laptop_high_end-perf-pgo_map.json
index 21e2067..c883ff6 100644
--- a/tools/perf/core/shard_maps/mac-laptop_high_end-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/mac-laptop_high_end-perf-pgo_map.json
@@ -151,9 +151,6 @@
                 "begin": 5,
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/mac-laptop_high_end-perf_map.json b/tools/perf/core/shard_maps/mac-laptop_high_end-perf_map.json
index 21e2067..c883ff6 100644
--- a/tools/perf/core/shard_maps/mac-laptop_high_end-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-laptop_high_end-perf_map.json
@@ -151,9 +151,6 @@
                 "begin": 5,
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/mac-laptop_low_end-perf-pgo_map.json b/tools/perf/core/shard_maps/mac-laptop_low_end-perf-pgo_map.json
index 5eae9d0..8bd9744 100644
--- a/tools/perf/core/shard_maps/mac-laptop_low_end-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/mac-laptop_low_end-perf-pgo_map.json
@@ -61,9 +61,6 @@
             "blink_perf.webaudio": {
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
@@ -418,4 +415,4 @@
         "shard #24": 1393.0,
         "shard #25": 1344.0
     }
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json
index 5eae9d0..8bd9744 100644
--- a/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-laptop_low_end-perf_map.json
@@ -61,9 +61,6 @@
             "blink_perf.webaudio": {
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
@@ -418,4 +415,4 @@
         "shard #24": 1393.0,
         "shard #25": 1344.0
     }
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json
index 21c2560..62181f8e 100644
--- a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf-pgo_map.json
@@ -126,9 +126,6 @@
             "desktop_ui": {
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json
index 21c2560..62181f8e 100644
--- a/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-m1_mini_2020-perf_map.json
@@ -126,9 +126,6 @@
             "desktop_ui": {
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel2-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel2-perf-pgo_timing.json
index c2e3abc..b51c0297 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel2-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel2-perf-pgo_timing.json
@@ -1396,22 +1396,6 @@
         "name": "blink_perf.webgl_fast_call/binding-draw-arrays.html"
     },
     {
-        "duration": "38.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "0.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "81.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "35.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "7.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel2-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel2-perf_timing.json
index c2e3abc..b51c0297 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel2-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel2-perf_timing.json
@@ -1396,22 +1396,6 @@
         "name": "blink_perf.webgl_fast_call/binding-draw-arrays.html"
     },
     {
-        "duration": "38.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "0.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "81.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "35.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "7.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf-pgo_timing.json
index 5be043dd..e56772c 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf-pgo_timing.json
@@ -1332,22 +1332,6 @@
         "name": "blink_perf.webaudio/panner-node.html"
     },
     {
-        "duration": "38.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "0.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "49.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "31.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "6.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf_timing.json
index 5be043dd..e56772c 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel2_webview-perf_timing.json
@@ -1332,22 +1332,6 @@
         "name": "blink_perf.webaudio/panner-node.html"
     },
     {
-        "duration": "38.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "0.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "49.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "31.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "6.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel4-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel4-perf-pgo_timing.json
index d38ad67b..adfe012 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel4-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel4-perf-pgo_timing.json
@@ -1344,22 +1344,6 @@
         "name": "blink_perf.webgl_fast_call/binding-draw-arrays.html"
     },
     {
-        "duration": "37.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "35.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "50.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "30.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "6.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel4-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel4-perf_timing.json
index d38ad67b..adfe012 100644
--- a/tools/perf/core/shard_maps/timing_data/android-pixel4-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android-pixel4-perf_timing.json
@@ -1344,22 +1344,6 @@
         "name": "blink_perf.webgl_fast_call/binding-draw-arrays.html"
     },
     {
-        "duration": "37.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "35.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "50.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "30.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "6.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/linux-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/linux-perf-pgo_timing.json
index 63b5b61..4ea7282 100644
--- a/tools/perf/core/shard_maps/timing_data/linux-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/linux-perf-pgo_timing.json
@@ -1396,22 +1396,6 @@
         "name": "desktop_ui/tab_search:top50:loading:2020"
     },
     {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "32.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "48.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "26.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "3.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/linux-perf_timing.json b/tools/perf/core/shard_maps/timing_data/linux-perf_timing.json
index 63b5b61..4ea7282 100644
--- a/tools/perf/core/shard_maps/timing_data/linux-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/linux-perf_timing.json
@@ -1396,22 +1396,6 @@
         "name": "desktop_ui/tab_search:top50:loading:2020"
     },
     {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "32.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "48.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "26.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "3.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf-pgo_timing.json
index f7e0584..f6766c3 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf-pgo_timing.json
@@ -1548,22 +1548,6 @@
         "name": "desktop_ui/tab_search:top50:loading:2020"
     },
     {
-        "duration": "37.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "35.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "48.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "28.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "6.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf_timing.json b/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf_timing.json
index f7e0584..f6766c3 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-laptop_high_end-perf_timing.json
@@ -1548,22 +1548,6 @@
         "name": "desktop_ui/tab_search:top50:loading:2020"
     },
     {
-        "duration": "37.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "35.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "48.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "28.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "6.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf-pgo_timing.json
index 189b0e0..446a3c3 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf-pgo_timing.json
@@ -1372,22 +1372,6 @@
         "name": "blink_perf.webaudio/panner-node.html"
     },
     {
-        "duration": "38.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "35.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "48.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "29.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "8.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf_timing.json b/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf_timing.json
index 189b0e0..446a3c3 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-laptop_low_end-perf_timing.json
@@ -1372,22 +1372,6 @@
         "name": "blink_perf.webaudio/panner-node.html"
     },
     {
-        "duration": "38.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "35.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "48.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "29.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "8.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf-pgo_timing.json
index 99d7883..4e83539 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf-pgo_timing.json
@@ -1484,22 +1484,6 @@
         "name": "desktop_ui/tab_search:top50:loading:2020"
     },
     {
-        "duration": "37.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "49.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "28.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "8.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf_timing.json b/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf_timing.json
index 99d7883..4e83539 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-m1_mini_2020-perf_timing.json
@@ -1484,22 +1484,6 @@
         "name": "desktop_ui/tab_search:top50:loading:2020"
     },
     {
-        "duration": "37.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "49.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "28.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "8.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/win-10-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/win-10-perf-pgo_timing.json
index 20b0061..b264fb8 100644
--- a/tools/perf/core/shard_maps/timing_data/win-10-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/win-10-perf-pgo_timing.json
@@ -1404,22 +1404,6 @@
         "name": "desktop_ui/tab_search:top50:loading:2020"
     },
     {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "49.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "29.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "11.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/win-10-perf_timing.json b/tools/perf/core/shard_maps/timing_data/win-10-perf_timing.json
index 20b0061..b264fb8 100644
--- a/tools/perf/core/shard_maps/timing_data/win-10-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/win-10-perf_timing.json
@@ -1404,22 +1404,6 @@
         "name": "desktop_ui/tab_search:top50:loading:2020"
     },
     {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "49.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "29.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "11.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf-pgo_timing.json b/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf-pgo_timing.json
index b00ea33..b259ab0 100644
--- a/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf-pgo_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf-pgo_timing.json
@@ -1716,22 +1716,6 @@
         "name": "desktop_ui/webui_tab_strip:top10:loading:2020"
     },
     {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "50.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "30.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "4.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json b/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json
index b00ea33..b259ab0 100644
--- a/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/win-10_laptop_low_end-perf_timing.json
@@ -1716,22 +1716,6 @@
         "name": "desktop_ui/webui_tab_strip:top10:loading:2020"
     },
     {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
-    },
-    {
-        "duration": "34.0",
-        "name": "dromaeo/http://dromaeo.com?dom-modify"
-    },
-    {
-        "duration": "50.0",
-        "name": "dromaeo/http://dromaeo.com?dom-query"
-    },
-    {
-        "duration": "30.0",
-        "name": "dromaeo/http://dromaeo.com?dom-traverse"
-    },
-    {
         "duration": "4.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
diff --git a/tools/perf/core/shard_maps/win-10-perf-pgo_map.json b/tools/perf/core/shard_maps/win-10-perf-pgo_map.json
index 7babce3..e7b6ecd651 100644
--- a/tools/perf/core/shard_maps/win-10-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/win-10-perf-pgo_map.json
@@ -212,9 +212,6 @@
                 "begin": 15,
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/win-10-perf_map.json b/tools/perf/core/shard_maps/win-10-perf_map.json
index 7babce3..e7b6ecd651 100644
--- a/tools/perf/core/shard_maps/win-10-perf_map.json
+++ b/tools/perf/core/shard_maps/win-10-perf_map.json
@@ -212,9 +212,6 @@
                 "begin": 15,
                 "abridged": false
             },
-            "dromaeo": {
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf-pgo_map.json b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf-pgo_map.json
index e926b690..b85f4e13 100644
--- a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf-pgo_map.json
@@ -145,10 +145,6 @@
                 "begin": 5,
                 "abridged": false
             },
-            "dromaeo": {
-                "end": 1,
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false
             }
@@ -156,10 +152,6 @@
     },
     "7": {
         "benchmarks": {
-            "dromaeo": {
-                "begin": 1,
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
index e926b690..b85f4e13 100644
--- a/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
+++ b/tools/perf/core/shard_maps/win-10_laptop_low_end-perf_map.json
@@ -145,10 +145,6 @@
                 "begin": 5,
                 "abridged": false
             },
-            "dromaeo": {
-                "end": 1,
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false
             }
@@ -156,10 +152,6 @@
     },
     "7": {
         "benchmarks": {
-            "dromaeo": {
-                "begin": 1,
-                "abridged": false
-            },
             "dummy_benchmark.noisy_benchmark_1": {
                 "abridged": false
             },
diff --git a/tools/perf/core/test_data/benchmarks_to_shard.json b/tools/perf/core/test_data/benchmarks_to_shard.json
index d68b9a4a..beece74 100644
--- a/tools/perf/core/test_data/benchmarks_to_shard.json
+++ b/tools/perf/core/test_data/benchmarks_to_shard.json
@@ -340,16 +340,6 @@
   {
     "repeat": 1,
     "stories": [
-      "http://dromaeo.com?dom-attr",
-      "http://dromaeo.com?dom-modify",
-      "http://dromaeo.com?dom-query",
-      "http://dromaeo.com?dom-traverse"
-    ],
-    "name": "dromaeo"
-  },
-  {
-    "repeat": 1,
-    "stories": [
       "dummy_page.html"
     ],
     "name": "dummy_benchmark.histogram_benchmark_1"
diff --git a/tools/perf/core/undocumented_benchmarks.py b/tools/perf/core/undocumented_benchmarks.py
index b2270df..5864ae8c 100644
--- a/tools/perf/core/undocumented_benchmarks.py
+++ b/tools/perf/core/undocumented_benchmarks.py
@@ -7,7 +7,6 @@
 # benchmarks should shrink overtime.
 UNDOCUMENTED_BENCHMARKS = {
     'components_perftests',
-    'dromaeo',
     'dummy_benchmark.noisy_benchmark_1',
     'dummy_benchmark.stable_benchmark_1',
     'jetstream',
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 299bf436..090a1fe 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -63,9 +63,6 @@
 crbug.com/1300680 [ android ] blink_perf.webcodecs/software-video-encoding.html [ Skip ]
 crbug.com/1301260 [ linux ] blink_perf.webcodecs/hardware-video-encoding.html [ Skip ]
 
-# Benchmark: dromaeo
-crbug.com/1050065 [ android-pixel-2 ] dromaeo/http://dromaeo.com?dom-modify [ Skip ]
-
 # Benchmark: jetstream
 crbug.com/1009843 [ android ] jetstream/JetStream [ Skip ]
 
diff --git a/tools/perf/generate_perf_sharding.py b/tools/perf/generate_perf_sharding.py
index 5cc4c0f9..59b3846 100755
--- a/tools/perf/generate_perf_sharding.py
+++ b/tools/perf/generate_perf_sharding.py
@@ -262,7 +262,7 @@
         if shard == 'extra_infos':
           break
         benchmarks = shard_map.get('benchmarks', dict())
-        for benchmark in benchmarks.keys():
+        for benchmark in list(benchmarks.keys()):
           if benchmark not in benchmarks_to_keep:
             del benchmarks[benchmark]
         executables = shard_map.get('executables', dict())
diff --git a/tools/perf/page_sets/data/dromaeo.cssqueryjquery_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.cssqueryjquery_000.wprgo.sha1
deleted file mode 100644
index 56665a9..0000000
--- a/tools/perf/page_sets/data/dromaeo.cssqueryjquery_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4a94eb6c006ddbec049bea88a2ccd10a807486db
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.domcoreattr_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.domcoreattr_000.wprgo.sha1
deleted file mode 100644
index f591f73..0000000
--- a/tools/perf/page_sets/data/dromaeo.domcoreattr_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-9239635b2df78af7b7f990a314993371671fa129
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.domcoremodify_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.domcoremodify_000.wprgo.sha1
deleted file mode 100644
index 61f1f37d..0000000
--- a/tools/perf/page_sets/data/dromaeo.domcoremodify_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b57d7cb34cb0a95e96c51a294fc0ff62836b12f9
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.domcorequery_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.domcorequery_000.wprgo.sha1
deleted file mode 100644
index e00bb3b..0000000
--- a/tools/perf/page_sets/data/dromaeo.domcorequery_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-43090871af82aff38fcfa71e39c13ce0691f6fc7
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.domcoretraverse_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.domcoretraverse_000.wprgo.sha1
deleted file mode 100644
index d769032..0000000
--- a/tools/perf/page_sets/data/dromaeo.domcoretraverse_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-dbd4dd2d6b354a9a1f689d58df32200e9d294821
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibattrjquery_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibattrjquery_000.wprgo.sha1
deleted file mode 100644
index 5a4e7096..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibattrjquery_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-9a987eb679ae3af873e56963b7e54aa9233e7ab7
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibattrprototype_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibattrprototype_000.wprgo.sha1
deleted file mode 100644
index de4dd9a..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibattrprototype_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-964ca09bba2de00ab1d7405bd148e0a6e7db0e21
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibeventjquery_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibeventjquery_000.wprgo.sha1
deleted file mode 100644
index e1fd632..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibeventjquery_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a63ae0919d69756a93e8b48d1c3540448d0732f5
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibeventprototype_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibeventprototype_000.wprgo.sha1
deleted file mode 100644
index 2f48683..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibeventprototype_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-15c3ddb626b922252f626c0879c9a650a94311db
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibmodifyjquery_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibmodifyjquery_000.wprgo.sha1
deleted file mode 100644
index 0e37046a..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibmodifyjquery_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e00c12a90cdd46eecad313aeeb0d0705db439d4b
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibmodifyprototype_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibmodifyprototype_000.wprgo.sha1
deleted file mode 100644
index c78fe60..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibmodifyprototype_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-039c6f0bac19b326daad4f770dad287ff8ce62f1
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibstylejquery_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibstylejquery_000.wprgo.sha1
deleted file mode 100644
index 9cb0927a..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibstylejquery_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-502cad7795944a4653d766867968d55e91630ea8
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibstyleprototype_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibstyleprototype_000.wprgo.sha1
deleted file mode 100644
index 050a28f2..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibstyleprototype_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2534bc295f3942d113a85eb83f157754809b0963
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibtraversejquery_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibtraversejquery_000.wprgo.sha1
deleted file mode 100644
index be1e747..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibtraversejquery_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a0326e149c0e1937f7d20f9b18b051d209981313
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.jslibtraverseprototype_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo.jslibtraverseprototype_000.wprgo.sha1
deleted file mode 100644
index 125e401..0000000
--- a/tools/perf/page_sets/data/dromaeo.jslibtraverseprototype_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-65144f4bc1aad8a24e627a6f3f66a3b76c44eb3c
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo.json b/tools/perf/page_sets/data/dromaeo.json
deleted file mode 100644
index 7a6b2e9..0000000
--- a/tools/perf/page_sets/data/dromaeo.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "archives": {
-        "http://dromaeo.com?dom-attr": {
-            "DEFAULT": "dromaeo_000.wprgo"
-        },
-        "http://dromaeo.com?dom-modify": {
-            "DEFAULT": "dromaeo_000.wprgo"
-        },
-        "http://dromaeo.com?dom-query": {
-            "DEFAULT": "dromaeo_000.wprgo"
-        },
-        "http://dromaeo.com?dom-traverse": {
-            "DEFAULT": "dromaeo_000.wprgo"
-        }
-    },
-    "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
-    "platform_specific": true
-}
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/dromaeo_000.wprgo.sha1 b/tools/perf/page_sets/data/dromaeo_000.wprgo.sha1
deleted file mode 100644
index 4525f7d..0000000
--- a/tools/perf/page_sets/data/dromaeo_000.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-286e419e1102290adfbc3c2efa4a2334de2c3870
\ No newline at end of file
diff --git a/tools/perf/page_sets/dromaeo_pages.py b/tools/perf/page_sets/dromaeo_pages.py
deleted file mode 100644
index 05a9102..0000000
--- a/tools/perf/page_sets/dromaeo_pages.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import json
-import math
-
-from telemetry import story
-
-from page_sets import press_story
-
-class DromaeoStory(press_story.PressStory):
-
-  def __init__(self, url, page_set):
-    self.URL = url
-    super(DromaeoStory, self).__init__(page_set)
-
-
-  def ExecuteTest(self, action_runner):
-    action_runner.WaitForJavaScriptCondition(
-        'window.document.getElementById("pause") &&' +
-        'window.document.getElementById("pause").value == "Run"',
-        timeout=120)
-
-    # Start spying on POST request that will report benchmark results, and
-    # intercept result data.
-    action_runner.ExecuteJavaScript("""
-        (function() {
-          var real_jquery_ajax_ = window.jQuery;
-          window.results_ = "";
-          window.jQuery.ajax = function(request) {
-            if (request.url == "store.php") {
-              window.results_ = decodeURIComponent(request.data);
-              window.results_ = window.results_.substring(
-                window.results_.indexOf("=") + 1,
-                window.results_.lastIndexOf("&"));
-              real_jquery_ajax_(request);
-            }
-          };
-        })();""")
-    # Starts benchmark.
-    action_runner.ExecuteJavaScript(
-        'window.document.getElementById("pause").click();')
-
-    action_runner.WaitForJavaScriptCondition('!!window.results_', timeout=600)
-
-
-  def ParseTestResults(self, action_runner):
-    score = json.loads(
-        action_runner.EvaluateJavaScript('window.results_ || "[]"'))
-
-    def Escape(k):
-      chars = [' ', '.', '-', '/', '(', ')', '*']
-      for c in chars:
-        k = k.replace(c, '_')
-      return k
-
-    def AggregateData(container, key, value):
-      if key not in container:
-        container[key] = {'count': 0, 'sum': 0}
-      container[key]['count'] += 1
-      container[key]['sum'] += math.log(value)
-
-    def AddResult(name, value):
-      self.AddMeasurement(Escape(name), 'unitless_biggerIsBetter', [value])
-
-    aggregated = {}
-    for data in score:
-      AddResult('%s/%s' % (data['collection'], data['name']),
-                data['mean'])
-
-      top_name = data['collection'].split('-', 1)[0]
-      AggregateData(aggregated, top_name, data['mean'])
-
-      collection_name = data['collection']
-      AggregateData(aggregated, collection_name, data['mean'])
-
-    for key, value in aggregated.items():
-      AddResult(key, math.exp(value['sum'] / value['count']))
-
-
-class DromaeoStorySet(story.StorySet):
-  def __init__(self):
-    super(DromaeoStorySet, self).__init__(
-        archive_data_file='../page_sets/data/dromaeo.json',
-        cloud_storage_bucket=story.PUBLIC_BUCKET)
-
-    for query_param in ['dom-attr', 'dom-modify', 'dom-query', 'dom-traverse']:
-      url = 'http://dromaeo.com?%s' % query_param
-      self.AddStory(DromaeoStory(url, self))
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 0de6e43..eec5729 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -940,9 +940,6 @@
 }
 
 void WaylandToplevelWindow::UpdateWindowMask() {
-  // TODO(crbug.com/1299315): Deprecate this and migrate to the rounded corner
-  // API.
-  UpdateWindowShape();
   std::vector<gfx::Rect> region{gfx::Rect({}, visual_size_px())};
   root_surface()->SetOpaqueRegion(opaque_region_px_.has_value()
                                       ? &*opaque_region_px_
@@ -951,20 +948,6 @@
                                                   : &*region.begin());
 }
 
-void WaylandToplevelWindow::UpdateWindowShape() {
-  // Create |window_shape_in_dips_| using the window mask of
-  // PlatformWindowDelegate otherwise resets it.
-  SkPath window_mask_in_pixels =
-      delegate()->GetWindowMaskForWindowShapeInPixels();
-  if (window_mask_in_pixels.isEmpty()) {
-    window_shape_in_dips_.reset();
-    return;
-  }
-  SkPath window_mask_in_dips =
-      wl::ConvertPathToDIP(window_mask_in_pixels, window_scale());
-  window_shape_in_dips_ = wl::CreateRectsFromSkPath(window_mask_in_dips);
-}
-
 bool WaylandToplevelWindow::GetTabletMode() {
   return connection()->GetTabletMode();
 }
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
index e91d638..b6008b3 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -139,8 +139,6 @@
   // Calls UpdateWindowShape, set_input_region and set_opaque_region for this
   // toplevel window.
   void UpdateWindowMask() override;
-  // Update the window shape using the window mask of PlatformWindowDelegate.
-  void UpdateWindowShape();
 
   // WmMoveLoopHandler:
   bool RunMoveLoop(const gfx::Vector2d& drag_offset) override;