diff --git a/DEPS b/DEPS
index 11f7120..7239f7f 100644
--- a/DEPS
+++ b/DEPS
@@ -304,15 +304,15 @@
   # 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': '8a2a2668ef385ac74692318969bf5d2da7989643',
+  'v8_revision': '388b4ff1055ca02b8d46e39b3abaac650890b342',
   # 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': '38a38b8debda3c470f0c6c71c7a1351d1e5b029b',
+  'angle_revision': 'e8b6cd21749527c4f9ae9ddb8c5b355bdc8a6222',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'f99e45c6e720e36fde440c0400e735a06d663890',
+  'swiftshader_revision': 'b1bf623ee5ad028ca9555738b7f4a0f20bed82b4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -955,7 +955,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'H_7edllEnqpVP0obbsAXdW94nq5VgUb9i7rP139b88gC',
+          'version': '32V52h3rlrpZqQ85cpCUJYyonyvMwplznzMVsLdWcosC',
       },
     ],
     'condition': 'checkout_android',
@@ -1198,7 +1198,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'bcb046f58350ee087cd392004bda421417913478',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8cdc635be8023cfcf2a9f5329dacd7d1e307169b',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1592,7 +1592,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'df569f9bea5e511f44440afe1370d8fa1e61c29e',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '3a01865035f177527f59b045ff52a485e2dc4284',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1762,7 +1762,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '65fdb80dfbef270fb5626525292a6d54857621e5',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '7baa63ff9c35f2f0d9845316031bb33b859cd216',
+    Var('webrtc_git') + '/src.git' + '@' + '6fbd123d3f8a3015d19ac1e18f0cf9b85eea9b99',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1835,7 +1835,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7939219a51dfc27dbd3a266a9c857735052535bf',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3d7e3cea41878a804d1989bf61a7e34d1f409194',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/apps/launcher.cc b/apps/launcher.cc
index 2cdd21d..a1b91dc 100644
--- a/apps/launcher.cc
+++ b/apps/launcher.cc
@@ -217,7 +217,7 @@
         std::make_unique<app_runtime::LaunchData>();
     launch_data->action_data = std::move(action_data_);
     if (!handler_id_.empty())
-      launch_data->id = std::make_unique<std::string>(handler_id_);
+      launch_data->id = handler_id_;
 
     AppRuntimeEventRouter::DispatchOnLaunchedEvent(
         context_, app, launch_source_, std::move(launch_data));
@@ -410,7 +410,7 @@
     std::unique_ptr<app_runtime::LaunchData> launch_data =
         std::make_unique<app_runtime::LaunchData>();
     if (!launch_id.empty())
-      launch_data->id = std::make_unique<std::string>(launch_id);
+      launch_data->id = launch_id;
     AppRuntimeEventRouter::DispatchOnLaunchedEvent(context, app, source,
                                                    std::move(launch_data));
     return;
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 456b900..ae274a4b 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -37,6 +37,8 @@
     "accelerators/accelerator_notifications.h",
     "accelerators/accelerator_table.cc",
     "accelerators/accelerator_table.h",
+    "accelerators/ash_accelerator_configuration.cc",
+    "accelerators/ash_accelerator_configuration.h",
     "accelerators/ash_focus_manager_factory.cc",
     "accelerators/ash_focus_manager_factory.h",
     "accelerators/debug_commands.cc",
@@ -2888,6 +2890,7 @@
     "system/network/wifi_toggle_notification_controller_unittest.cc",
     "system/night_light/night_light_controller_unittest.cc",
     "system/night_light/night_light_feature_pod_controller_unittest.cc",
+    "system/notification_center/notification_center_tray_unittest.cc",
     "system/overview/overview_button_tray_unittest.cc",
     "system/palette/mock_palette_tool_delegate.cc",
     "system/palette/mock_palette_tool_delegate.h",
diff --git a/ash/accelerators/ash_accelerator_configuration.cc b/ash/accelerators/ash_accelerator_configuration.cc
new file mode 100644
index 0000000..5d4ab57
--- /dev/null
+++ b/ash/accelerators/ash_accelerator_configuration.cc
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/accelerators/ash_accelerator_configuration.h"
+
+#include <vector>
+
+#include "ash/public/mojom/accelerator_info.mojom.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace ash {
+
+AshAcceleratorConfiguration::AshAcceleratorConfiguration()
+    : AcceleratorConfiguration(ash::mojom::AcceleratorSource::kAsh) {}
+AshAcceleratorConfiguration::~AshAcceleratorConfiguration() = default;
+
+// TODO(jimmyxgong): Implement all functions below as these are only stubs.
+const std::vector<AcceleratorInfo>&
+AshAcceleratorConfiguration::GetConfigForAction(AcceleratorAction actionId) {
+  return accelerator_infos_;
+}
+
+bool AshAcceleratorConfiguration::IsMutable() const {
+  return false;
+}
+
+AcceleratorConfigResult AshAcceleratorConfiguration::AddUserAccelerator(
+    AcceleratorAction action,
+    const ui::Accelerator& accelerator) {
+  return AcceleratorConfigResult::kActionLocked;
+}
+
+AcceleratorConfigResult AshAcceleratorConfiguration::RemoveAccelerator(
+    AcceleratorAction action,
+    const ui::Accelerator& accelerator) {
+  return AcceleratorConfigResult::kActionLocked;
+}
+
+AcceleratorConfigResult AshAcceleratorConfiguration::ReplaceAccelerator(
+    AcceleratorAction action,
+    const ui::Accelerator& old_acc,
+    const ui::Accelerator& new_acc) {
+  return AcceleratorConfigResult::kActionLocked;
+}
+
+AcceleratorConfigResult AshAcceleratorConfiguration::RestoreDefault(
+    AcceleratorAction action) {
+  return AcceleratorConfigResult::kActionLocked;
+}
+
+AcceleratorConfigResult AshAcceleratorConfiguration::RestoreAllDefaults() {
+  return AcceleratorConfigResult::kActionLocked;
+}
+
+}  // namespace ash
diff --git a/ash/accelerators/ash_accelerator_configuration.h b/ash/accelerators/ash_accelerator_configuration.h
new file mode 100644
index 0000000..89f9fdd
--- /dev/null
+++ b/ash/accelerators/ash_accelerator_configuration.h
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_ACCELERATORS_ASH_ACCELERATOR_CONFIGURATION_H_
+#define ASH_ACCELERATORS_ASH_ACCELERATOR_CONFIGURATION_H_
+
+#include <vector>
+
+#include "ash/ash_export.h"
+#include "ash/public/cpp/accelerator_configuration.h"
+#include "ash/public/mojom/accelerator_info.mojom.h"
+
+namespace ash {
+
+// Implementor of AcceleratorConfiguration for Ash accelerators.
+// This class exist as a way to provide access to view and modify Ash
+// accelerators.
+class ASH_EXPORT AshAcceleratorConfiguration : public AcceleratorConfiguration {
+ public:
+  AshAcceleratorConfiguration();
+  AshAcceleratorConfiguration(const AshAcceleratorConfiguration&) = delete;
+  AshAcceleratorConfiguration& operator=(const AshAcceleratorConfiguration&) =
+      delete;
+  ~AshAcceleratorConfiguration() override;
+
+  // AcceleratorConfiguration::
+  const std::vector<AcceleratorInfo>& GetConfigForAction(
+      AcceleratorAction actionId) override;
+  bool IsMutable() const override;
+  AcceleratorConfigResult AddUserAccelerator(
+      AcceleratorAction action,
+      const ui::Accelerator& accelerator) override;
+  AcceleratorConfigResult RemoveAccelerator(
+      AcceleratorAction action,
+      const ui::Accelerator& accelerator) override;
+  AcceleratorConfigResult ReplaceAccelerator(
+      AcceleratorAction action,
+      const ui::Accelerator& old_acc,
+      const ui::Accelerator& new_acc) override;
+  AcceleratorConfigResult RestoreDefault(AcceleratorAction action) override;
+  AcceleratorConfigResult RestoreAllDefaults() override;
+
+ private:
+  std::vector<AcceleratorInfo> accelerator_infos_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_ACCELERATORS_ASH_ACCELERATOR_CONFIGURATION_H_
diff --git a/ash/components/arc/mojom/bluetooth.mojom b/ash/components/arc/mojom/bluetooth.mojom
index 68e2e76b..7f3fc51 100644
--- a/ash/components/arc/mojom/bluetooth.mojom
+++ b/ash/components/arc/mojom/bluetooth.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 19
+// Next MinVersion: 20
 
 module arc.mojom;
 
@@ -153,7 +153,9 @@
 // https://source.android.com/devices/halref/structbtgatt__gatt__id__t.html
 struct BluetoothGattID {
   BluetoothUUID uuid;
-  uint8 inst_id;
+  uint8 deprecated_inst_id;
+  // Diverges from above spec since ARC++ uses 16-bit handles in this field.
+  [MinVersion=19] uint16 instance_id;
 };
 
 // Copy from Android API
@@ -228,7 +230,7 @@
 };
 
 struct BluetoothGattDBElement {
-  uint8 id;
+  uint8 deprecated_id;
   BluetoothUUID uuid;
   BluetoothGattDBAttributeType type;
   uint16 attribute_handle;
@@ -246,6 +248,9 @@
    * the characteristic.
    */
   uint8 properties;
+
+  // Attribute IDs are stored in this field; it must be 16-bits wide.
+  [MinVersion=19] uint16 element_id;
 };
 
 // Bluetooth SDP types
diff --git a/ash/components/hid_detection/bluetooth_hid_detector_impl.cc b/ash/components/hid_detection/bluetooth_hid_detector_impl.cc
index e711d45..39ede7a9 100644
--- a/ash/components/hid_detection/bluetooth_hid_detector_impl.cc
+++ b/ash/components/hid_detection/bluetooth_hid_detector_impl.cc
@@ -354,6 +354,10 @@
   HID_LOG(EVENT) << "Pairing with device with id: "
                  << current_pairing_device_.value()->id;
   ++num_pairing_attempts_;
+  current_pairing_timer_.Start(
+      FROM_HERE, kMaxPairingSessionDuration,
+      base::BindOnce(&BluetoothHidDetectorImpl::ClearCurrentPairingState,
+                     weak_ptr_factory_.GetWeakPtr()));
   device_pairing_handler_remote_->PairDevice(
       current_pairing_device_.value()->id,
       device_pairing_delegate_receiver_.BindNewPipeAndPassRemote(),
@@ -364,7 +368,7 @@
 }
 
 void BluetoothHidDetectorImpl::OnPairDevice(
-    std::unique_ptr<base::ElapsedTimer> pairing_timer,
+    std::unique_ptr<base::ElapsedTimer> metrics_timer,
     chromeos::bluetooth_config::mojom::PairingResult pairing_result) {
   DCHECK(current_pairing_device_)
       << "OnPairDevice() called with no |current_pairing_device_|";
@@ -378,7 +382,7 @@
       pairing_result ==
       chromeos::bluetooth_config::mojom::PairingResult::kSuccess;
   hid_detection::RecordBluetoothPairingResult(success,
-                                              pairing_timer->Elapsed());
+                                              metrics_timer->Elapsed());
 
   // If pairing has succeeded, wait for SetInputDevicesStatus() to be called
   // with the corresponding HID type no longer missing.
@@ -395,7 +399,8 @@
 
 void BluetoothHidDetectorImpl::ClearCurrentPairingState() {
   // If there is an ongoing pairing, it will be cancelled. Invalidate the
-  // pairing finished callback.
+  // pairing finished callback. This will also invalidate the
+  // |current_pairing_timer_| callback.
   weak_ptr_factory_.InvalidateWeakPtrs();
 
   queued_device_ids_.erase(current_pairing_device_.value()->id);
@@ -417,6 +422,7 @@
   // Reset queue-related properties.
   current_pairing_device_.reset();
   current_pairing_state_.reset();
+  current_pairing_timer_.Stop();
   queue_ = std::make_unique<base::queue<
       chromeos::bluetooth_config::mojom::BluetoothDevicePropertiesPtr>>();
   queued_device_ids_.clear();
diff --git a/ash/components/hid_detection/bluetooth_hid_detector_impl.h b/ash/components/hid_detection/bluetooth_hid_detector_impl.h
index d7358018..c70ab979 100644
--- a/ash/components/hid_detection/bluetooth_hid_detector_impl.h
+++ b/ash/components/hid_detection/bluetooth_hid_detector_impl.h
@@ -11,6 +11,7 @@
 #include "base/containers/queue.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/elapsed_timer.h"
+#include "base/timer/timer.h"
 #include "chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -33,6 +34,11 @@
   const BluetoothHidDetectionStatus GetBluetoothHidDetectionStatus() override;
 
  private:
+  friend class BluetoothHidDetectorImplTest;
+
+  static constexpr base::TimeDelta kMaxPairingSessionDuration =
+      base::Seconds(33);
+
   // States used for internal state machine.
   enum State {
     // HID detection is currently not active.
@@ -134,6 +140,11 @@
   // code that should be displayed to the user for them to enter into the HID.
   absl::optional<BluetoothHidPairingState> current_pairing_state_;
 
+  // A timer started when the current pairing begins. If the pairing session
+  // finishes, the timer's callback is invalidated. If the timer exceeds
+  // kMaxPairingDuration, the current pairing session will be canceled.
+  base::OneShotTimer current_pairing_timer_;
+
   InputDevicesStatus input_devices_status_;
   State state_ = kNotStarted;
 
diff --git a/ash/components/hid_detection/bluetooth_hid_detector_impl_unittest.cc b/ash/components/hid_detection/bluetooth_hid_detector_impl_unittest.cc
index 5fb166e..c5583bc8 100644
--- a/ash/components/hid_detection/bluetooth_hid_detector_impl_unittest.cc
+++ b/ash/components/hid_detection/bluetooth_hid_detector_impl_unittest.cc
@@ -36,7 +36,6 @@
 
 const char kTestPinCode[] = "123456";
 const uint32_t kTestPasskey = 123456;
-const base::TimeDelta kTestDuration = base::Milliseconds(3000);
 
 class FakeBluetoothHidDetectorDelegate : public BluetoothHidDetector::Delegate {
  public:
@@ -164,18 +163,17 @@
       const std::string& device_id,
       FakeDevicePairingHandler* device_pairing_handler,
       absl::optional<device::ConnectionFailureReason> failure_reason) {
-    // Mock time passing to measure pairing duration.
-    task_environment_.FastForwardBy(kTestDuration);
-
-    unpaired_devices_.erase(
-        std::remove_if(unpaired_devices_.begin(), unpaired_devices_.end(),
-                       [device_id](BluetoothDevicePropertiesPtr const& device) {
-                         return device->id == device_id;
-                       }),
-        unpaired_devices_.end());
-
-    UpdateDiscoveredDevicesProviderDevices();
-    base::RunLoop().RunUntilIdle();
+    if (!failure_reason) {
+      unpaired_devices_.erase(
+          std::remove_if(
+              unpaired_devices_.begin(), unpaired_devices_.end(),
+              [device_id](BluetoothDevicePropertiesPtr const& device) {
+                return device->id == device_id;
+              }),
+          unpaired_devices_.end());
+      UpdateDiscoveredDevicesProviderDevices();
+      base::RunLoop().RunUntilIdle();
+    }
 
     device_pairing_handler->SimulatePairDeviceFinished(failure_reason);
     EXPECT_TRUE(device_pairing_handler->current_pairing_device_id().empty());
@@ -212,11 +210,13 @@
     }
   }
 
-  void AssertBluetoothPairingResult(bool success, int count) {
+  void AssertBluetoothPairingResult(bool success,
+                                    int count,
+                                    base::TimeDelta duration) {
     histogram_tester_.ExpectTimeBucketCount(
         base::StrCat({"OOBE.HidDetectionScreen.BluetoothPairing.Duration.",
                       success ? "Success" : "Failure"}),
-        kTestDuration, count);
+        duration, count);
     histogram_tester_.ExpectBucketCount(
         "OOBE.HidDetectionScreen.BluetoothPairing.Result", success, count);
   }
@@ -230,6 +230,14 @@
         "OOBE.HidDetectionScreen.BluetoothPairingAttempts", total_count);
   }
 
+  void FastForward(base::TimeDelta time) {
+    task_environment_.FastForwardBy(time);
+  }
+
+  base::TimeDelta GetMaxPairingSessionDuration() {
+    return bluetooth_hid_detector_->kMaxPairingSessionDuration;
+  }
+
  private:
   void UpdateDiscoveredDevicesProviderDevices() {
     std::vector<BluetoothDevicePropertiesPtr> unpaired_devices;
@@ -525,7 +533,11 @@
   AssertBluetoothHidDetectionStatus(
       BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
       /*pairing_state=*/absl::nullopt);
-  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  // Mock time passing to measure pairing duration.
+  FastForward(GetMaxPairingSessionDuration() / 2);
 
   // Mock |device_id1| being paired. BluetoothHidDetectorImpl should not inform
   // the delegate or move to the next device in queue until the input devices
@@ -547,7 +559,8 @@
   AssertBluetoothHidDetectionStatus(
       /*current_pairing_device=*/absl::nullopt,
       /*pairing_state=*/absl::nullopt);
-  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1,
+                               GetMaxPairingSessionDuration() / 2);
 
   std::string device_id2;
   AddUnpairedDevice(&device_id2, DeviceType::kKeyboard);
@@ -586,7 +599,11 @@
   std::string device_id2;
   AddUnpairedDevice(&device_id2, DeviceType::kKeyboardMouseCombo);
   EXPECT_EQ(1u, delegate->num_bluetooth_hid_status_changed_calls());
-  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  // Mock time passing to measure pairing duration.
+  FastForward(GetMaxPairingSessionDuration() / 2);
 
   // Mock |device_id1| being paired. BluetoothHidDetectorImpl should not inform
   // the delegate or move to the next device in queue until the input devices
@@ -605,11 +622,15 @@
   EXPECT_EQ(device_id2,
             GetDevicePairingHandlers()[0]->current_pairing_device_id());
   EXPECT_EQ(3u, delegate->num_bluetooth_hid_status_changed_calls());
-  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1,
+                               GetMaxPairingSessionDuration() / 2);
   AssertBluetoothHidDetectionStatus(
       BluetoothHidMetadata(device_id2, BluetoothHidType::kKeyboardPointerCombo),
       /*pairing_state=*/absl::nullopt);
 
+  // Mock time passing to measure pairing duration.
+  FastForward(GetMaxPairingSessionDuration() / 2);
+
   // Mock |device_id2| being registered as connected. Two devices should be
   // paired successfully.
   MockPairDeviceFinished(device_id2, GetDevicePairingHandlers()[0],
@@ -625,7 +646,8 @@
   AssertBluetoothHidDetectionStatus(
       /*current_pairing_device=*/absl::nullopt,
       /*pairing_state=*/absl::nullopt);
-  AssertBluetoothPairingResult(/*success=*/true, /*count=*/2);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/2,
+                               GetMaxPairingSessionDuration() / 2);
 
   StopBluetoothHidDetection(/*is_using_bluetooth=*/false);
   AssertBluetoothPairingAttemptsCount(/*bucket=*/2, /*count=*/1,
@@ -653,7 +675,11 @@
   AssertBluetoothHidDetectionStatus(
       BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
       /*pairing_state=*/absl::nullopt);
-  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  // Mock time passing to measure pairing duration.
+  FastForward(GetMaxPairingSessionDuration() / 2);
 
   // Mock |device_id1| being paired. BluetoothHidDetectorImpl should not inform
   // the delegate or move to the next device in queue until the input devices
@@ -675,8 +701,13 @@
   AssertBluetoothHidDetectionStatus(
       BluetoothHidMetadata(device_id3, BluetoothHidType::kKeyboard),
       /*pairing_state=*/absl::nullopt);
-  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1);
-  AssertBluetoothPairingResult(/*success=*/false, /*count=*/0);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1,
+                               GetMaxPairingSessionDuration() / 2);
+  AssertBluetoothPairingResult(/*success=*/false, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  // Mock time passing to measure pairing duration.
+  FastForward(GetMaxPairingSessionDuration() / 2);
 
   // Mock |device_id3| pairing failing. BluetoothHidDetectorImpl should move to
   // the next device in the queue immediately.
@@ -686,7 +717,8 @@
   AssertBluetoothHidDetectionStatus(
       /*current_pairing_device=*/absl::nullopt,
       /*pairing_state=*/absl::nullopt);
-  AssertBluetoothPairingResult(/*success=*/false, /*count=*/1);
+  AssertBluetoothPairingResult(/*success=*/false, /*count=*/1,
+                               GetMaxPairingSessionDuration() / 2);
 
   StopBluetoothHidDetection(/*is_using_bluetooth=*/false);
   AssertBluetoothPairingAttemptsCount(/*bucket=*/2, /*count=*/1,
@@ -1108,7 +1140,11 @@
         BluetoothHidPairingState(kTestPinCode, num_keys_entered));
   }
   EXPECT_EQ(8u, delegate->num_bluetooth_hid_status_changed_calls());
-  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  // Mock time passing to measure pairing duration.
+  FastForward(GetMaxPairingSessionDuration() / 2);
 
   // Mock |device_id1| being paired. BluetoothHidDetectorImpl should not inform
   // the delegate or move to the next device in queue until the input devices
@@ -1130,7 +1166,8 @@
   AssertBluetoothHidDetectionStatus(
       BluetoothHidMetadata(device_id2, BluetoothHidType::kPointer),
       /*pairing_state=*/absl::nullopt);
-  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1,
+                               GetMaxPairingSessionDuration() / 2);
 
   // Simulate "DisplayPasskey" authorization required.
   GetDevicePairingHandlers()[0]->SimulateDisplayPasskey(kTestPasskey);
@@ -1155,7 +1192,11 @@
         BluetoothHidPairingState(kTestPinCode, num_keys_entered));
   }
   EXPECT_EQ(17u, delegate->num_bluetooth_hid_status_changed_calls());
-  AssertBluetoothPairingResult(/*success=*/false, /*count=*/0);
+  AssertBluetoothPairingResult(/*success=*/false, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  // Mock time passing to measure pairing duration.
+  FastForward(GetMaxPairingSessionDuration() / 2);
 
   // Mock |device_id2| pairing failing.
   MockPairDeviceFinished(device_id2, GetDevicePairingHandlers()[0],
@@ -1163,11 +1204,247 @@
   EXPECT_EQ(18u, delegate->num_bluetooth_hid_status_changed_calls());
   AssertBluetoothHidDetectionStatus(/*current_pairing_device=*/absl::nullopt,
                                     /*pairing_state=*/absl::nullopt);
-  AssertBluetoothPairingResult(/*success=*/false, /*count=*/1);
+  AssertBluetoothPairingResult(/*success=*/false, /*count=*/1,
+                               GetMaxPairingSessionDuration() / 2);
 
   StopBluetoothHidDetection(/*is_using_bluetooth=*/false);
   AssertBluetoothPairingAttemptsCount(/*bucket=*/2, /*count=*/1,
                                       /*total_count=*/1);
 }
 
+TEST_F(BluetoothHidDetectorImplTest, PairingTimesOut) {
+  std::string device_id1;
+  AddUnpairedDevice(&device_id1, DeviceType::kMouse);
+
+  std::string device_id2;
+  AddUnpairedDevice(&device_id2, DeviceType::kKeyboard);
+
+  // Begin HID detection. |device_id1| should be attempted to be paired with.
+  FakeBluetoothHidDetectorDelegate* delegate = StartBluetoothHidDetection();
+  EXPECT_TRUE(IsDiscoverySessionActive());
+  EXPECT_EQ(1u, GetDevicePairingHandlers().size());
+  EXPECT_EQ(device_id1,
+            GetDevicePairingHandlers()[0]->current_pairing_device_id());
+  EXPECT_EQ(1u, delegate->num_bluetooth_hid_status_changed_calls());
+
+  // Simulate "DisplayPasskey" authorization required.
+  GetDevicePairingHandlers()[0]->SimulateDisplayPasskey(kTestPasskey);
+  EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      BluetoothHidPairingState(kTestPinCode, /*num_keys_entered=*/0u));
+
+  // Fast forward past the pairing timeout period. The pairing state should
+  // reset and the second device should be pairing.
+  FastForward(GetMaxPairingSessionDuration());
+  EXPECT_EQ(device_id2,
+            GetDevicePairingHandlers()[0]->current_pairing_device_id());
+  EXPECT_EQ(4u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id2, BluetoothHidType::kKeyboard),
+      /*pairing_state=*/absl::nullopt);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  FastForward(GetMaxPairingSessionDuration() / 2);
+  MockPairDeviceFinished(device_id2, GetDevicePairingHandlers()[0],
+                         /*failure_reason=*/absl::nullopt);
+  EXPECT_EQ(4u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1,
+                               GetMaxPairingSessionDuration() / 2);
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id2, BluetoothHidType::kKeyboard),
+      /*pairing_state=*/absl::nullopt);
+
+  // Fast forward past the pairing timeout period. This should cancel the
+  // current pairing, even if it was a success, because SetInputDevicesStatus()
+  // hadn't been called. The pairing state should move on to pairing the
+  // |device_id1| again.
+  FastForward(GetMaxPairingSessionDuration() / 2);
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      /*pairing_state=*/absl::nullopt);
+  EXPECT_EQ(6u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1,
+                               GetMaxPairingSessionDuration() / 2);
+}
+
+TEST_F(BluetoothHidDetectorImplTest, TimeoutTimerCancelledOnFailure) {
+  std::string device_id1;
+  AddUnpairedDevice(&device_id1, DeviceType::kMouse);
+
+  // Begin HID detection. |device_id1| should be attempted to be paired with.
+  FakeBluetoothHidDetectorDelegate* delegate = StartBluetoothHidDetection();
+  EXPECT_TRUE(IsDiscoverySessionActive());
+  EXPECT_EQ(1u, GetDevicePairingHandlers().size());
+  EXPECT_EQ(1u, delegate->num_bluetooth_hid_status_changed_calls());
+  EXPECT_EQ(device_id1,
+            GetDevicePairingHandlers()[0]->current_pairing_device_id());
+
+  // Simulate "DisplayPasskey" authorization required.
+  GetDevicePairingHandlers()[0]->SimulateDisplayPasskey(kTestPasskey);
+  EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      BluetoothHidPairingState(kTestPinCode, /*num_keys_entered=*/0u));
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  FastForward(GetMaxPairingSessionDuration() / 2);
+  EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      BluetoothHidPairingState(kTestPinCode, /*num_keys_entered=*/0u));
+
+  // Mock |device_id1| as failed pairing.
+  MockPairDeviceFinished(device_id1, GetDevicePairingHandlers()[0],
+                         device::ConnectionFailureReason::kAuthFailed);
+  EXPECT_EQ(3u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+  AssertBluetoothHidDetectionStatus(
+      /*current_pairing_device=*/absl::nullopt,
+      /*pairing_state=*/absl::nullopt);
+
+  // Advance the rest of the timeout duration. If the timer from the last
+  // pairing was not cancelled, this should cause a crash.
+  FastForward(GetMaxPairingSessionDuration() / 2);
+  EXPECT_EQ(3u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      /*current_pairing_device=*/absl::nullopt,
+      /*pairing_state=*/absl::nullopt);
+}
+
+TEST_F(BluetoothHidDetectorImplTest, TimeoutTimerCancelledOnSuccess) {
+  std::string device_id1;
+  AddUnpairedDevice(&device_id1, DeviceType::kMouse);
+
+  // Begin HID detection. |device_id1| should be attempted to be paired with.
+  FakeBluetoothHidDetectorDelegate* delegate = StartBluetoothHidDetection();
+  EXPECT_TRUE(IsDiscoverySessionActive());
+  EXPECT_EQ(1u, GetDevicePairingHandlers().size());
+  EXPECT_EQ(1u, delegate->num_bluetooth_hid_status_changed_calls());
+  EXPECT_EQ(device_id1,
+            GetDevicePairingHandlers()[0]->current_pairing_device_id());
+
+  // Simulate "DisplayPasskey" authorization required.
+  GetDevicePairingHandlers()[0]->SimulateDisplayPasskey(kTestPasskey);
+  EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      BluetoothHidPairingState(kTestPinCode, /*num_keys_entered=*/0u));
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  FastForward(GetMaxPairingSessionDuration() / 2);
+  EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      BluetoothHidPairingState(kTestPinCode, /*num_keys_entered=*/0u));
+
+  // Mock |device_id1| being paired. BluetoothHidDetectorImpl should not inform
+  // the delegate or move to the next device in queue until the input devices
+  // status has been updated.
+  MockPairDeviceFinished(device_id1, GetDevicePairingHandlers()[0],
+                         /*failure_reason=*/absl::nullopt);
+  EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      BluetoothHidPairingState(kTestPinCode, /*num_keys_entered=*/0u));
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/1,
+                               GetMaxPairingSessionDuration() / 2);
+  SetInputDevicesStatus(
+      {.pointer_is_missing = false, .keyboard_is_missing = true});
+  EXPECT_EQ(3u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      /*current_pairing_device=*/absl::nullopt,
+      /*pairing_state=*/absl::nullopt);
+
+  // Advance the rest of the timeout duration. If the timer from the last
+  // pairing was not cancelled, this should cause a crash.
+  FastForward(GetMaxPairingSessionDuration() / 2);
+  EXPECT_EQ(3u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      /*current_pairing_device=*/absl::nullopt,
+      /*pairing_state=*/absl::nullopt);
+}
+
+TEST_F(BluetoothHidDetectorImplTest,
+       TimeoutTimerCancelledOnHidDetectionStopped) {
+  std::string device_id1;
+  AddUnpairedDevice(&device_id1, DeviceType::kMouse);
+
+  // Begin HID detection. |device_id1| should be attempted to be paired with.
+  FakeBluetoothHidDetectorDelegate* delegate1 = StartBluetoothHidDetection();
+  EXPECT_TRUE(IsDiscoverySessionActive());
+  EXPECT_EQ(1u, GetDevicePairingHandlers().size());
+  EXPECT_EQ(1u, delegate1->num_bluetooth_hid_status_changed_calls());
+  EXPECT_EQ(device_id1,
+            GetDevicePairingHandlers()[0]->current_pairing_device_id());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      /*pairing_state=*/absl::nullopt);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+
+  FastForward(GetMaxPairingSessionDuration() / 2);
+  EXPECT_EQ(1u, delegate1->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      /*pairing_state=*/absl::nullopt);
+
+  StopBluetoothHidDetection(/*is_using_bluetooth=*/false);
+  EXPECT_FALSE(IsDiscoverySessionActive());
+  EXPECT_EQ(2u, delegate1->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      /*current_pairing_device=*/absl::nullopt,
+      /*pairing_state=*/absl::nullopt);
+
+  // Advance the rest of the timeout duration. If the timer from the previous
+  // pairing was not cancelled, this should cause a crash.
+  FastForward(GetMaxPairingSessionDuration() / 2);
+  EXPECT_EQ(2u, delegate1->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      /*current_pairing_device=*/absl::nullopt,
+      /*pairing_state=*/absl::nullopt);
+}
+
+TEST_F(BluetoothHidDetectorImplTest, TimeoutTimerCancelledOnBluetoothDisabled) {
+  std::string device_id1;
+  AddUnpairedDevice(&device_id1, DeviceType::kMouse);
+
+  // Begin HID detection. |device_id1| should be attempted to be paired with.
+  FakeBluetoothHidDetectorDelegate* delegate = StartBluetoothHidDetection();
+  EXPECT_TRUE(IsDiscoverySessionActive());
+  EXPECT_EQ(1u, GetDevicePairingHandlers().size());
+  EXPECT_EQ(1u, delegate->num_bluetooth_hid_status_changed_calls());
+  EXPECT_EQ(device_id1,
+            GetDevicePairingHandlers()[0]->current_pairing_device_id());
+  AssertBluetoothHidDetectionStatus(
+      BluetoothHidMetadata(device_id1, BluetoothHidType::kPointer),
+      /*pairing_state=*/absl::nullopt);
+  AssertBluetoothPairingResult(/*success=*/true, /*count=*/0,
+                               GetMaxPairingSessionDuration() / 2);
+  FastForward(GetMaxPairingSessionDuration() / 2);
+
+  // Mock the adapter disabling. The timeout should be stopped.
+  SetAdapterState(BluetoothSystemState::kDisabled);
+  EXPECT_FALSE(IsDiscoverySessionActive());
+  AssertBluetoothHidDetectionStatus(
+      /*current_pairing_device=*/absl::nullopt,
+      /*pairing_state=*/absl::nullopt);
+  EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
+
+  // Advance the rest of the timeout duration. If the timer from the previous
+  // pairing was not cancelled, this should cause a crash.
+  FastForward(GetMaxPairingSessionDuration() / 2);
+  EXPECT_EQ(2u, delegate->num_bluetooth_hid_status_changed_calls());
+  AssertBluetoothHidDetectionStatus(
+      /*current_pairing_device=*/absl::nullopt,
+      /*pairing_state=*/absl::nullopt);
+
+  // HID detection must be stopped before BluetoothHidDetectorImpl is destroyed.
+  StopBluetoothHidDetection(/*is_using_bluetooth=*/false);
+}
+
 }  // namespace ash::hid_detection
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 92c4b06..c0b0596 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -958,6 +958,11 @@
 const base::Feature kImeStylusHandwriting{"StylusHandwriting",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether to hide voice button in IME tray if accessibility mic icon
+// is already shown in the shelf.
+const base::Feature kImeTrayHideVoiceButton{"ImeTrayHideVoiceButton",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables improved keyboard shortcuts for activating desks at specified indices
 // and toggling whether a window is assigned to all desks.
 const base::Feature kImprovedDesksKeyboardShortcuts{
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 911f0306..03e6336 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -388,6 +388,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kVirtualKeyboardNewHeader;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kImeTrayHideVoiceButton;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kImeOptionsInSettings;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kImeRuleConfig;
 COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/glanceables/glanceables_controller.cc b/ash/glanceables/glanceables_controller.cc
index 6242f29..c2492868 100644
--- a/ash/glanceables/glanceables_controller.cc
+++ b/ash/glanceables/glanceables_controller.cc
@@ -60,6 +60,7 @@
 }
 
 void GlanceablesController::ShowOnLogin() {
+  show_session_restore_ = true;
   CreateUi();
   FetchData();
 }
@@ -67,6 +68,7 @@
 void GlanceablesController::ShowFromOverview() {
   // Hide any open windows.
   window_hider_ = std::make_unique<GlanceablesWindowHider>();
+  show_session_restore_ = false;
   CreateUi();
   FetchData();
 }
@@ -93,7 +95,8 @@
   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
   widget_->Init(std::move(params));
 
-  view_ = widget_->SetContentsView(std::make_unique<GlanceablesView>());
+  view_ = widget_->SetContentsView(
+      std::make_unique<GlanceablesView>(show_session_restore_));
 
   ApplyBackdrop();
   widget_->Show();
diff --git a/ash/glanceables/glanceables_controller.h b/ash/glanceables/glanceables_controller.h
index b13dbe8a..bb287e0 100644
--- a/ash/glanceables/glanceables_controller.h
+++ b/ash/glanceables/glanceables_controller.h
@@ -71,6 +71,7 @@
   std::unique_ptr<GlanceablesDelegate> delegate_;
   std::unique_ptr<views::Widget> widget_;
   GlanceablesView* view_ = nullptr;
+  bool show_session_restore_ = true;
 
   // Hides windows while glanceables are showing.
   std::unique_ptr<GlanceablesWindowHider> window_hider_;
diff --git a/ash/glanceables/glanceables_unittests.cc b/ash/glanceables/glanceables_unittests.cc
index 17f8a75..dd224c6 100644
--- a/ash/glanceables/glanceables_unittests.cc
+++ b/ash/glanceables/glanceables_unittests.cc
@@ -125,6 +125,10 @@
     return controller_->view_->up_next_view_;
   }
 
+  views::Label* GetRestoreSessionLabel() {
+    return controller_->view_->restore_session_label_;
+  }
+
   GlanceablesRestoreView* GetRestoreView() {
     return controller_->view_->restore_view_;
   }
@@ -172,9 +176,25 @@
   EXPECT_TRUE(GetWeatherIcon());
   EXPECT_TRUE(GetWeatherTemperature());
   EXPECT_TRUE(GetUpNextView());
+  EXPECT_TRUE(GetRestoreSessionLabel());
   EXPECT_TRUE(GetRestoreView());
 }
 
+TEST_F(GlanceablesTest, ShowFromOverviewDoesNotCreateRestoreViews) {
+  controller_->ShowFromOverview();
+
+  GlanceablesView* view = GetGlanceablesView();
+  ASSERT_TRUE(view);
+  EXPECT_TRUE(GetWelcomeLabel());
+  EXPECT_TRUE(GetWeatherIcon());
+  EXPECT_TRUE(GetWeatherTemperature());
+  EXPECT_TRUE(GetUpNextView());
+
+  // Session restore views are skipped.
+  EXPECT_FALSE(GetRestoreSessionLabel());
+  EXPECT_FALSE(GetRestoreView());
+}
+
 TEST_F(GlanceablesTest, WeatherViewShowsWeather) {
   controller_->CreateUi();
 
diff --git a/ash/glanceables/glanceables_view.cc b/ash/glanceables/glanceables_view.cc
index 6dce4a6..22b007d 100644
--- a/ash/glanceables/glanceables_view.cc
+++ b/ash/glanceables/glanceables_view.cc
@@ -36,7 +36,7 @@
 
 }  // namespace
 
-GlanceablesView::GlanceablesView() {
+GlanceablesView::GlanceablesView(bool show_session_restore) {
   // Inside border insets are set in OnBoundsChanged() when this view is added
   // to the widget.
   layout_ = SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -71,15 +71,17 @@
       views::BoxLayout::Orientation::kVertical, gfx::Insets(),
       /*between_child_spacing=*/32));
 
-  // The "Restore last session" label.
-  restore_session_label_ =
-      right_column->AddChildView(std::make_unique<views::Label>());
-  SetupSectionLabel(restore_session_label_);
-  restore_session_label_->SetText(
-      l10n_util::GetStringUTF16(IDS_GLANCEABLES_RESTORE_SESSION));
+  if (show_session_restore) {
+    // The "Restore last session" label.
+    restore_session_label_ =
+        right_column->AddChildView(std::make_unique<views::Label>());
+    SetupSectionLabel(restore_session_label_);
+    restore_session_label_->SetText(
+        l10n_util::GetStringUTF16(IDS_GLANCEABLES_RESTORE_SESSION));
 
-  restore_view_ =
-      right_column->AddChildView(std::make_unique<GlanceablesRestoreView>());
+    restore_view_ =
+        right_column->AddChildView(std::make_unique<GlanceablesRestoreView>());
+  }
 
   // Share space equally between the two columns.
   container_layout->SetFlexForView(left_column, 1);
@@ -102,7 +104,8 @@
   views::View::OnThemeChanged();
   // TODO(crbug.com/1353119): Use color provider.
   up_next_label_->SetEnabledColor(SK_ColorWHITE);
-  restore_session_label_->SetEnabledColor(SK_ColorWHITE);
+  if (restore_session_label_)
+    restore_session_label_->SetEnabledColor(SK_ColorWHITE);
 }
 
 }  // namespace ash
diff --git a/ash/glanceables/glanceables_view.h b/ash/glanceables/glanceables_view.h
index 9ea58a1..172afb25 100644
--- a/ash/glanceables/glanceables_view.h
+++ b/ash/glanceables/glanceables_view.h
@@ -23,7 +23,9 @@
 // Container view for the "welcome back" glanceables screen shown on login.
 class ASH_EXPORT GlanceablesView : public views::View {
  public:
-  GlanceablesView();
+  // `show_session_restore` controls whether the session restore views are
+  // created.
+  explicit GlanceablesView(bool show_session_restore);
   GlanceablesView(const GlanceablesView&) = delete;
   GlanceablesView& operator=(const GlanceablesView&) = delete;
   ~GlanceablesView() override;
diff --git a/ash/public/cpp/session/session_controller_client.h b/ash/public/cpp/session/session_controller_client.h
index 1979b19..3a4386b5 100644
--- a/ash/public/cpp/session/session_controller_client.h
+++ b/ash/public/cpp/session/session_controller_client.h
@@ -26,6 +26,9 @@
   // Requests signing out all users, ending the current session.
   virtual void RequestSignOut() = 0;
 
+  // Requests to restart the system for OS update.
+  virtual void RequestRestartForUpdate() = 0;
+
   // Attempts to restart the chrome browser.
   virtual void AttemptRestartChrome() = 0;
 
diff --git a/ash/public/cpp/system_tray_client.h b/ash/public/cpp/system_tray_client.h
index 3f3eca5..f3f433c 100644
--- a/ash/public/cpp/system_tray_client.h
+++ b/ash/public/cpp/system_tray_client.h
@@ -136,9 +136,6 @@
   // Shows the Firmware update app.
   virtual void ShowFirmwareUpdate() = 0;
 
-  // Attempts to restart the system for update.
-  virtual void RequestRestartForUpdate() = 0;
-
   // Sets the UI locale to |locale_iso_code| and exit the session to take
   // effect.
   virtual void SetLocaleAndExit(const std::string& locale_iso_code) = 0;
diff --git a/ash/public/cpp/test/test_system_tray_client.cc b/ash/public/cpp/test/test_system_tray_client.cc
index 4b49157..c383c3b 100644
--- a/ash/public/cpp/test/test_system_tray_client.cc
+++ b/ash/public/cpp/test/test_system_tray_client.cc
@@ -108,8 +108,6 @@
   show_firmware_update_count_++;
 }
 
-void TestSystemTrayClient::RequestRestartForUpdate() {}
-
 void TestSystemTrayClient::SetLocaleAndExit(
     const std::string& locale_iso_code) {}
 
diff --git a/ash/public/cpp/test/test_system_tray_client.h b/ash/public/cpp/test/test_system_tray_client.h
index ed0fba8..a439a042 100644
--- a/ash/public/cpp/test/test_system_tray_client.h
+++ b/ash/public/cpp/test/test_system_tray_client.h
@@ -59,7 +59,6 @@
   void ShowNetworkSettings(const std::string& network_id) override;
   void ShowMultiDeviceSetup() override;
   void ShowFirmwareUpdate() override;
-  void RequestRestartForUpdate() override;
   void SetLocaleAndExit(const std::string& locale_iso_code) override;
   void ShowAccessCodeCastingDialog(
       AccessCodeCastDialogOpenLocation open_location) override;
diff --git a/ash/session/session_controller_impl.cc b/ash/session/session_controller_impl.cc
index 51e063d0..26efbff9 100644
--- a/ash/session/session_controller_impl.cc
+++ b/ash/session/session_controller_impl.cc
@@ -237,6 +237,11 @@
     client_->RequestSignOut();
 }
 
+void SessionControllerImpl::RequestRestartForUpdate() {
+  if (client_)
+    client_->RequestRestartForUpdate();
+}
+
 void SessionControllerImpl::AttemptRestartChrome() {
   if (client_)
     client_->AttemptRestartChrome();
diff --git a/ash/session/session_controller_impl.h b/ash/session/session_controller_impl.h
index 5c8cfd5..c14e32b 100644
--- a/ash/session/session_controller_impl.h
+++ b/ash/session/session_controller_impl.h
@@ -149,6 +149,9 @@
   // should use LockStateController::RequestSignOut() instead.
   void RequestSignOut();
 
+  // Requests a system restart to apply an OS update.
+  void RequestRestartForUpdate();
+
   // Attempts to restart the chrome browser.
   void AttemptRestartChrome();
 
diff --git a/ash/session/test_session_controller_client.cc b/ash/session/test_session_controller_client.cc
index 260a90a..5e90cfa 100644
--- a/ash/session/test_session_controller_client.cc
+++ b/ash/session/test_session_controller_client.cc
@@ -236,6 +236,10 @@
   ++request_sign_out_count_;
 }
 
+void TestSessionControllerClient::RequestRestartForUpdate() {
+  ++request_restart_for_update_count_;
+}
+
 void TestSessionControllerClient::AttemptRestartChrome() {
   ++attempt_restart_chrome_count_;
 }
diff --git a/ash/session/test_session_controller_client.h b/ash/session/test_session_controller_client.h
index 728db53b..ea1d5b2 100644
--- a/ash/session/test_session_controller_client.h
+++ b/ash/session/test_session_controller_client.h
@@ -63,6 +63,9 @@
     return attempt_restart_chrome_count_;
   }
   int request_sign_out_count() const { return request_sign_out_count_; }
+  int request_restart_for_update_count() const {
+    return request_restart_for_update_count_;
+  }
 
   // Helpers to set SessionController state.
   void SetCanLockScreen(bool can_lock);
@@ -129,6 +132,7 @@
   void RequestLockScreen() override;
   void RequestHideLockScreen() override;
   void RequestSignOut() override;
+  void RequestRestartForUpdate() override;
   void AttemptRestartChrome() override;
   void SwitchActiveUser(const AccountId& account_id) override;
   void CycleActiveUser(CycleUserDirection direction) override;
@@ -160,6 +164,7 @@
 
   bool use_lower_case_user_id_ = true;
   int request_sign_out_count_ = 0;
+  int request_restart_for_update_count_ = 0;
   int attempt_restart_chrome_count_ = 0;
 
   bool should_show_lock_screen_ = false;
diff --git a/ash/system/notification_center/notification_center_tray.cc b/ash/system/notification_center/notification_center_tray.cc
index 7a61cdc..0eb4c9ee 100644
--- a/ash/system/notification_center/notification_center_tray.cc
+++ b/ash/system/notification_center/notification_center_tray.cc
@@ -11,6 +11,8 @@
 #include "ash/system/tray/tray_bubble_view.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/message_center_types.h"
 #include "ui/views/layout/flex_layout.h"
 
 namespace ash {
@@ -18,9 +20,20 @@
 NotificationCenterTray::NotificationCenterTray(Shelf* shelf)
     : TrayBackgroundView(shelf, RoundedCornerBehavior::kStartRounded) {
   SetLayoutManager(std::make_unique<views::FlexLayout>());
+  set_use_bounce_in_animation(false);
+
+  message_center::MessageCenter::Get()->AddObserver(this);
 }
 
-NotificationCenterTray::~NotificationCenterTray() = default;
+NotificationCenterTray::~NotificationCenterTray() {
+  message_center::MessageCenter::Get()->RemoveObserver(this);
+}
+
+void NotificationCenterTray::OnSystemTrayVisibilityChanged(
+    bool system_tray_visible) {
+  system_tray_visible_ = system_tray_visible;
+  UpdateVisibility();
+}
 
 std::u16string NotificationCenterTray::GetAccessibleNameForTray() {
   return std::u16string();
@@ -39,7 +52,9 @@
 
 void NotificationCenterTray::ShowBubble() {}
 
-void NotificationCenterTray::UpdateAfterLoginStatusChange() {}
+void NotificationCenterTray::UpdateAfterLoginStatusChange() {
+  UpdateVisibility();
+}
 
 TrayBubbleView* NotificationCenterTray::GetBubbleView() {
   return nullptr;
@@ -49,6 +64,38 @@
   return nullptr;
 }
 
+void NotificationCenterTray::OnNotificationAdded(
+    const std::string& notification_id) {
+  UpdateVisibility();
+}
+
+void NotificationCenterTray::OnNotificationDisplayed(
+    const std::string& notification_id,
+    const message_center::DisplaySource source) {
+  UpdateVisibility();
+}
+
+void NotificationCenterTray::OnNotificationRemoved(
+    const std::string& notification_id,
+    bool by_user) {
+  UpdateVisibility();
+}
+
+void NotificationCenterTray::OnNotificationUpdated(
+    const std::string& notification_id) {
+  UpdateVisibility();
+}
+
+void NotificationCenterTray::UpdateVisibility() {
+  const bool new_visibility =
+      message_center::MessageCenter::Get()->NotificationCount() > 0 &&
+      system_tray_visible_;
+  if (new_visibility == visible_preferred())
+    return;
+
+  SetVisiblePreferred(new_visibility);
+}
+
 BEGIN_METADATA(NotificationCenterTray, TrayBackgroundView)
 END_METADATA
 
diff --git a/ash/system/notification_center/notification_center_tray.h b/ash/system/notification_center/notification_center_tray.h
index 336cf6a69..00e42990 100644
--- a/ash/system/notification_center/notification_center_tray.h
+++ b/ash/system/notification_center/notification_center_tray.h
@@ -5,10 +5,15 @@
 #ifndef ASH_SYSTEM_NOTIFICATION_CENTER_NOTIFICATION_CENTER_TRAY_H_
 #define ASH_SYSTEM_NOTIFICATION_CENTER_NOTIFICATION_CENTER_TRAY_H_
 
+#include <string>
+
 #include "ash/ash_export.h"
 #include "ash/system/tray/tray_background_view.h"
 #include "ash/system/unified/notification_icons_controller.h"
 #include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/message_center_observer.h"
+#include "ui/message_center/message_center_types.h"
 
 namespace views {
 class Widget;
@@ -22,7 +27,9 @@
 // A button in the tray which displays the number of currently available
 // notifications along with icons for pinned notifications. Clicking this button
 // opens a bubble with a scrollable list of all current notifications.
-class ASH_EXPORT NotificationCenterTray : public TrayBackgroundView {
+class ASH_EXPORT NotificationCenterTray
+    : public TrayBackgroundView,
+      public message_center::MessageCenterObserver {
  public:
   METADATA_HEADER(NotificationCenterTray);
 
@@ -31,6 +38,9 @@
   NotificationCenterTray& operator=(const NotificationCenterTray&) = delete;
   ~NotificationCenterTray() override;
 
+  // Called when UnifiedSystemTray's preferred visibility changes.
+  void OnSystemTrayVisibilityChanged(bool system_tray_visible);
+
   // TrayBackgroundView:
   std::u16string GetAccessibleNameForTray() override;
   void HandleLocaleChange() override;
@@ -43,11 +53,30 @@
   views::Widget* GetBubbleWidget() const override;
 
  private:
+  // message_center::MessageCenterObserver:
+  void OnNotificationAdded(const std::string& notification_id) override;
+  void OnNotificationDisplayed(
+      const std::string& notification_id,
+      const message_center::DisplaySource source) override;
+  void OnNotificationRemoved(const std::string& notification_id,
+                             bool by_user) override;
+  void OnNotificationUpdated(const std::string& notification_id) override;
+
+  // Update the visibility of the tray button based on available notifications.
+  // If there are no notifications the tray button should be hidden and shown
+  // otherwise.
+  void UpdateVisibility();
+
   // Manages showing notification icons in the tray.
   const std::unique_ptr<NotificationIconsController>
       notification_icons_controller_;
 
   // TODO(1311738): Add NotificationCenterBubble.
+
+  // The notification center tray can only be shown along side the system and
+  // date tray. This flag keeps track of the system tray's visibility being set
+  // by the status area widget.
+  bool system_tray_visible_ = true;
 };
 
 }  // namespace ash
diff --git a/ash/system/notification_center/notification_center_tray_unittest.cc b/ash/system/notification_center/notification_center_tray_unittest.cc
new file mode 100644
index 0000000..24ceeb8
--- /dev/null
+++ b/ash/system/notification_center/notification_center_tray_unittest.cc
@@ -0,0 +1,83 @@
+// 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 "ash/system/notification_center/notification_center_tray.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/system/status_area_widget.h"
+#include "ash/system/status_area_widget_test_helper.h"
+#include "ash/test/ash_test_base.h"
+#include "base/test/scoped_feature_list.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/public/cpp/notification.h"
+
+namespace ash {
+
+class NotificationCenterTrayTest : public AshTestBase {
+ public:
+  NotificationCenterTrayTest() = default;
+  NotificationCenterTrayTest(const NotificationCenterTrayTest&) = delete;
+  NotificationCenterTrayTest& operator=(const NotificationCenterTrayTest&) =
+      delete;
+  ~NotificationCenterTrayTest() override = default;
+
+  void SetUp() override {
+    // Enable quick settings revamp feature.
+    scoped_feature_list_.InitWithFeatures({ash::features::kQsRevamp}, {});
+
+    AshTestBase::SetUp();
+
+    notification_tray_ = StatusAreaWidgetTestHelper::GetStatusAreaWidget()
+                             ->notification_center_tray();
+  }
+
+  void TearDown() override {
+    notification_tray_ = nullptr;
+    AshTestBase::TearDown();
+  }
+
+  std::unique_ptr<message_center::Notification> CreateNotification(
+      const std::string& id,
+      const std::string& title = "test_title") {
+    return std::make_unique<message_center::Notification>(
+        message_center::NOTIFICATION_TYPE_SIMPLE, id, base::UTF8ToUTF16(title),
+        u"test message", ui::ImageModel(),
+        /*display_source=*/std::u16string(), GURL(),
+        message_center::NotifierId(), message_center::RichNotificationData(),
+        new message_center::NotificationDelegate());
+  }
+
+  std::string AddNotification() {
+    std::string id = base::NumberToString(id_++);
+    message_center::MessageCenter::Get()->AddNotification(
+        CreateNotification(id));
+    return id;
+  }
+
+  NotificationCenterTray* GetNotificationCenterTray() {
+    return notification_tray_;
+  }
+
+ private:
+  int id_ = 0;
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  // Owned by `StatusAreaWidget`.
+  NotificationCenterTray* notification_tray_ = nullptr;
+};
+
+// Test the initial state.
+TEST_F(NotificationCenterTrayTest, VisibilityBasedOnAvailableNotifications) {
+  EXPECT_FALSE(GetNotificationCenterTray()->GetVisible());
+
+  std::string id = AddNotification();
+  EXPECT_TRUE(GetNotificationCenterTray()->GetVisible());
+
+  message_center::MessageCenter::Get()->RemoveNotification(id, true);
+
+  EXPECT_FALSE(GetNotificationCenterTray()->GetVisible());
+}
+
+}  // namespace ash
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 45b9625..e501f6d9 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -4,6 +4,9 @@
 
 #include "ash/system/status_area_widget.h"
 
+#include <memory>
+#include <string>
+
 #include "ash/capture_mode/stop_recording_button_tray.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
@@ -23,6 +26,7 @@
 #include "ash/system/media/media_tray.h"
 #include "ash/system/model/clock_model.h"
 #include "ash/system/model/system_tray_model.h"
+#include "ash/system/notification_center/notification_center_tray.h"
 #include "ash/system/overview/overview_button_tray.h"
 #include "ash/system/palette/palette_tray.h"
 #include "ash/system/phonehub/phone_hub_tray.h"
@@ -45,6 +49,8 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/display.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/message_center_types.h"
 
 namespace ash {
 
@@ -136,6 +142,12 @@
     phone_hub_tray_ = AddTrayButton(std::make_unique<PhoneHubTray>(shelf_));
   }
 
+  if (chromeos::features::IsQsRevampEnabled()) {
+    notification_center_tray_ =
+        AddTrayButton(std::make_unique<NotificationCenterTray>(shelf_));
+    notification_center_tray_->AddObserver(this);
+  }
+
   auto unified_system_tray = std::make_unique<UnifiedSystemTray>(shelf_);
   unified_system_tray_ = unified_system_tray.get();
   if (features::IsCalendarViewEnabled()) {
@@ -175,6 +187,8 @@
 
 StatusAreaWidget::~StatusAreaWidget() {
   Shell::Get()->session_controller()->RemoveObserver(this);
+  if (features::IsQsRevampEnabled())
+    notification_center_tray_->RemoveObserver(this);
   status_area_widget_delegate_->Shutdown();
 }
 
@@ -193,14 +207,18 @@
 }
 
 void StatusAreaWidget::SetSystemTrayVisibility(bool visible) {
-  TrayBackgroundView* tray = unified_system_tray_;
-  tray->SetVisiblePreferred(visible);
+  unified_system_tray_->SetVisiblePreferred(visible);
+
   if (features::IsCalendarViewEnabled())
     date_tray_->SetVisiblePreferred(visible);
+
+  if (features::IsQsRevampEnabled())
+    notification_center_tray_->OnSystemTrayVisibilityChanged(visible);
+
   if (visible) {
     Show();
   } else {
-    tray->CloseBubble();
+    unified_system_tray_->CloseBubble();
     Hide();
   }
 }
@@ -238,6 +256,7 @@
     if (tray_button == overflow_button_tray_ ||
         tray_button == overview_button_tray_ ||
         tray_button == unified_system_tray_ || tray_button == date_tray_ ||
+        tray_button == notification_center_tray_ ||
         !tray_button->GetVisible()) {
       continue;
     }
@@ -573,6 +592,14 @@
   return true;
 }
 
+void StatusAreaWidget::OnViewVisibilityChanged(views::View* observed_view,
+                                               views::View* starting_view) {
+  if (observed_view != notification_center_tray_)
+    return;
+
+  UpdateDateTrayRoundedCorners();
+}
+
 void StatusAreaWidget::OnMouseEvent(ui::MouseEvent* event) {
   if (event->IsMouseWheelEvent()) {
     ui::MouseWheelEvent* mouse_wheel_event = event->AsMouseWheelEvent();
@@ -648,6 +675,16 @@
           child_visibility_bitmask, should_animate};
 }
 
+void StatusAreaWidget::UpdateDateTrayRoundedCorners() {
+  if (!features::IsQsRevampEnabled() || !date_tray_)
+    return;
+
+  date_tray_->SetRoundedCornerBehavior(
+      notification_center_tray_->GetVisible()
+          ? TrayBackgroundView::RoundedCornerBehavior::kNotRounded
+          : TrayBackgroundView::RoundedCornerBehavior::kStartRounded);
+}
+
 int StatusAreaWidget::GetCollapseAvailableWidth(bool force_collapsible) const {
   const int shelf_width =
       shelf_->shelf_widget()->GetClientAreaBoundsInScreen().width();
diff --git a/ash/system/status_area_widget.h b/ash/system/status_area_widget.h
index 966c36e8..a1e29c4d 100644
--- a/ash/system/status_area_widget.h
+++ b/ash/system/status_area_widget.h
@@ -11,6 +11,9 @@
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/shelf/shelf_component.h"
 #include "base/memory/weak_ptr.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/message_center_observer.h"
+#include "ui/views/view_observer.h"
 #include "ui/views/widget/widget.h"
 
 namespace aura {
@@ -24,6 +27,7 @@
 class ImeMenuTray;
 class LogoutButtonTray;
 class MediaTray;
+class NotificationCenterTray;
 class OverviewButtonTray;
 class PaletteTray;
 class PhoneHubTray;
@@ -44,6 +48,7 @@
 // on secondary monitors at the login screen).
 class ASH_EXPORT StatusAreaWidget : public SessionObserver,
                                     public ShelfComponent,
+                                    public views::ViewObserver,
                                     public views::Widget {
  public:
   // Whether the status area is collapsed or expanded. Currently, this is only
@@ -123,6 +128,9 @@
     return status_area_widget_delegate_;
   }
   UnifiedSystemTray* unified_system_tray() { return unified_system_tray_; }
+  NotificationCenterTray* notification_center_tray() {
+    return notification_center_tray_;
+  }
   DateTray* date_tray() { return date_tray_; }
   DictationButtonTray* dictation_button_tray() {
     return dictation_button_tray_;
@@ -214,6 +222,10 @@
   // changed.
   absl::optional<LayoutInputs> layout_inputs_;
 
+  // views::ViewObserver:
+  void OnViewVisibilityChanged(views::View* observed_view,
+                               views::View* starting_view) override;
+
   // views::Widget:
   void OnMouseEvent(ui::MouseEvent* event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
@@ -240,6 +252,10 @@
   // current conditions.
   CollapseState CalculateCollapseState() const;
 
+  // Update rounded corners for the date tray. The corner behavior for date
+  // tray depends on the visibility of the notification center tray.
+  void UpdateDateTrayRoundedCorners();
+
   // Gets the collapse available width based on if the date tray is shown.
   // If `force_collapsible`, returns a fixed width which is not based on the
   // shelf width.
@@ -247,10 +263,13 @@
 
   StatusAreaWidgetDelegate* const status_area_widget_delegate_;
 
+  // All tray items are owned by StatusAreaWidgetDelegate, and destroyed
+  // explicitly in a shutdown call in the StatusAreaWidget dtor.
   StatusAreaOverflowButtonTray* overflow_button_tray_ = nullptr;
   OverviewButtonTray* overview_button_tray_ = nullptr;
   DictationButtonTray* dictation_button_tray_ = nullptr;
   MediaTray* media_tray_ = nullptr;
+  NotificationCenterTray* notification_center_tray_ = nullptr;
   DateTray* date_tray_ = nullptr;
   UnifiedSystemTray* unified_system_tray_ = nullptr;
   LogoutButtonTray* logout_button_tray_ = nullptr;
diff --git a/ash/system/status_area_widget_delegate.cc b/ash/system/status_area_widget_delegate.cc
index d0fd9e04..e9636db3 100644
--- a/ash/system/status_area_widget_delegate.cc
+++ b/ash/system/status_area_widget_delegate.cc
@@ -13,6 +13,7 @@
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
+#include "ash/system/notification_center/notification_center_tray.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/date_tray.h"
@@ -33,7 +34,7 @@
 namespace {
 
 constexpr int kPaddingBetweenItems = 8;
-constexpr int kPaddingOffsetBetweenDateAndSystemTray = -4;
+constexpr int kSystemTraysRightPaddingOffset = -4;
 
 class StatusAreaWidgetDelegateAnimationSettings
     : public ui::ScopedLayerAnimationSettings {
@@ -260,11 +261,14 @@
   // is enabled).
   int right_edge = kPaddingBetweenItems;
 
-  // If this view is `DateTray`, apply the offset
-  // `kPaddingOffsetBetweenDateAndSystemTray` between it and
-  // `UnifiedSystemTray`.
-  if (child->GetClassName() == DateTray::kViewClassName) {
-    right_edge += kPaddingOffsetBetweenDateAndSystemTray;
+  // TODO(crbug/1354354): Refactor this hack to make it more efficient and less
+  // of a hack.
+  // If this view is `DateTray` or `NotificationCenterTray`, apply
+  // the offset `kSystemTraysRightPaddingOffset` between it and the tray on it's
+  // right.
+  if (child->GetClassName() == DateTray::kViewClassName ||
+      child->GetClassName() == NotificationCenterTray::kViewClassName) {
+    right_edge += kSystemTraysRightPaddingOffset;
   }
 
   if (is_child_on_edge) {
diff --git a/ash/system/status_area_widget_unittest.cc b/ash/system/status_area_widget_unittest.cc
index cc46cac..53c9f8e 100644
--- a/ash/system/status_area_widget_unittest.cc
+++ b/ash/system/status_area_widget_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/focus_cycler.h"
 #include "ash/ime/ime_controller_impl.h"
@@ -23,17 +24,20 @@
 #include "ash/system/ime_menu/ime_menu_tray.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/model/virtual_keyboard_model.h"
+#include "ash/system/notification_center/notification_center_tray.h"
 #include "ash/system/overview/overview_button_tray.h"
 #include "ash/system/palette/palette_tray.h"
 #include "ash/system/session/logout_button_tray.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/system/tray/status_area_overflow_button_tray.h"
 #include "ash/system/tray/system_tray_notifier.h"
+#include "ash/system/unified/date_tray.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/virtual_keyboard/virtual_keyboard_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
+#include "base/test/scoped_feature_list.h"
 #include "chromeos/ash/components/network/cellular_metrics_logger.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
@@ -627,4 +631,45 @@
   EXPECT_FALSE(overflow_button_->GetVisible());
 }
 
+class StatusAreaWidgetQSRevampTest : public AshTestBase {
+ protected:
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeatures({ash::features::kQsRevamp}, {});
+    AshTestBase::SetUp();
+  }
+
+  TrayBackgroundView::RoundedCornerBehavior GetTrayCornerBehavior(
+      TrayBackgroundView* tray) {
+    return tray->corner_behavior_;
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// The corner radius of the date tray changes based on the visibility of the
+// `NotificationCenterTray`. The date tray should have rounded corners on the
+// left if the `NotificationCenterTray` is not visible and no rounded corners
+// otherwise.
+TEST_F(StatusAreaWidgetQSRevampTest, DateTrayRoundedCornerBehavior) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kQsRevamp);
+
+  StatusAreaWidget* status_area =
+      StatusAreaWidgetTestHelper::GetStatusAreaWidget();
+  EXPECT_FALSE(status_area->notification_center_tray()->GetVisible());
+  EXPECT_EQ(GetTrayCornerBehavior(status_area->date_tray()),
+            TrayBackgroundView::RoundedCornerBehavior::kStartRounded);
+
+  status_area->notification_center_tray()->SetVisiblePreferred(true);
+
+  EXPECT_EQ(GetTrayCornerBehavior(status_area->date_tray()),
+            TrayBackgroundView::RoundedCornerBehavior::kNotRounded);
+
+  status_area->notification_center_tray()->SetVisiblePreferred(false);
+
+  EXPECT_EQ(GetTrayCornerBehavior(status_area->date_tray()),
+            TrayBackgroundView::RoundedCornerBehavior::kStartRounded);
+}
+
 }  // namespace ash
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index 4d0894f..38f54fa8 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -219,6 +219,7 @@
  private:
   class TrayWidgetObserver;
   class TrayBackgroundViewSessionChangeHandler;
+  friend class StatusAreaWidgetQSRevampTest;
 
   void StartVisibilityAnimation(bool visible);
 
diff --git a/ash/system/update/update_notification_controller.cc b/ash/system/update/update_notification_controller.cc
index 32ca20e..848a725 100644
--- a/ash/system/update/update_notification_controller.cc
+++ b/ash/system/update/update_notification_controller.cc
@@ -292,7 +292,7 @@
     return;
   }
   // System updates require restarting the device.
-  Shell::Get()->system_tray_model()->client()->RequestRestartForUpdate();
+  Shell::Get()->session_controller()->RequestRestartForUpdate();
   base::RecordAction(
       base::UserMetricsAction("StatusArea_OS_Update_Default_Selected"));
 }
diff --git a/ash/system/update/update_notification_controller_unittest.cc b/ash/system/update/update_notification_controller_unittest.cc
index fee2654..f7be1b0 100644
--- a/ash/system/update/update_notification_controller_unittest.cc
+++ b/ash/system/update/update_notification_controller_unittest.cc
@@ -169,6 +169,14 @@
                 base::UTF16ToUTF8(system_app_name_) + " update",
             GetNotificationMessage());
   EXPECT_EQ("Restart to update", GetNotificationButton(0));
+
+  // Click the restart button.
+  message_center::MessageCenter::Get()->ClickOnNotificationButton(
+      kNotificationId, 0);
+
+  // Restart was requested.
+  EXPECT_EQ(1,
+            GetSessionControllerClient()->request_restart_for_update_count());
 }
 
 // Tests that the update icon becomes visible when an update becomes
diff --git a/ash/webui/print_management/print_management_ui.cc b/ash/webui/print_management/print_management_ui.cc
index 4b98b5d5..89b5fe42 100644
--- a/ash/webui/print_management/print_management_ui.cc
+++ b/ash/webui/print_management/print_management_ui.cc
@@ -116,7 +116,8 @@
           kChromeUIPrintManagementHost);
   html_source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::ScriptSrc,
-      "script-src chrome://resources chrome://test 'self';");
+      "script-src chrome://resources chrome://test chrome://webui-test "
+      "'self';");
   html_source->DisableTrustedTypesCSP();
 
   const auto resources = base::make_span(kAshPrintManagementResources,
diff --git a/ash/webui/print_management/resources/BUILD.gn b/ash/webui/print_management/resources/BUILD.gn
index ab16c582..78a7ef7 100644
--- a/ash/webui/print_management/resources/BUILD.gn
+++ b/ash/webui/print_management/resources/BUILD.gn
@@ -112,6 +112,7 @@
 }
 
 ts_library("build_ts") {
+  composite = true
   root_dir = "$target_gen_dir/$preprocess_folder"
   out_dir = "$target_gen_dir/tsc"
   tsconfig_base = "tsconfig_base.json"
diff --git a/ash/webui/print_management/resources/print_job_entry.ts b/ash/webui/print_management/resources/print_job_entry.ts
index 01ebbdd..f25e825 100644
--- a/ash/webui/print_management/resources/print_job_entry.ts
+++ b/ash/webui/print_management/resources/print_job_entry.ts
@@ -153,7 +153,7 @@
     mixinBehaviors([FocusRowBehavior], I18nMixin(PolymerElement)) as
     {new (): PolymerElement & I18nMixinInterface};
 
-class PrintJobEntryElement extends PrintJobEntryElementBase {
+export class PrintJobEntryElement extends PrintJobEntryElementBase {
   static get is() {
     return 'print-job-entry';
   }
@@ -255,6 +255,11 @@
     this.addEventListener('click', () => this.onClick_());
   }
 
+  // Return private property this.fileIconClass for usage in browser tests.
+  getFileIconClass(): string {
+    return this.fileIconClass_;
+  }
+
   /**
    * Check if any elements with the class "overflow-ellipsis" needs to
    * add/remove the title attribute.
@@ -540,4 +545,10 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'print-job-entry': PrintJobEntryElement;
+  }
+}
+
 customElements.define(PrintJobEntryElement.is, PrintJobEntryElement);
diff --git a/ash/webui/print_management/resources/print_management.ts b/ash/webui/print_management/resources/print_management.ts
index b3622638..b91af8c5 100644
--- a/ash/webui/print_management/resources/print_management.ts
+++ b/ash/webui/print_management/resources/print_management.ts
@@ -59,12 +59,12 @@
 
 const PrintManagementElementBase = I18nMixin(PolymerElement);
 
-interface PrintManagementElement {
+export interface PrintManagementElement {
   $: {deleteIcon: IronIconElement};
 }
 
-class PrintManagementElement extends PrintManagementElementBase implements
-    PrintJobsObserverInterface {
+export class PrintManagementElement extends PrintManagementElementBase
+    implements PrintJobsObserverInterface {
   static get is() {
     return 'print-management';
   }
diff --git a/ash/webui/projector_app/BUILD.gn b/ash/webui/projector_app/BUILD.gn
index 87af2f0a..66ae22fe 100644
--- a/ash/webui/projector_app/BUILD.gn
+++ b/ash/webui/projector_app/BUILD.gn
@@ -37,10 +37,12 @@
     "//ash/strings",
     "//ash/webui/media_app_ui:buildflags",
     "//ash/webui/resources:media_app_bundle_resources",
+    "//ash/webui/resources:projector_annotator_trusted_resources",
+    "//ash/webui/resources:projector_annotator_untrusted_resources",
     "//ash/webui/resources:projector_app_bundle_resources",
     "//ash/webui/resources:projector_app_trusted_resources",
     "//ash/webui/resources:projector_app_untrusted_resources",
-    "//ash/webui/resources:projector_app_untrusted_resources",
+    "//ash/webui/resources:projector_common_resources",
     "//chromeos/strings",
     "//components/prefs",
     "//components/signin/public/identity_manager",
diff --git a/ash/webui/projector_app/public/cpp/projector_app_constants.cc b/ash/webui/projector_app/public/cpp/projector_app_constants.cc
index e0101a2..42e6491 100644
--- a/ash/webui/projector_app/public/cpp/projector_app_constants.cc
+++ b/ash/webui/projector_app/public/cpp/projector_app_constants.cc
@@ -10,24 +10,17 @@
 const char kChromeUIProjectorAnnotatorHost[] = "projector-annotator";
 
 // content::WebUIDataSource::Create() requires trailing slash.
-const char kChromeUIUntrustedProjectorAppUrl[] =
-    "chrome-untrusted://projector/";
+const char kChromeUIUntrustedProjectorUrl[] = "chrome-untrusted://projector/";
 const char kChromeUIUntrustedProjectorPwaUrl[] =
     "https://screencast.apps.chrome";
 
 const char kChromeUITrustedProjectorUrl[] = "chrome://projector/";
-const char kChromeUITrustedProjectorAppUrl[] = "chrome://projector/app/";
-
 const char kChromeUITrustedAnnotatorUrl[] = "chrome://projector-annotator/";
 const char kChromeUIUntrustedAnnotatorUrl[] =
     "chrome-untrusted://projector-annotator/";
-const char kChromeUITrustedAnnotatorAppUrl[] =
-    "chrome://projector-annotator/annotator/annotator_embedder.html";
-const char kChromeUIUntrustedAnnotatorAppUrl[] =
-    "chrome-untrusted://projector-annotator/annotator/annotator.html";
 
 const char kChromeUITrustedProjectorSwaAppId[] =
-    "fgnpbdobngpkonkajbmelfhjkemaddhp";
+    "nblbgfbmjfjaeonhjnbbkabkdploocij";
 
 const base::FilePath::CharType kProjectorMetadataFileExtension[] =
     FILE_PATH_LITERAL(".projector");
diff --git a/ash/webui/projector_app/public/cpp/projector_app_constants.h b/ash/webui/projector_app/public/cpp/projector_app_constants.h
index 49a87b73..0441a44b 100644
--- a/ash/webui/projector_app/public/cpp/projector_app_constants.h
+++ b/ash/webui/projector_app/public/cpp/projector_app_constants.h
@@ -12,16 +12,12 @@
 extern const char kChromeUIProjectorAppHost[];
 extern const char kChromeUIProjectorAnnotatorHost[];
 
-extern const char kChromeUIUntrustedProjectorAppUrl[];
+extern const char kChromeUIUntrustedProjectorUrl[];
 extern const char kChromeUIUntrustedProjectorPwaUrl[];
 
 extern const char kChromeUITrustedProjectorUrl[];
-extern const char kChromeUITrustedProjectorAppUrl[];
-
 extern const char kChromeUITrustedAnnotatorUrl[];
 extern const char kChromeUIUntrustedAnnotatorUrl[];
-extern const char kChromeUITrustedAnnotatorAppUrl[];
-extern const char kChromeUIUntrustedAnnotatorAppUrl[];
 
 extern const char kChromeUITrustedProjectorSwaAppId[];
 
diff --git a/ash/webui/projector_app/resources/BUILD.gn b/ash/webui/projector_app/resources/BUILD.gn
index 511bd9d3..027a704 100644
--- a/ash/webui/projector_app/resources/BUILD.gn
+++ b/ash/webui/projector_app/resources/BUILD.gn
@@ -11,124 +11,13 @@
 js_type_check("closure_compile") {
   is_polymer3 = true
   deps = [
-    ":message_types",
-    ":mock_app_closure_compile",
-    ":projector_browser_proxy",
-    ":trusted_annotator_closure_compile",
-    ":trusted_app_closure_compile",
-    ":untrusted_annotator_closure_compile",
-    ":untrusted_app_closure_compile",
-  ]
-}
-
-js_library("projector_browser_proxy") {
-  sources = [ "communication/projector_browser_proxy.js" ]
-  externs_list = [ "communication/projector_app.externs.js" ]
-}
-
-js_library("message_types") {
-  sources = [ "communication/message_types.js" ]
-}
-
-js_library("trusted_annotator_closure_compile") {
-  sources = [
-    "annotator/annotator_embedder_impl.js",
-    "annotator/trusted/trusted_annotator_comm_factory.js",
-  ]
-  deps = [
-    ":projector_browser_proxy",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/js:post_message_api_client.m",
-    "//ui/webui/resources/js:post_message_api_request_handler.m",
-    "//ui/webui/resources/js:web_ui_listener_behavior.m",
-  ]
-  externs_list = [ "communication/projector_app.externs.js" ]
-}
-
-js_library("trusted_app_closure_compile") {
-  sources = [
-    "app/embedder.js",
-    "app/trusted/launch.js",
-    "app/trusted/trusted_app_comm_factory.js",
-  ]
-  deps = [
-    ":projector_browser_proxy",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/js:post_message_api_client.m",
-    "//ui/webui/resources/js:post_message_api_request_handler.m",
-    "//ui/webui/resources/js:web_ui_listener_behavior.m",
-  ]
-  externs_list = [
-    "communication/projector_app.externs.js",
-    "//ash/webui/web_applications/externs/file_handling.externs.js",
-  ]
-}
-
-js_library("untrusted_annotator_closure_compile") {
-  sources = [ "annotator/untrusted/untrusted_annotator_comm_factory.js" ]
-  deps = [
-    "//ui/webui/resources/js:post_message_api_client.m",
-    "//ui/webui/resources/js:post_message_api_request_handler.m",
-  ]
-  externs_list = [ "communication/projector_app.externs.js" ]
-}
-
-js_library("untrusted_app_closure_compile") {
-  sources = [ "app/untrusted/untrusted_app_comm_factory.js" ]
-  deps = [
-    ":message_types",
-    "//ui/webui/resources/js:post_message_api_client.m",
-    "//ui/webui/resources/js:post_message_api_request_handler.m",
-  ]
-  externs_list = [ "communication/projector_app.externs.js" ]
-}
-
-js_library("mock_app_closure_compile") {
-  sources = [ "mock/app_bin.js" ]
-  deps = [
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-  externs_list = [ "communication/projector_app.externs.js" ]
-}
-
-generate_grd("build_untrusted_grd") {
-  input_files = [
-    "annotator/annotator.html",
-    "annotator/untrusted/untrusted_annotator_comm_factory.js",
-    "app/index.html",
-    "app/sandboxed_load_time_data.js",
-    "app/untrusted/untrusted_app_comm_factory.js",
-    "communication/message_types.js",
+    "//ash/webui/projector_app/resources/annotator/trusted:trusted_annotator_library",
+    "//ash/webui/projector_app/resources/annotator/untrusted:untrusted_annotator_library",
+    "//ash/webui/projector_app/resources/app/trusted:trusted_app",
+    "//ash/webui/projector_app/resources/app/untrusted:untrusted_app",
+    "//ash/webui/projector_app/resources/common:message_types",
+    "//ash/webui/projector_app/resources/mock:mock_app",
   ]
 
-  input_files_base_dir = rebase_path(".", "//")
-  grd_prefix = "ash_projector_app_untrusted"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
-}
-
-generate_grd("build_trusted_grd") {
-  input_files = [
-    "annotator/annotator_embedder.html",
-    "annotator/annotator_embedder.css",
-    "annotator/annotator_embedder_impl.js",
-    "annotator/trusted/trusted_annotator_comm_factory.js",
-    "app/trusted/launch.js",
-    "app/trusted/trusted_app_comm_factory.js",
-    "communication/annotator_browser_proxy.js",
-    "communication/projector_browser_proxy.js",
-    "app/embedder.html",
-    "app/embedder.css",
-    "app/embedder.js",
-    "assets/icon_16.png",
-    "assets/icon_32.png",
-    "assets/icon_48.png",
-    "assets/icon_64.png",
-    "assets/icon_96.png",
-    "assets/icon_128.png",
-    "assets/icon_192.png",
-    "assets/icon_256.png",
-  ]
-  input_files_base_dir = rebase_path(".", "//")
-  grd_prefix = "ash_projector_app_trusted"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+  closure_flags = default_closure_args + [ "browser_resolver_prefix_replacements=\"chrome-untrusted://projector/common/=../../ash/webui/projector_app/resources/common/\"" ]
 }
diff --git a/ash/webui/projector_app/resources/annotator/annotator.html b/ash/webui/projector_app/resources/annotator/annotator.html
deleted file mode 100644
index 7bba46c9..0000000
--- a/ash/webui/projector_app/resources/annotator/annotator.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
-Copyright 2021 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.
--->
-
-<!DOCTYPE html>
-<html>
-  <head>
-    <script type="module" src="./untrusted/untrusted_annotator_comm_factory.js"></script>
-    <script src="./ink.js"></script>
-    <script src="./annotator_bin.js"></script>
-  </head>
-  <body>
-  </body>
-</html>
diff --git a/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn b/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn
new file mode 100644
index 0000000..bc63964e
--- /dev/null
+++ b/ash/webui/projector_app/resources/annotator/trusted/BUILD.gn
@@ -0,0 +1,39 @@
+# 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.
+
+import("//chrome/common/features.gni")
+import("//third_party/closure_compiler/compile_js.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(is_chromeos_ash, "Projector Annotator is ChromeOS only")
+
+js_library("trusted_annotator_library") {
+  sources = [
+    "annotator_browser_proxy.js",
+    "annotator_embedder_impl.js",
+    "trusted_annotator_comm_factory.js",
+  ]
+  deps = [
+    "//ash/webui/projector_app/resources/common:message_types",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:post_message_api_client.m",
+    "//ui/webui/resources/js:post_message_api_request_handler.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
+  ]
+  externs_list = [ "../../common/projector_app.externs.js" ]
+}
+
+generate_grd("build_trusted_grd") {
+  input_files = [
+    "annotator_embedder.html",
+    "annotator_embedder.css",
+    "annotator_browser_proxy.js",
+    "trusted_annotator_comm_factory.js",
+    "annotator_embedder_impl.js",
+  ]
+
+  input_files_base_dir = rebase_path(".", "//")
+  grd_prefix = "ash_projector_annotator_trusted"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+}
diff --git a/ash/webui/projector_app/resources/communication/annotator_browser_proxy.js b/ash/webui/projector_app/resources/annotator/trusted/annotator_browser_proxy.js
similarity index 100%
rename from ash/webui/projector_app/resources/communication/annotator_browser_proxy.js
rename to ash/webui/projector_app/resources/annotator/trusted/annotator_browser_proxy.js
diff --git a/ash/webui/projector_app/resources/annotator/annotator_embedder.css b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.css
similarity index 100%
rename from ash/webui/projector_app/resources/annotator/annotator_embedder.css
rename to ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.css
diff --git a/ash/webui/projector_app/resources/annotator/annotator_embedder.html b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html
similarity index 65%
rename from ash/webui/projector_app/resources/annotator/annotator_embedder.html
rename to ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html
index dc69d33..e18c2ed 100644
--- a/ash/webui/projector_app/resources/annotator/annotator_embedder.html
+++ b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder.html
@@ -8,12 +8,15 @@
 <html>
 <head>
     <link rel="stylesheet" href="annotator_embedder.css">
+    <script type="module" src="common/message_types.js"></script>
+    <script type="module" src="annotator_browser_proxy.js"></script>
+    <script type="module" src="trusted_annotator_comm_factory.js"></script>
     <script type="module" src="annotator_embedder_impl.js"></script>
   </head>
   <body>
     <iframe
       class="marker-iframe"
-      src="chrome-untrusted://projector-annotator/annotator/annotator.html"
+      src="chrome-untrusted://projector-annotator"
       allow="cross-origin-isolated">
     </iframe>
     <annotator-embedder-impl></annotator-embedder-impl>
diff --git a/ash/webui/projector_app/resources/annotator/annotator_embedder_impl.js b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js
similarity index 92%
rename from ash/webui/projector_app/resources/annotator/annotator_embedder_impl.js
rename to ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js
index 513cb78a..8c2c6ff6 100644
--- a/ash/webui/projector_app/resources/annotator/annotator_embedder_impl.js
+++ b/ash/webui/projector_app/resources/annotator/trusted/annotator_embedder_impl.js
@@ -5,9 +5,8 @@
 import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
 import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {AnnotatorBrowserProxyImpl} from '../../communication/annotator_browser_proxy.js';
-
-import {AnnotatorTrustedCommFactory, UntrustedAnnotatorClient} from './trusted/trusted_annotator_comm_factory.js';
+import {AnnotatorBrowserProxyImpl} from './annotator_browser_proxy.js';
+import {AnnotatorTrustedCommFactory, UntrustedAnnotatorClient} from './trusted_annotator_comm_factory.js';
 
 /**
  * Enum for passing annotator error message to the browser process.
diff --git a/ash/webui/projector_app/resources/annotator/trusted/trusted_annotator_comm_factory.js b/ash/webui/projector_app/resources/annotator/trusted/trusted_annotator_comm_factory.js
index 293f1307..695206a 100644
--- a/ash/webui/projector_app/resources/annotator/trusted/trusted_annotator_comm_factory.js
+++ b/ash/webui/projector_app/resources/annotator/trusted/trusted_annotator_comm_factory.js
@@ -5,7 +5,7 @@
 import {PostMessageAPIClient} from 'chrome://resources/js/post_message_api_client.m.js';
 import {RequestHandler} from 'chrome://resources/js/post_message_api_request_handler.m.js';
 
-import {AnnotatorBrowserProxy, AnnotatorBrowserProxyImpl} from '../../communication/annotator_browser_proxy.js';
+import {AnnotatorBrowserProxy, AnnotatorBrowserProxyImpl} from './annotator_browser_proxy.js';
 
 const TARGET_URL = 'chrome-untrusted://projector-annotator/';
 
diff --git a/ash/webui/projector_app/resources/annotator/untrusted/BUILD.gn b/ash/webui/projector_app/resources/annotator/untrusted/BUILD.gn
new file mode 100644
index 0000000..16b2f6c6
--- /dev/null
+++ b/ash/webui/projector_app/resources/annotator/untrusted/BUILD.gn
@@ -0,0 +1,30 @@
+# 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.
+
+import("//chrome/common/features.gni")
+import("//third_party/closure_compiler/compile_js.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(is_chromeos_ash, "Projector Annotator is ChromeOS only")
+
+js_library("untrusted_annotator_library") {
+  sources = [ "untrusted_annotator_comm_factory.js" ]
+  deps = [
+    "//ash/webui/projector_app/resources/common:message_types",
+    "//ui/webui/resources/js:post_message_api_client.m",
+    "//ui/webui/resources/js:post_message_api_request_handler.m",
+  ]
+  externs_list = [ "../../common/projector_app.externs.js" ]
+}
+
+generate_grd("build_untrusted_grd") {
+  input_files = [
+    "annotator.html",
+    "untrusted_annotator_comm_factory.js",
+  ]
+
+  input_files_base_dir = rebase_path(".", "//")
+  grd_prefix = "ash_projector_annotator_untrusted"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+}
diff --git a/ash/webui/projector_app/resources/annotator/untrusted/annotator.html b/ash/webui/projector_app/resources/annotator/untrusted/annotator.html
new file mode 100644
index 0000000..d27ca08
--- /dev/null
+++ b/ash/webui/projector_app/resources/annotator/untrusted/annotator.html
@@ -0,0 +1,17 @@
+<!--
+Copyright 2021 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.
+-->
+
+<!DOCTYPE html>
+<html>
+  <head>
+    <script type="module" src="common/message_types.js"></script>
+    <script type="module" src="untrusted_annotator_comm_factory.js"></script>
+    <script src="ink.js"></script>
+    <script src="annotator/annotator_bin.js"></script>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/ash/webui/projector_app/resources/app/index.html b/ash/webui/projector_app/resources/app/index.html
deleted file mode 100644
index bbc79489..0000000
--- a/ash/webui/projector_app/resources/app/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-Copyright 2021 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.
--->
-
-<!DOCTYPE html>
-<html dir="$i18n{textdirection}" lang="$i18n{appLocale}">
-  <head>
-    <meta charset="utf-8">
-    <title>$i18n{appTitle}</title>
-    <script src="/app/sandboxed_load_time_data.js" defer></script>
-    <script src="/strings.js" defer></script>
-    <script type="module" src="../app/untrusted/untrusted_app_comm_factory.js"></script>
-    <script type="module" src="../app_bin.js"></script>
-    <link rel="stylesheet" href="../projector_cros_stylesheet.css"/>
-  </head>
-  <body>
-  </body>
-</html>
diff --git a/ash/webui/projector_app/resources/app/trusted/BUILD.gn b/ash/webui/projector_app/resources/app/trusted/BUILD.gn
new file mode 100644
index 0000000..cb01a57
--- /dev/null
+++ b/ash/webui/projector_app/resources/app/trusted/BUILD.gn
@@ -0,0 +1,50 @@
+# Copyright 2021 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("//chrome/common/features.gni")
+import("//third_party/closure_compiler/compile_js.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(is_chromeos_ash, "Projector is ChromeOS only")
+
+js_library("trusted_app") {
+  sources = [
+    "embedder.js",
+    "launch.js",
+    "projector_browser_proxy.js",
+    "trusted_app_comm_factory.js",
+  ]
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:post_message_api_client.m",
+    "//ui/webui/resources/js:post_message_api_request_handler.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
+  ]
+  externs_list = [
+    "../../common/projector_app.externs.js",
+    "//ash/webui/web_applications/externs/file_handling.externs.js",
+  ]
+}
+
+generate_grd("build_trusted_grd") {
+  input_files = [
+    "launch.js",
+    "trusted_app_comm_factory.js",
+    "projector_browser_proxy.js",
+    "embedder.html",
+    "embedder.css",
+    "embedder.js",
+    "assets/icon_16.png",
+    "assets/icon_32.png",
+    "assets/icon_48.png",
+    "assets/icon_64.png",
+    "assets/icon_96.png",
+    "assets/icon_128.png",
+    "assets/icon_192.png",
+    "assets/icon_256.png",
+  ]
+  input_files_base_dir = rebase_path(".", "//")
+  grd_prefix = "ash_projector_app_trusted"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+}
diff --git a/ash/webui/projector_app/resources/assets/icon_128.png b/ash/webui/projector_app/resources/app/trusted/assets/icon_128.png
similarity index 100%
rename from ash/webui/projector_app/resources/assets/icon_128.png
rename to ash/webui/projector_app/resources/app/trusted/assets/icon_128.png
Binary files differ
diff --git a/ash/webui/projector_app/resources/assets/icon_16.png b/ash/webui/projector_app/resources/app/trusted/assets/icon_16.png
similarity index 100%
rename from ash/webui/projector_app/resources/assets/icon_16.png
rename to ash/webui/projector_app/resources/app/trusted/assets/icon_16.png
Binary files differ
diff --git a/ash/webui/projector_app/resources/assets/icon_192.png b/ash/webui/projector_app/resources/app/trusted/assets/icon_192.png
similarity index 100%
rename from ash/webui/projector_app/resources/assets/icon_192.png
rename to ash/webui/projector_app/resources/app/trusted/assets/icon_192.png
Binary files differ
diff --git a/ash/webui/projector_app/resources/assets/icon_256.png b/ash/webui/projector_app/resources/app/trusted/assets/icon_256.png
similarity index 100%
rename from ash/webui/projector_app/resources/assets/icon_256.png
rename to ash/webui/projector_app/resources/app/trusted/assets/icon_256.png
Binary files differ
diff --git a/ash/webui/projector_app/resources/assets/icon_32.png b/ash/webui/projector_app/resources/app/trusted/assets/icon_32.png
similarity index 100%
rename from ash/webui/projector_app/resources/assets/icon_32.png
rename to ash/webui/projector_app/resources/app/trusted/assets/icon_32.png
Binary files differ
diff --git a/ash/webui/projector_app/resources/assets/icon_48.png b/ash/webui/projector_app/resources/app/trusted/assets/icon_48.png
similarity index 100%
rename from ash/webui/projector_app/resources/assets/icon_48.png
rename to ash/webui/projector_app/resources/app/trusted/assets/icon_48.png
Binary files differ
diff --git a/ash/webui/projector_app/resources/assets/icon_64.png b/ash/webui/projector_app/resources/app/trusted/assets/icon_64.png
similarity index 100%
rename from ash/webui/projector_app/resources/assets/icon_64.png
rename to ash/webui/projector_app/resources/app/trusted/assets/icon_64.png
Binary files differ
diff --git a/ash/webui/projector_app/resources/assets/icon_96.png b/ash/webui/projector_app/resources/app/trusted/assets/icon_96.png
similarity index 100%
rename from ash/webui/projector_app/resources/assets/icon_96.png
rename to ash/webui/projector_app/resources/app/trusted/assets/icon_96.png
Binary files differ
diff --git a/ash/webui/projector_app/resources/app/embedder.css b/ash/webui/projector_app/resources/app/trusted/embedder.css
similarity index 100%
rename from ash/webui/projector_app/resources/app/embedder.css
rename to ash/webui/projector_app/resources/app/trusted/embedder.css
diff --git a/ash/webui/projector_app/resources/app/embedder.html b/ash/webui/projector_app/resources/app/trusted/embedder.html
similarity index 88%
rename from ash/webui/projector_app/resources/app/embedder.html
rename to ash/webui/projector_app/resources/app/trusted/embedder.html
index 8059b39..e13cee6 100644
--- a/ash/webui/projector_app/resources/app/embedder.html
+++ b/ash/webui/projector_app/resources/app/trusted/embedder.html
@@ -9,6 +9,7 @@
   <head>
     <meta charset="utf-8">
     <title>$i18n{appTitle}</title>
+    <script type="module" src="common/message_types.js"></script>
     <link rel="stylesheet" href="embedder.css">
     <script type="module" src="embedder.js"></script>
   </head>
diff --git a/ash/webui/projector_app/resources/app/embedder.js b/ash/webui/projector_app/resources/app/trusted/embedder.js
similarity index 92%
rename from ash/webui/projector_app/resources/app/embedder.js
rename to ash/webui/projector_app/resources/app/trusted/embedder.js
index e0ca309..4451bc2 100644
--- a/ash/webui/projector_app/resources/app/embedder.js
+++ b/ash/webui/projector_app/resources/app/trusted/embedder.js
@@ -5,10 +5,9 @@
 import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
 import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {ProjectorBrowserProxyImpl} from '../communication/projector_browser_proxy.js';
-
-import {installLaunchHandler} from './trusted/launch.js';
-import {AppTrustedCommFactory, UntrustedAppClient} from './trusted/trusted_app_comm_factory.js';
+import {installLaunchHandler} from './launch.js';
+import {ProjectorBrowserProxyImpl} from './projector_browser_proxy.js';
+import {AppTrustedCommFactory, UntrustedAppClient} from './trusted_app_comm_factory.js';
 
 /**
  * Gets the query string from the URL.
diff --git a/ash/webui/projector_app/resources/communication/projector_browser_proxy.js b/ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js
similarity index 100%
rename from ash/webui/projector_app/resources/communication/projector_browser_proxy.js
rename to ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js
diff --git a/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js b/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js
index 216c7c53..dd3d02a 100644
--- a/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js
+++ b/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js
@@ -5,7 +5,7 @@
 import {PostMessageAPIClient} from 'chrome://resources/js/post_message_api_client.m.js';
 import {RequestHandler} from 'chrome://resources/js/post_message_api_request_handler.m.js';
 
-import {ProjectorBrowserProxy, ProjectorBrowserProxyImpl} from '../../communication/projector_browser_proxy.js';
+import {ProjectorBrowserProxy, ProjectorBrowserProxyImpl} from './projector_browser_proxy.js';
 
 const TARGET_URL = 'chrome-untrusted://projector/';
 
diff --git a/ash/webui/projector_app/resources/app/untrusted/BUILD.gn b/ash/webui/projector_app/resources/app/untrusted/BUILD.gn
new file mode 100644
index 0000000..ea6ea72
--- /dev/null
+++ b/ash/webui/projector_app/resources/app/untrusted/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2021 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("//chrome/common/features.gni")
+import("//third_party/closure_compiler/compile_js.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(is_chromeos_ash, "Projector is ChromeOS only")
+
+js_library("untrusted_app") {
+  sources = [ "untrusted_app_comm_factory.js" ]
+  deps = [
+    "//ash/webui/projector_app/resources/common:message_types",
+    "//ui/webui/resources/js:post_message_api_client.m",
+    "//ui/webui/resources/js:post_message_api_request_handler.m",
+  ]
+  externs_list = [ "../../common/projector_app.externs.js" ]
+}
+
+generate_grd("build_untrusted_grd") {
+  input_files = [
+    "index.html",
+    "sandboxed_load_time_data.js",
+    "untrusted_app_comm_factory.js",
+  ]
+
+  input_files_base_dir = rebase_path(".", "//")
+  grd_prefix = "ash_projector_app_untrusted"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+}
diff --git a/ash/webui/projector_app/resources/app/untrusted/index.html b/ash/webui/projector_app/resources/app/untrusted/index.html
new file mode 100644
index 0000000..7033f30
--- /dev/null
+++ b/ash/webui/projector_app/resources/app/untrusted/index.html
@@ -0,0 +1,21 @@
+<!--
+Copyright 2021 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.
+-->
+
+<!DOCTYPE html>
+<html dir="$i18n{textdirection}" lang="$i18n{appLocale}">
+  <head>
+    <meta charset="utf-8">
+    <title>$i18n{appTitle}</title>
+    <script src="sandboxed_load_time_data.js"></script>
+    <script src="strings.js"></script>
+    <script type="module" src="common/message_types.js"></script>
+    <script type="module" src="untrusted_app_comm_factory.js"></script>
+    <script type="module" src="app_bin.js"></script>
+    <link rel="stylesheet" href="projector_cros_stylesheet.css">
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/ash/webui/projector_app/resources/app/sandboxed_load_time_data.js b/ash/webui/projector_app/resources/app/untrusted/sandboxed_load_time_data.js
similarity index 100%
rename from ash/webui/projector_app/resources/app/sandboxed_load_time_data.js
rename to ash/webui/projector_app/resources/app/untrusted/sandboxed_load_time_data.js
diff --git a/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js b/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js
index 7566f11b..7f446a8 100644
--- a/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js
+++ b/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js
@@ -5,8 +5,7 @@
 import {PostMessageAPIClient} from '//resources/js/post_message_api_client.m.js';
 import {RequestHandler} from '//resources/js/post_message_api_request_handler.m.js';
 import {PromiseResolver} from '//resources/js/promise_resolver.m.js';
-
-import {ProjectorError} from '../../communication/message_types.js';
+import {ProjectorError} from 'chrome-untrusted://projector/common/message_types.js';
 
 const TARGET_URL = 'chrome://projector/';
 
diff --git a/ash/webui/projector_app/resources/common/BUILD.gn b/ash/webui/projector_app/resources/common/BUILD.gn
new file mode 100644
index 0000000..c3f84ec
--- /dev/null
+++ b/ash/webui/projector_app/resources/common/BUILD.gn
@@ -0,0 +1,28 @@
+# 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.
+
+import("//third_party/closure_compiler/compile_js.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(is_chromeos_ash, "Projector is ChromeOS only")
+
+js_library("message_types") {
+  sources = [ "./message_types.js" ]
+}
+
+js_library("projector_app_externs") {
+  sources = [ "./projector_app.externs.js" ]
+}
+
+generate_grd("build_common_grd") {
+  input_files = [
+    "message_types.js",
+    "projector_app.externs.js",
+  ]
+
+  resource_path_prefix = "common"
+  input_files_base_dir = rebase_path(".", "//")
+  grd_prefix = "ash_projector_common"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+}
diff --git a/ash/webui/projector_app/resources/communication/message_types.js b/ash/webui/projector_app/resources/common/message_types.js
similarity index 99%
rename from ash/webui/projector_app/resources/communication/message_types.js
rename to ash/webui/projector_app/resources/common/message_types.js
index 59fe9f8..0efa933 100644
--- a/ash/webui/projector_app/resources/communication/message_types.js
+++ b/ash/webui/projector_app/resources/common/message_types.js
@@ -22,8 +22,6 @@
   ERASER: 'eraser',
 };
 
-
-
 /**
  * Enum for projector error types supported.
  * @enum {string}
diff --git a/ash/webui/projector_app/resources/communication/projector_app.externs.js b/ash/webui/projector_app/resources/common/projector_app.externs.js
similarity index 100%
rename from ash/webui/projector_app/resources/communication/projector_app.externs.js
rename to ash/webui/projector_app/resources/common/projector_app.externs.js
diff --git a/ash/webui/projector_app/resources/mock/BUILD.gn b/ash/webui/projector_app/resources/mock/BUILD.gn
new file mode 100644
index 0000000..1bb71b7
--- /dev/null
+++ b/ash/webui/projector_app/resources/mock/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2021 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("//chrome/common/features.gni")
+import("//third_party/closure_compiler/compile_js.gni")
+
+assert(is_chromeos_ash, "Projector is ChromeOS only")
+
+js_library("mock_app") {
+  sources = [ "app_bin.js" ]
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+  externs_list = [ "../common/projector_app.externs.js" ]
+}
diff --git a/ash/webui/projector_app/trusted_projector_annotator_ui.cc b/ash/webui/projector_app/trusted_projector_annotator_ui.cc
index ef4be02..a7036bca 100644
--- a/ash/webui/projector_app/trusted_projector_annotator_ui.cc
+++ b/ash/webui/projector_app/trusted_projector_annotator_ui.cc
@@ -5,10 +5,11 @@
 #include "ash/webui/projector_app/trusted_projector_annotator_ui.h"
 
 #include "ash/public/cpp/projector/projector_annotator_controller.h"
-#include "ash/webui/grit/ash_projector_app_trusted_resources.h"
-#include "ash/webui/grit/ash_projector_app_trusted_resources_map.h"
+#include "ash/webui/grit/ash_projector_annotator_trusted_resources.h"
+#include "ash/webui/grit/ash_projector_annotator_trusted_resources_map.h"
+#include "ash/webui/grit/ash_projector_common_resources.h"
+#include "ash/webui/grit/ash_projector_common_resources_map.h"
 #include "ash/webui/projector_app/annotator_message_handler.h"
-#include "ash/webui/projector_app/projector_message_handler.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents.h"
@@ -28,11 +29,13 @@
 
   // TODO(b/216523790): Split trusted annotator resources into a separate
   // bundle.
-  source->AddResourcePaths(base::make_span(
-      kAshProjectorAppTrustedResources, kAshProjectorAppTrustedResourcesSize));
-
+  source->AddResourcePaths(
+      base::make_span(kAshProjectorAnnotatorTrustedResources,
+                      kAshProjectorAnnotatorTrustedResourcesSize));
+  source->AddResourcePaths(base::make_span(kAshProjectorCommonResources,
+                                           kAshProjectorCommonResourcesSize));
   source->AddResourcePath(
-      "", IDR_ASH_PROJECTOR_APP_TRUSTED_ANNOTATOR_ANNOTATOR_EMBEDDER_HTML);
+      "", IDR_ASH_PROJECTOR_ANNOTATOR_TRUSTED_ANNOTATOR_EMBEDDER_HTML);
 
   std::string csp =
       std::string("frame-src ") + kChromeUIUntrustedAnnotatorUrl + ";";
diff --git a/ash/webui/projector_app/trusted_projector_ui.cc b/ash/webui/projector_app/trusted_projector_ui.cc
index 640f728..5ee9f4f9 100644
--- a/ash/webui/projector_app/trusted_projector_ui.cc
+++ b/ash/webui/projector_app/trusted_projector_ui.cc
@@ -8,6 +8,8 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/webui/grit/ash_projector_app_trusted_resources.h"
 #include "ash/webui/grit/ash_projector_app_trusted_resources_map.h"
+#include "ash/webui/grit/ash_projector_common_resources.h"
+#include "ash/webui/grit/ash_projector_common_resources_map.h"
 #include "ash/webui/projector_app/projector_message_handler.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "components/prefs/pref_service.h"
@@ -28,12 +30,13 @@
 
   source->AddResourcePaths(base::make_span(
       kAshProjectorAppTrustedResources, kAshProjectorAppTrustedResourcesSize));
-
-  source->AddResourcePath("", IDR_ASH_PROJECTOR_APP_TRUSTED_APP_EMBEDDER_HTML);
+  source->AddResourcePaths(base::make_span(kAshProjectorCommonResources,
+                                           kAshProjectorCommonResourcesSize));
+  source->AddResourcePath("", IDR_ASH_PROJECTOR_APP_TRUSTED_EMBEDDER_HTML);
   source->AddLocalizedString("appTitle", IDS_ASH_PROJECTOR_DISPLAY_SOURCE);
 
   std::string csp =
-      std::string("frame-src ") + kChromeUIUntrustedProjectorAppUrl + ";";
+      std::string("frame-src ") + kChromeUIUntrustedProjectorUrl + ";";
 
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::FrameSrc, csp);
diff --git a/ash/webui/projector_app/untrusted_projector_annotator_ui.cc b/ash/webui/projector_app/untrusted_projector_annotator_ui.cc
index 80456eb4..9678a23c 100644
--- a/ash/webui/projector_app/untrusted_projector_annotator_ui.cc
+++ b/ash/webui/projector_app/untrusted_projector_annotator_ui.cc
@@ -4,8 +4,10 @@
 
 #include "ash/webui/projector_app/untrusted_projector_annotator_ui.h"
 
-#include "ash/webui/grit/ash_projector_app_untrusted_resources.h"
-#include "ash/webui/grit/ash_projector_app_untrusted_resources_map.h"
+#include "ash/webui/grit/ash_projector_annotator_untrusted_resources.h"
+#include "ash/webui/grit/ash_projector_annotator_untrusted_resources_map.h"
+#include "ash/webui/grit/ash_projector_common_resources.h"
+#include "ash/webui/grit/ash_projector_common_resources_map.h"
 #include "ash/webui/media_app_ui/buildflags.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "chromeos/grit/chromeos_projector_app_bundle_resources.h"
@@ -32,19 +34,23 @@
   // TODO(b/216523790): Split untrusted annotator resources into a separate
   // bundle.
   source->AddResourcePaths(
-      base::make_span(kAshProjectorAppUntrustedResources,
-                      kAshProjectorAppUntrustedResourcesSize));
+      base::make_span(kAshProjectorAnnotatorUntrustedResources,
+                      kAshProjectorAnnotatorUntrustedResourcesSize));
   source->AddResourcePaths(
       base::make_span(kChromeosProjectorAppBundleResources,
                       kChromeosProjectorAppBundleResourcesSize));
+  source->AddResourcePaths(base::make_span(kAshProjectorCommonResources,
+                                           kAshProjectorCommonResourcesSize));
+  source->AddResourcePath("",
+                          IDR_ASH_PROJECTOR_ANNOTATOR_UNTRUSTED_ANNOTATOR_HTML);
 
 #if BUILDFLAG(ENABLE_CROS_MEDIA_APP)
   // Loads WASM resources shipped to Chromium by chrome://media-app.
-  source->AddResourcePath("annotator/ink_engine_ink.worker.js",
+  source->AddResourcePath("ink_engine_ink.worker.js",
                           IDR_MEDIA_APP_INK_ENGINE_INK_WORKER_JS);
-  source->AddResourcePath("annotator/ink_engine_ink.wasm",
+  source->AddResourcePath("ink_engine_ink.wasm",
                           IDR_MEDIA_APP_INK_ENGINE_INK_WASM);
-  source->AddResourcePath("annotator/ink.js", IDR_MEDIA_APP_INK_JS);
+  source->AddResourcePath("ink.js", IDR_MEDIA_APP_INK_JS);
 #endif  // BUILDFLAG(ENABLE_CROS_MEDIA_APP)
 
   // Provide a list of specific script resources (javascript files and inlined
diff --git a/ash/webui/projector_app/untrusted_projector_ui.cc b/ash/webui/projector_app/untrusted_projector_ui.cc
index bf6714b..369f644 100644
--- a/ash/webui/projector_app/untrusted_projector_ui.cc
+++ b/ash/webui/projector_app/untrusted_projector_ui.cc
@@ -7,6 +7,8 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/webui/grit/ash_projector_app_untrusted_resources.h"
 #include "ash/webui/grit/ash_projector_app_untrusted_resources_map.h"
+#include "ash/webui/grit/ash_projector_common_resources.h"
+#include "ash/webui/grit/ash_projector_common_resources_map.h"
 #include "ash/webui/media_app_ui/buildflags.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "chromeos/grit/chromeos_projector_app_bundle_resources.h"
@@ -24,15 +26,18 @@
 content::WebUIDataSource* CreateProjectorHTMLSource(
     UntrustedProjectorUIDelegate* delegate) {
   content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(kChromeUIUntrustedProjectorAppUrl);
+      content::WebUIDataSource::Create(kChromeUIUntrustedProjectorUrl);
 
   source->AddResourcePaths(
       base::make_span(kAshProjectorAppUntrustedResources,
                       kAshProjectorAppUntrustedResourcesSize));
+  source->AddResourcePaths(base::make_span(kAshProjectorCommonResources,
+                                           kAshProjectorCommonResourcesSize));
   source->AddResourcePaths(
       base::make_span(kChromeosProjectorAppBundleResources,
                       kChromeosProjectorAppBundleResourcesSize));
-  source->AddResourcePath("", IDR_ASH_PROJECTOR_APP_UNTRUSTED_APP_INDEX_HTML);
+
+  source->AddResourcePath("", IDR_ASH_PROJECTOR_APP_UNTRUSTED_INDEX_HTML);
   source->AddLocalizedString("appTitle", IDS_ASH_PROJECTOR_DISPLAY_SOURCE);
 
   // Provide a list of specific script resources (javascript files and inlined
diff --git a/ash/webui/resources/BUILD.gn b/ash/webui/resources/BUILD.gn
index c5f1a2f..fcb0f82 100644
--- a/ash/webui/resources/BUILD.gn
+++ b/ash/webui/resources/BUILD.gn
@@ -274,17 +274,20 @@
 
 # Resources used by chrome-untrusted://projector SWA.
 ash_generated_grit("projector_app_untrusted_resources") {
-  source = "$root_gen_dir/ash/webui/projector_app/resources/ash_projector_app_untrusted_resources.grd"
-  deps = [ "//ash/webui/projector_app/resources:build_untrusted_grd" ]
+  source = "$root_gen_dir/ash/webui/projector_app/resources/app/untrusted/ash_projector_app_untrusted_resources.grd"
+  deps = [
+    "//ash/webui/projector_app/resources/app/untrusted:build_untrusted_grd",
+  ]
 }
 
 # Resources used by chrome://projector SWA.
 ash_generated_grit("projector_app_trusted_resources") {
-  source = "$root_gen_dir/ash/webui/projector_app/resources/ash_projector_app_trusted_resources.grd"
-  deps = [ "//ash/webui/projector_app/resources:build_trusted_grd" ]
+  source = "$root_gen_dir/ash/webui/projector_app/resources/app/trusted/ash_projector_app_trusted_resources.grd"
+  deps = [ "//ash/webui/projector_app/resources/app/trusted:build_trusted_grd" ]
 }
 
 # Resources automatically served by the chrome-untrusted://projector bundle, obtained via DEPS.
+# TODO(b/216523790): Split up the projector_app_bundle_resource into app_bundle and annotator_bundle.
 grit("projector_app_bundle_resources") {
   if (enable_cros_projector_app) {
     # Obtained via an internal CIPD package in src/DEPS.
@@ -339,6 +342,26 @@
   output_dir = "$ash_webui_grit_output_dir"
 }
 
+# Resources used by chrome-untrusted://projector-annotator SWA.
+ash_generated_grit("projector_annotator_untrusted_resources") {
+  source = "$root_gen_dir/ash/webui/projector_app/resources/annotator/untrusted/ash_projector_annotator_untrusted_resources.grd"
+  deps = [ "//ash/webui/projector_app/resources/annotator/untrusted:build_untrusted_grd" ]
+}
+
+# Resources used by chrome://projector-annotator SWA.
+ash_generated_grit("projector_annotator_trusted_resources") {
+  source = "$root_gen_dir/ash/webui/projector_app/resources/annotator/trusted/ash_projector_annotator_trusted_resources.grd"
+  deps = [
+    "//ash/webui/projector_app/resources/annotator/trusted:build_trusted_grd",
+  ]
+}
+
+# Resources used by chrome://projector and chrome://projector-annotator SWA.
+ash_generated_grit("projector_common_resources") {
+  source = "$root_gen_dir/ash/webui/projector_app/resources/common/ash_projector_common_resources.grd"
+  deps = [ "//ash/webui/projector_app/resources/common:build_common_grd" ]
+}
+
 # Resources used by chrome://eche-app
 ash_generated_grit("eche_app_resources") {
   source = "$root_gen_dir/ash/webui/eche_app_ui/ash_eche_app_resources.grd"
diff --git a/build/android/pylib/gtest/filter/unit_tests_disabled b/build/android/pylib/gtest/filter/unit_tests_disabled
index 3e08033..c8564bf 100644
--- a/build/android/pylib/gtest/filter/unit_tests_disabled
+++ b/build/android/pylib/gtest/filter/unit_tests_disabled
@@ -50,19 +50,10 @@
 PermissionsTest.GetWarningMessages_Plugins
 ImageOperations.ResizeShouldAverageColors
 
-# crbug.com/139643
-VariationsUtilTest.DisableAfterInitialization
-VariationsUtilTest.AssociateGoogleVariationID
-VariationsUtilTest.NoAssociation
-
 # crbug.com/141473
 AutofillManagerTest.UpdatePasswordSyncState
 AutofillManagerTest.UpdatePasswordGenerationState
 
-# crbug.com/145843
-EntropyProviderTest.UseOneTimeRandomizationSHA1
-EntropyProviderTest.UseOneTimeRandomizationPermuted
-
 # crbug.com/147500
 ManifestTest.RestrictedKeys
 
diff --git a/chrome/VERSION b/chrome/VERSION
index 93d5c91..05a494cf 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=107
 MINOR=0
-BUILD=5276
+BUILD=5277
 PATCH=0
diff --git a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
index 4a33626..50bb38a4 100644
--- a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
+++ b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
@@ -266,8 +266,9 @@
                     }
                     break;
                 case Fido2Api.NOT_ALLOWED_ERR:
-                    if (error.second.equals(
-                                "Request doesn't have a valid list of allowed credentials.")) {
+                    if (error.second != null
+                            && error.second.equals(
+                                    "Request doesn't have a valid list of allowed credentials.")) {
                         ctapStatus = CTAP2_ERR_NO_CREDENTIALS;
                     } else {
                         ctapStatus = CTAP2_ERR_OPERATION_DENIED;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/FullscreenVideoPictureInPictureController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/FullscreenVideoPictureInPictureController.java
index cb003f6..563bb7d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/FullscreenVideoPictureInPictureController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/FullscreenVideoPictureInPictureController.java
@@ -17,6 +17,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
+import org.chromium.base.BuildInfo;
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
 import org.chromium.base.MathUtils;
@@ -132,11 +133,10 @@
         mActivityTabProvider = activityTabProvider;
         mFullscreenManager = fullscreenManager;
 
-        // TODO(crbug.com/1345586): This should be >= rather than > .  However, it looks like
-        // auto-enter might be causing a very bad display issue on S, which local ToT builds don't
-        // replicate even though canary does.  Turning off temporarily to see if canary builds start
-        // working, or if auto-enter isn't actually the problem.
-        mListenForAutoEnterability = Build.VERSION.SDK_INT > Build.VERSION_CODES.S;
+        // TODO(crbug.com/1345586): This should be Build.VERSION.SDK_INT > Build.VERSION_CODES.S,
+        // rather than checking for T or later.  However, auto-enter seems to be causing a very bad
+        // display issue on S (31 or 32), so turn this off for S.
+        mListenForAutoEnterability = BuildInfo.isAtLeastT();
         if (mListenForAutoEnterability) addObserversIfNeeded();
     }
 
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 3eda6103..7e72834 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-107.0.5271.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-107.0.5273.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index cbb1fcbe..1e22aca 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10356,10 +10356,10 @@
           Translated
         </message>
         <message name="IDS_PARTIAL_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE" desc="Title text for the partial translate bubble label when the text selection could not be translated. This is also announced to the screenreader.">
-          The selection could not be translated at this time
+          This selection couldn't be translated
         </message>
         <message name="IDS_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE" desc="Title text for the translate bubble label when the page could not be translated. This is also announced to the screenreader.">
-          This page could not be translated
+          This page couldn't be translated
         </message>
         <message name="IDS_TRANSLATE_BUBBLE_SOURCE_LANG_COMBOBOX_ACCNAME" desc="Accessible name for the source language selection combobox.">
           Choose the page language to translate from
diff --git a/chrome/app/generated_resources_grd/IDS_PARTIAL_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_PARTIAL_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE.png.sha1
index 994f1d8f..9a46f5ba 100644
--- a/chrome/app/generated_resources_grd/IDS_PARTIAL_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_PARTIAL_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE.png.sha1
@@ -1 +1 @@
-8754e437086cac98d95de5a3a443cb4ef7de0134
\ No newline at end of file
+a29aab3ee8dbdd7bdcb50ebc082564ccb0ed9b6e
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE.png.sha1
new file mode 100644
index 0000000..e4114450
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE_TITLE.png.sha1
@@ -0,0 +1 @@
+542c5619f277efcb970c73325bf7c3062d52e127
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index b5d98ad..ccb8ead 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3573,6 +3573,10 @@
     {"enable-javascript-harmony", flag_descriptions::kJavascriptHarmonyName,
      flag_descriptions::kJavascriptHarmonyDescription, kOsAll,
      SINGLE_VALUE_TYPE(switches::kJavaScriptHarmony)},
+    {"enable-javascript-experimental-shared-memory",
+     flag_descriptions::kJavascriptExperimentalSharedMemoryName,
+     flag_descriptions::kJavascriptExperimentalSharedMemoryDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kJavaScriptExperimentalSharedMemory)},
     {"enable-experimental-webassembly-features",
      flag_descriptions::kExperimentalWebAssemblyFeaturesName,
      flag_descriptions::kExperimentalWebAssemblyFeaturesDescription, kOsAll,
@@ -4919,6 +4923,10 @@
      flag_descriptions::kImeStylusHandwritingName,
      flag_descriptions::kImeStylusHandwritingDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kImeStylusHandwriting)},
+    {"enable-cros-ime-tray-hide-voice-button",
+     flag_descriptions::kImeTrayHideVoiceButtonName,
+     flag_descriptions::kImeTrayHideVoiceButtonDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kImeTrayHideVoiceButton)},
     {"enable-cros-language-settings-update-japanese",
      flag_descriptions::kCrosLanguageSettingsUpdateJapaneseName,
      flag_descriptions::kCrosLanguageSettingsUpdateJapaneseDescription, kOsCrOS,
diff --git a/chrome/browser/android/send_tab_to_self/metrics_recorder.cc b/chrome/browser/android/send_tab_to_self/metrics_recorder.cc
index 55fad96..b34e66d 100644
--- a/chrome/browser/android/send_tab_to_self/metrics_recorder.cc
+++ b/chrome/browser/android/send_tab_to_self/metrics_recorder.cc
@@ -11,8 +11,10 @@
 namespace send_tab_to_self {
 
 // Static free function declared and called directly from Java.
-static void JNI_MetricsRecorder_RecordDeviceClickedInShareSheet(JNIEnv* env) {
-  RecordDeviceClicked(ShareEntryPoint::kShareSheet);
+static void JNI_MetricsRecorder_RecordSendingEvent(JNIEnv* env,
+                                                   int sending_event) {
+  RecordSendingEvent(ShareEntryPoint::kShareSheet,
+                     static_cast<SendingEvent>(sending_event));
 }
 
 // Static free function declared and called directly from Java.
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 3509d0c..35b67cd 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -5899,19 +5899,19 @@
 
   // Load an app with a <webview> guest that starts at a data: URL.
   LoadAppWithGuest("web_view/simple");
-  content::WebContents* guest = GetGuestWebContents();
-  ASSERT_TRUE(guest);
+  ASSERT_TRUE(GetGuestRenderFrameHost());
   scoped_refptr<content::SiteInstance> first_instance =
-      guest->GetPrimaryMainFrame()->GetSiteInstance();
+      GetGuestRenderFrameHost()->GetSiteInstance();
   EXPECT_TRUE(first_instance->IsGuest());
   EXPECT_TRUE(first_instance->GetProcess()->IsForGuestsOnly());
 
   // Ask <webview> to navigate itself to about:blank.  This should stay in the
   // same SiteInstance.
   const GURL blank_url(url::kAboutBlankURL);
-  EXPECT_TRUE(content::NavigateToURLFromRenderer(guest, blank_url));
+  EXPECT_TRUE(
+      content::NavigateToURLFromRenderer(GetGuestRenderFrameHost(), blank_url));
   scoped_refptr<content::SiteInstance> second_instance =
-      guest->GetPrimaryMainFrame()->GetSiteInstance();
+      GetGuestRenderFrameHost()->GetSiteInstance();
   EXPECT_EQ(first_instance, second_instance);
 
   // Navigate <webview> away to another page.  This should swap
@@ -5919,8 +5919,9 @@
   const GURL second_url =
       embedded_test_server()->GetURL("b.test", "/title1.html");
   EXPECT_TRUE(BrowserInitNavigationToUrl(GetGuestView(), second_url));
+  ASSERT_TRUE(GetGuestRenderFrameHost());
   scoped_refptr<content::SiteInstance> third_instance =
-      guest->GetPrimaryMainFrame()->GetSiteInstance();
+      GetGuestRenderFrameHost()->GetSiteInstance();
   EXPECT_TRUE(third_instance->IsGuest());
   EXPECT_TRUE(third_instance->GetProcess()->IsForGuestsOnly());
   EXPECT_NE(first_instance, third_instance);
@@ -5934,14 +5935,15 @@
   // stay in the same SiteInstance.
   content::WebContents* embedder = GetEmbedderWebContents();
   {
-    content::TestNavigationObserver load_observer(guest);
+    content::TestFrameNavigationObserver load_observer(
+        GetGuestRenderFrameHost());
     EXPECT_TRUE(ExecuteScript(
         embedder,
         "document.querySelector('webview').src = '" + blank_url.spec() + "';"));
     load_observer.Wait();
   }
   scoped_refptr<content::SiteInstance> fourth_instance =
-      guest->GetPrimaryMainFrame()->GetSiteInstance();
+      GetGuestRenderFrameHost()->GetSiteInstance();
   EXPECT_EQ(fourth_instance, third_instance);
 }
 
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 40bbe6a..4d1449d 100644
--- a/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
@@ -115,7 +115,7 @@
   // Projector:
   if (app_id == ash::kChromeUITrustedProjectorSwaAppId &&
       url.GetWithEmptyPath() == GURL(ash::kChromeUIUntrustedProjectorPwaUrl)) {
-    std::string override_url = ash::kChromeUITrustedProjectorAppUrl;
+    std::string override_url = ash::kChromeUITrustedProjectorUrl;
     if (url.path().length() > 1)
       override_url += url.path().substr(1);
     std::stringstream ss;
diff --git a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api_util.cc b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api_util.cc
index e6ff11ec..77fa7770 100644
--- a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api_util.cc
+++ b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api_util.cc
@@ -12,13 +12,6 @@
 namespace chrome_apps {
 namespace api {
 
-template <class T>
-void SetValueScopedPtr(T value, std::unique_ptr<T>* destination) {
-  DCHECK(destination);
-  if (value >= 0)
-    destination->reset(new T(value));
-}
-
 template <typename T>
 void SetValueOptional(T value, absl::optional<T>& destination) {
   if (value >= 0)
@@ -26,11 +19,10 @@
 }
 
 template <>
-void SetValueScopedPtr(std::string value,
-                       std::unique_ptr<std::string>* destination) {
-  DCHECK(destination);
+void SetValueOptional(std::string value,
+                      absl::optional<std::string>& destination) {
   if (!value.empty())
-    *destination = std::make_unique<std::string>(std::move(value));
+    destination = std::move(value);
 }
 
 std::unique_ptr<base::DictionaryValue> SerializeMediaMetadata(
@@ -44,17 +36,16 @@
   }
 
   SetValueOptional(metadata->duration, extension_metadata.duration);
-  SetValueScopedPtr(std::move(metadata->artist), &extension_metadata.artist);
-  SetValueScopedPtr(std::move(metadata->album), &extension_metadata.album);
-  SetValueScopedPtr(std::move(metadata->comment), &extension_metadata.comment);
-  SetValueScopedPtr(std::move(metadata->copyright),
-                    &extension_metadata.copyright);
+  SetValueOptional(std::move(metadata->artist), extension_metadata.artist);
+  SetValueOptional(std::move(metadata->album), extension_metadata.album);
+  SetValueOptional(std::move(metadata->comment), extension_metadata.comment);
+  SetValueOptional(std::move(metadata->copyright),
+                   extension_metadata.copyright);
   SetValueOptional(metadata->disc, extension_metadata.disc);
-  SetValueScopedPtr(std::move(metadata->genre), &extension_metadata.genre);
-  SetValueScopedPtr(std::move(metadata->language),
-                    &extension_metadata.language);
+  SetValueOptional(std::move(metadata->genre), extension_metadata.genre);
+  SetValueOptional(std::move(metadata->language), extension_metadata.language);
   SetValueOptional(metadata->rotation, extension_metadata.rotation);
-  SetValueScopedPtr(std::move(metadata->title), &extension_metadata.title);
+  SetValueOptional(std::move(metadata->title), extension_metadata.title);
   SetValueOptional(metadata->track, extension_metadata.track);
 
   for (const chrome::mojom::MediaStreamInfoPtr& info : metadata->raw_tags) {
diff --git a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc
index c47bac1..f533f23 100644
--- a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc
+++ b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc
@@ -260,7 +260,7 @@
 arc::mojom::BluetoothGattDBElementPtr CreateGattDBElement(
     const arc::mojom::BluetoothGattDBAttributeType type,
     const RemoteGattAttribute* attribute) {
-  absl::optional<int16_t> id =
+  absl::optional<uint16_t> id =
       ConvertGattIdentifierToId(attribute->GetIdentifier());
   if (!id)
     return nullptr;
@@ -269,8 +269,10 @@
       arc::mojom::BluetoothGattDBElement::New();
   element->type = type;
   element->uuid = attribute->GetUUID();
-  element->id = element->attribute_handle = element->start_handle =
+  element->element_id = element->attribute_handle = element->start_handle =
       element->end_handle = *id;
+  // TODO(b/191129417) remove once ARC++ handles new field
+  element->deprecated_id = *id;
   element->properties = 0;
   return element;
 }
@@ -820,13 +822,13 @@
   if (!btle_instance)
     return;
 
-  const absl::optional<int16_t> char_inst_id =
+  const absl::optional<uint16_t> char_inst_id =
       ConvertGattIdentifierToId(characteristic->GetIdentifier());
   if (!char_inst_id)
     return;
 
   BluetoothRemoteGattService* service = characteristic->GetService();
-  const absl::optional<int16_t> service_inst_id =
+  const absl::optional<uint16_t> service_inst_id =
       ConvertGattIdentifierToId(service->GetIdentifier());
   if (!service_inst_id)
     return;
@@ -838,11 +840,15 @@
       mojom::BluetoothGattServiceID::New();
   service_id->is_primary = service->IsPrimary();
   service_id->id = mojom::BluetoothGattID::New();
-  service_id->id->inst_id = *service_inst_id;
+  service_id->id->instance_id = *service_inst_id;
+  // TODO(b/191129417) remove once ARC++ handles new field
+  service_id->id->deprecated_inst_id = *service_inst_id;
   service_id->id->uuid = service->GetUUID();
 
   mojom::BluetoothGattIDPtr char_id = mojom::BluetoothGattID::New();
-  char_id->inst_id = *char_inst_id;
+  char_id->instance_id = *char_inst_id;
+  // TODO(b/191129417) remove once ARC++ handles new field
+  char_id->deprecated_inst_id = *char_inst_id;
   char_id->uuid = characteristic->GetUUID();
 
   btle_instance->OnGattNotify(std::move(address), std::move(service_id),
@@ -1599,12 +1605,12 @@
     const auto& characteristics = service->GetCharacteristics();
     if (characteristics.size() > 0) {
       const auto& descriptors = characteristics.back()->GetDescriptors();
-      const absl::optional<int16_t> start_handle =
+      const absl::optional<uint16_t> start_handle =
           ConvertGattIdentifierToId(characteristics.front()->GetIdentifier());
       if (!start_handle)
         continue;
 
-      const absl::optional<int16_t> end_handle = ConvertGattIdentifierToId(
+      const absl::optional<uint16_t> end_handle = ConvertGattIdentifierToId(
           descriptors.size() > 0 ? descriptors.back()->GetIdentifier()
                                  : characteristics.back()->GetIdentifier());
       if (!end_handle)
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action.cc b/chrome/browser/ash/arc/input_overlay/actions/action.cc
index 377025ef..1c5b9cf 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/action.cc
@@ -281,6 +281,13 @@
                                touch_injector_->content_bounds());
 }
 
+void Action::PrepareToBindPosition(std::unique_ptr<Position> position) {
+  if (pending_position_)
+    pending_position_.reset();
+  // Now it only supports changing the first touch position.
+  pending_position_ = std::move(position);
+}
+
 void Action::RestoreToDefault() {
   DCHECK(action_view_);
   bool restored = false;
@@ -426,13 +433,25 @@
 }
 
 std::unique_ptr<ActionProto> Action::ConvertToProtoIfCustomized() {
-  if (*original_input_ == *current_input_)
+  if (*original_input_ == *current_input_ &&
+      (!beta_ || original_positions_ == current_positions_)) {
     return nullptr;
+  }
 
   auto proto = std::make_unique<ActionProto>();
   proto->set_id(id_);
-  proto->set_allocated_input_element(
-      current_input_->ConvertToProto().release());
+  if (*original_input_ != *current_input_) {
+    proto->set_allocated_input_element(
+        current_input_->ConvertToProto().release());
+  }
+
+  if (beta_ && original_positions_ != current_positions_) {
+    // Now only supports changing and saving the first touch position.
+    auto pos_proto = current_positions_[0].ConvertToProto();
+    *proto->add_positions() = *pos_proto;
+    pos_proto.reset();
+  }
+
   return proto;
 }
 
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action.h b/chrome/browser/ash/arc/input_overlay/actions/action.h
index 57f839b..3ed938e 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action.h
+++ b/chrome/browser/ash/arc/input_overlay/actions/action.h
@@ -91,11 +91,12 @@
   void PrepareToBindInput(std::unique_ptr<InputElement> input_element);
   // Save |pending_input_| as |current_input_|.
   void BindPending();
-  // Cancel |pending_input_|.
+  // Cancel |pending_input_| and |pending_position_|.
   void CancelPendingBind();
   void ResetPendingBind();
 
   void PrepareToBindPosition(const gfx::Point& new_touch_center);
+  void PrepareToBindPosition(std::unique_ptr<Position> position);
 
   // Restore the input binding back to the original binding.
   void RestoreToDefault();
diff --git a/chrome/browser/ash/arc/input_overlay/actions/position.cc b/chrome/browser/ash/arc/input_overlay/actions/position.cc
index 9cb929066..41b76b7 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/position.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/position.cc
@@ -108,6 +108,16 @@
 Position& Position::operator=(const Position&) = default;
 Position::~Position() = default;
 
+// static
+std::unique_ptr<Position> Position::ConvertFromProto(
+    const PositionProto& proto) {
+  DCHECK_EQ(proto.anchor_to_target().size(), 2);
+  auto position = std::make_unique<Position>(PositionType::kDefault);
+  position->set_anchor_to_target(
+      gfx::Vector2dF(proto.anchor_to_target()[0], proto.anchor_to_target()[1]));
+  return position;
+}
+
 bool Position::ParseFromJson(const base::Value& value) {
   switch (position_type_) {
     case PositionType::kDefault:
@@ -235,6 +245,13 @@
   aspect_ratio_.reset();
 }
 
+std::unique_ptr<PositionProto> Position::ConvertToProto() {
+  auto proto = std::make_unique<PositionProto>();
+  proto->add_anchor_to_target(anchor_to_target_.x());
+  proto->add_anchor_to_target(anchor_to_target_.y());
+  return proto;
+}
+
 bool Position::operator==(const Position& other) const {
   if (this->position_type_ != other.position_type() ||
       this->anchor_ != other.anchor() ||
diff --git a/chrome/browser/ash/arc/input_overlay/actions/position.h b/chrome/browser/ash/arc/input_overlay/actions/position.h
index 268ec14..2a31742 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/position.h
+++ b/chrome/browser/ash/arc/input_overlay/actions/position.h
@@ -5,8 +5,11 @@
 #ifndef CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_ACTIONS_POSITION_H_
 #define CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_ACTIONS_POSITION_H_
 
+#include <memory>
+
 #include "base/values.h"
 #include "chrome/browser/ash/arc/input_overlay/constants.h"
+#include "chrome/browser/ash/arc/input_overlay/db/proto/app_data.pb.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/vector2d_f.h"
@@ -28,6 +31,8 @@
   Position& operator=(const Position&);
   ~Position();
 
+  static std::unique_ptr<Position> ConvertFromProto(const PositionProto& proto);
+
   bool ParseFromJson(const base::Value& value);
   // Return the position coords in |content_bounds|. |content_bounds| is bounds
   // excluding caption if the caption shows.
@@ -35,6 +40,7 @@
   // Normalize the |point| inside of |content_bounds|. |point| is relative
   // position inside of |content_bounds|.
   void Normalize(const gfx::Point& point, const gfx::RectF& content_bounds);
+  std::unique_ptr<PositionProto> ConvertToProto();
 
   bool operator==(const Position& other) const;
   bool operator!=(const Position& other) const;
@@ -42,6 +48,10 @@
   PositionType position_type() const { return position_type_; }
   const gfx::PointF& anchor() const { return anchor_; }
   const gfx::Vector2dF& anchor_to_target() const { return anchor_to_target_; }
+  void set_anchor_to_target(const gfx::Vector2dF& points_vector) {
+    anchor_to_target_ = points_vector;
+  }
+
   absl::optional<float> x_on_y() const { return x_on_y_; }
   absl::optional<float> y_on_x() const { return y_on_x_; }
   absl::optional<float> aspect_ratio() const { return aspect_ratio_; }
diff --git a/chrome/browser/ash/arc/input_overlay/actions/position_unittest.cc b/chrome/browser/ash/arc/input_overlay/actions/position_unittest.cc
index 282c8ac..bb236a172 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/position_unittest.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/position_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/test/geometry_util.h"
 
 namespace arc {
 namespace input_overlay {
@@ -524,5 +525,21 @@
   EXPECT_EQ(*pos1, *pos3);
 }
 
+TEST(PositionTest, TestProtoConversion) {
+  auto pos = std::make_unique<Position>(PositionType::kDefault);
+  auto json_value = base::JSONReader::ReadAndReturnValueWithError(kValidJson);
+  EXPECT_TRUE(json_value.has_value());
+  EXPECT_TRUE(json_value->is_dict());
+  pos->ParseFromJson(*json_value);
+  auto proto = pos->ConvertToProto();
+  EXPECT_FALSE(proto->anchor_to_target().empty());
+  EXPECT_EQ(proto->anchor_to_target().size(), 2);
+  EXPECT_FLOAT_EQ(proto->anchor_to_target()[0], 0.5);
+  EXPECT_FLOAT_EQ(proto->anchor_to_target()[1], 0.5);
+  auto deserialized = Position::ConvertFromProto(*proto);
+  EXPECT_VECTOR2DF_EQ(deserialized->anchor_to_target(),
+                      gfx::Vector2dF(0.5, 0.5));
+}
+
 }  // namespace input_overlay
 }  // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager_unittest.cc b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager_unittest.cc
index bff48ebb..1261cdd 100644
--- a/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager_unittest.cc
+++ b/chrome/browser/ash/arc/input_overlay/arc_input_overlay_manager_unittest.cc
@@ -363,12 +363,12 @@
   UpdateDisplay("800x600/r");
   EXPECT_TRUE(injector->rotation_transform());
   EXPECT_EQ(injector->content_bounds(), gfx::RectF(10, 10, 100, 100));
-  auto expect_pos = gfx::PointF(60, 60);
-  injector->rotation_transform()->TransformPoint(&expect_pos);
-  EXPECT_EQ(injector->actions()[0]->touch_down_positions()[0], expect_pos);
-  expect_pos = gfx::PointF(100, 100);
-  injector->rotation_transform()->TransformPoint(&expect_pos);
-  EXPECT_EQ(injector->actions()[1]->touch_down_positions()[0], expect_pos);
+  auto expected_pos = gfx::PointF(60, 60);
+  injector->rotation_transform()->TransformPoint(&expected_pos);
+  EXPECT_EQ(injector->actions()[0]->touch_down_positions()[0], expected_pos);
+  expected_pos = gfx::PointF(100, 100);
+  injector->rotation_transform()->TransformPoint(&expected_pos);
+  EXPECT_EQ(injector->actions()[1]->touch_down_positions()[0], expected_pos);
 }
 
 }  // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/db/proto/app_data.proto b/chrome/browser/ash/arc/input_overlay/db/proto/app_data.proto
index 312c14f..aa7a9a8 100644
--- a/chrome/browser/ash/arc/input_overlay/db/proto/app_data.proto
+++ b/chrome/browser/ash/arc/input_overlay/db/proto/app_data.proto
@@ -40,11 +40,19 @@
   optional MouseAction mouse_action = 3;
 }
 
+message PositionProto {
+  // Anchor to target with x and y values.
+  repeated float anchor_to_target = 1;
+}
+
 message ActionProto {
   // Action ID. Each action has a unique ID within the game/app.
   required uint32 id = 1;
   // Input Element. Customized input binding for this action.
-  required InputElementProto input_element = 2;
+  optional InputElementProto input_element = 2;
+  // Customized Positions for this action. Now it only saves one position. Use
+  // keyword |repeated| for future expansion.
+  repeated PositionProto positions = 3;
 }
 
 message AppDataProto {
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.cc b/chrome/browser/ash/arc/input_overlay/touch_injector.cc
index 158349e..c80478a 100644
--- a/chrome/browser/ash/arc/input_overlay/touch_injector.cc
+++ b/chrome/browser/ash/arc/input_overlay/touch_injector.cc
@@ -150,6 +150,7 @@
 }
 
 void TouchInjector::ParseActions(const base::Value& root) {
+  DCHECK(actions_.empty());
   if (enable_mouse_lock_)
     ParseMouseLock(root);
 
@@ -211,6 +212,12 @@
   target_action->PrepareToBindInput(std::move(input_element));
 }
 
+void TouchInjector::OnPositionBingingChange(
+    Action* target_action,
+    std::unique_ptr<Position> position) {
+  target_action->PrepareToBindPosition(std::move(position));
+}
+
 void TouchInjector::OnApplyPendingBinding() {
   for (auto& action : actions_)
     action->BindPending();
@@ -245,10 +252,19 @@
     DCHECK(action);
     if (!action)
       return;
-    auto input_element =
-        InputElement::ConvertFromProto(action_proto.input_element());
-    DCHECK(input_element);
-    OnInputBindingChange(action, std::move(input_element));
+
+    if (action_proto.has_input_element()) {
+      auto input_element =
+          InputElement::ConvertFromProto(action_proto.input_element());
+      DCHECK(input_element);
+      OnInputBindingChange(action, std::move(input_element));
+    }
+
+    if (beta_ && !action_proto.positions().empty()) {
+      auto position = Position::ConvertFromProto(action_proto.positions()[0]);
+      DCHECK(position);
+      OnPositionBingingChange(action, std::move(position));
+    }
   }
   OnApplyPendingBinding();
 }
@@ -652,14 +668,20 @@
   return nullptr;
 }
 
-void TouchInjector::OnSaveProtoFile() {
+std::unique_ptr<AppDataProto> TouchInjector::ConvertToProto() {
   auto app_data_proto = std::make_unique<AppDataProto>();
   for (auto& action : actions_) {
     auto customized_proto = action->ConvertToProtoIfCustomized();
     if (customized_proto)
       *app_data_proto->add_actions() = *customized_proto;
+    customized_proto.reset();
   }
   AddMenuStateToProto(*app_data_proto);
+  return app_data_proto;
+}
+
+void TouchInjector::OnSaveProtoFile() {
+  auto app_data_proto = ConvertToProto();
   std::string package_name(*GetPackageName());
   save_file_callback_.Run(std::move(app_data_proto), package_name);
 }
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.h b/chrome/browser/ash/arc/input_overlay/touch_injector.h
index d67fbcb..2feb321 100644
--- a/chrome/browser/ash/arc/input_overlay/touch_injector.h
+++ b/chrome/browser/ash/arc/input_overlay/touch_injector.h
@@ -119,6 +119,10 @@
   // DisplayMode::kView).
   void OnInputBindingChange(Action* target_action,
                             std::unique_ptr<InputElement> input_element);
+  // Change position bindings. Only supports changing the first position for
+  // now.
+  void OnPositionBingingChange(Action* target_action,
+                               std::unique_ptr<Position> position);
   // Apply pending binding as current binding, but don't save into the storage.
   void OnApplyPendingBinding();
   // Save customized input binding/pending binding as current binding and go
@@ -201,6 +205,8 @@
   // Search action by its id.
   Action* GetActionById(int id);
 
+  // Convert the customized data to AppDataProto.
+  std::unique_ptr<AppDataProto> ConvertToProto();
   // Save proto file.
   void OnSaveProtoFile();
 
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector_unittest.cc b/chrome/browser/ash/arc/input_overlay/touch_injector_unittest.cc
index cb12d856a..54659fa 100644
--- a/chrome/browser/ash/arc/input_overlay/touch_injector_unittest.cc
+++ b/chrome/browser/ash/arc/input_overlay/touch_injector_unittest.cc
@@ -238,6 +238,10 @@
     return injector_->GetRewrittenTouchInfoSizeForTesting();
   }
 
+  std::unique_ptr<AppDataProto> ConvertToProto() {
+    return injector_->ConvertToProto();
+  }
+
   aura::TestScreen* test_screen() {
     return aura::test::AuraTestHelper::GetInstance()->GetTestScreen();
   }
@@ -269,6 +273,7 @@
         widget_->GetNativeWindow(),
         base::BindLambdaForTesting(
             [&](std::unique_ptr<AppDataProto>, const std::string&) {}));
+    injector_->set_beta(true);
   }
 
   void TearDown() override {
@@ -306,7 +311,7 @@
   EXPECT_POINTF_NEAR(expectA1, event->root_location_f(), kTolerance);
   EXPECT_EQ(0, event->pointer_details().id);
 
-  event_generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE, 1);
+  event_generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE, kTolerance);
   EXPECT_FALSE(actionA->touch_id());
   EXPECT_TRUE(event_capturer_.key_events().empty());
   EXPECT_EQ(2, (int)event_capturer_.touch_events().size());
@@ -361,7 +366,7 @@
   EXPECT_POINTF_NEAR(expectA2, event->root_location_f(), kTolerance);
   EXPECT_EQ(1, event->pointer_details().id);
 
-  event_generator_->ReleaseKey(ui::VKEY_B, ui::EF_NONE, 1);
+  event_generator_->ReleaseKey(ui::VKEY_B, ui::EF_NONE, kTolerance);
   EXPECT_TRUE(event_capturer_.key_events().empty());
   EXPECT_EQ(4, (int)event_capturer_.touch_events().size());
   event = event_capturer_.touch_events()[3].get();
@@ -804,5 +809,55 @@
   EXPECT_EQ(0, GetRewrittenTouchInfoSizeForTesting());
 }
 
+TEST_F(TouchInjectorTest, TestProtoConversion) {
+  // Check whether AppDataProto is serialized correctly.
+  auto json_value =
+      base::JSONReader::ReadAndReturnValueWithError(kValidJsonActionTapKey);
+  injector_->ParseActions(*json_value);
+  // Change input binding on actions[1].
+  auto new_input = InputElement::CreateActionTapKeyElement(ui::DomCode::US_C);
+  auto* expected_input = new_input.get();
+  injector_->OnInputBindingChange(&*injector_->actions()[1],
+                                  std::move(new_input));
+  injector_->OnApplyPendingBinding();
+  // Change position binding on actions[0].
+  auto new_pos = std::make_unique<Position>(PositionType::kDefault);
+  new_pos->Normalize(gfx::Point(20, 20), gfx::RectF(100, 100));
+  auto expected_pos = *new_pos;
+  injector_->OnPositionBingingChange(&*injector_->actions()[0],
+                                     std::move(new_pos));
+  injector_->OnApplyPendingBinding();
+  auto proto = ConvertToProto();
+  // Check whether the actions[1] with new input binding is converted to proto
+  // correctly.
+  auto action_proto = proto->actions()[1];
+  EXPECT_TRUE(action_proto.has_input_element());
+  auto input_element =
+      InputElement::ConvertFromProto(action_proto.input_element());
+  EXPECT_EQ(*input_element, *expected_input);
+  // Check whether the actions[0] with new position is converted to proto
+  // correctly.
+  action_proto = proto->actions()[0];
+  EXPECT_FALSE(action_proto.positions().empty());
+  auto position = Position::ConvertFromProto(action_proto.positions()[0]);
+  EXPECT_EQ(*position, expected_pos);
+
+  // Check whether AppDataProto is deserialized correctly.
+  auto injector = std::make_unique<TouchInjector>(
+      widget_->GetNativeWindow(),
+      base::BindLambdaForTesting(
+          [&](std::unique_ptr<AppDataProto>, const std::string&) {}));
+  injector->set_beta(true);
+  injector->ParseActions(*json_value);
+  injector->OnProtoDataAvailable(*proto);
+  EXPECT_EQ(injector_->actions().size(), injector->actions().size());
+  for (int i = 0; i < injector_->actions().size(); i++) {
+    const auto* action_a = injector_->actions()[i].get();
+    const auto* action_b = injector->actions()[i].get();
+    EXPECT_EQ(*action_a->current_input(), *action_b->current_input());
+    EXPECT_EQ(action_a->current_positions(), action_b->current_positions());
+  }
+}
+
 }  // namespace input_overlay
 }  // namespace arc
diff --git a/chrome/browser/ash/file_manager/volume_manager.cc b/chrome/browser/ash/file_manager/volume_manager.cc
index 81b225c..220d5576 100644
--- a/chrome/browser/ash/file_manager/volume_manager.cc
+++ b/chrome/browser/ash/file_manager/volume_manager.cc
@@ -693,7 +693,7 @@
   const bool success = RegisterDownloadsMountPoint(profile_, localVolume);
   DCHECK(success);
 
-  DoMountEvent(ash::MountError::kNone, Volume::CreateForDownloads(localVolume));
+  DoMountEvent(Volume::CreateForDownloads(localVolume));
 
   // Asynchronously record the disk usage for the downloads path.
   base::ThreadPool::PostTask(
@@ -704,10 +704,8 @@
 
   // Subscribe to DriveIntegrationService.
   drive_integration_service_->AddObserver(this);
-  if (drive_integration_service_->IsMounted()) {
-    DoMountEvent(ash::MountError::kNone,
-                 Volume::CreateForDrive(GetDriveMountPointPath()));
-  }
+  if (drive_integration_service_->IsMounted())
+    DoMountEvent(Volume::CreateForDrive(GetDriveMountPointPath()));
 
   // Subscribe to DiskMountManager.
   disk_mount_manager_->AddObserver(this);
@@ -762,7 +760,6 @@
 
   RegisterShareCacheMountPoint(profile_);
   DoMountEvent(
-      ash::MountError::kNone,
       Volume::CreateForShareCache(util::GetShareCacheFilePath(profile_)));
 }
 
@@ -851,14 +848,11 @@
     const base::FilePath& sshfs_mount_path,
     const base::FilePath& remote_mount_path) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  std::unique_ptr<Volume> volume =
-      Volume::CreateForSshfsCrostini(sshfs_mount_path, remote_mount_path);
   // Ignore if volume already exists.
-  if (mounted_volumes_.count(volume->volume_id()) != 0)
+  if (!DoMountEvent(
+          Volume::CreateForSshfsCrostini(sshfs_mount_path, remote_mount_path)))
     return;
 
-  DoMountEvent(ash::MountError::kNone, std::move(volume));
-
   // Listen for crostini container shutdown and remove volume.
   crostini::CrostiniManager::GetForProfile(profile_)
       ->AddShutdownContainerCallback(
@@ -877,12 +871,8 @@
     const base::FilePath& remote_mount_path,
     const guest_os::VmType vm_type) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  std::unique_ptr<Volume> volume = Volume::CreateForSftpGuestOs(
-      display_name, sftp_mount_path, remote_mount_path, vm_type);
-  // Ignore if volume already exists.
-  if (mounted_volumes_.count(volume->volume_id()) != 0)
-    return;
-  DoMountEvent(ash::MountError::kNone, std::move(volume));
+  DoMountEvent(Volume::CreateForSftpGuestOs(display_name, sftp_mount_path,
+                                            remote_mount_path, vm_type));
 }
 
 void VolumeManager::RemoveSshfsCrostiniVolume(
@@ -917,16 +907,13 @@
           storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
           path);
   DCHECK(result);
-  DoMountEvent(ash::MountError::kNone, Volume::CreateForAndroidFiles(path));
-  return true;
+  return DoMountEvent(Volume::CreateForAndroidFiles(path));
 }
 
 bool VolumeManager::RegisterMediaViewForTesting(
     const std::string& root_document_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DoMountEvent(ash::MountError::kNone,
-               Volume::CreateForMediaView(root_document_id));
-  return true;
+  return DoMountEvent(Volume::CreateForMediaView(root_document_id));
 }
 
 bool VolumeManager::RemoveAndroidFilesDirectoryForTesting(
@@ -953,28 +940,27 @@
     DoUnmountEvent(*Volume::CreateForDownloads(old_path));
   }
 
-  bool success = RegisterDownloadsMountPoint(profile_, path);
-  DoMountEvent(success ? ash::MountError::kNone : ash::MountError::kInvalidPath,
-               Volume::CreateForDownloads(path));
-  return success;
+  const bool ok = RegisterDownloadsMountPoint(profile_, path);
+  return DoMountEvent(
+      Volume::CreateForDownloads(path),
+      ok ? ash::MountError::kNone : ash::MountError::kInvalidPath);
 }
 
 bool VolumeManager::RegisterCrostiniDirectoryForTesting(
     const base::FilePath& path) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  bool success =
+  const bool ok =
       storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
           file_manager::util::GetCrostiniMountPointName(profile_),
           storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
           path);
-  DoMountEvent(
-      success ? ash::MountError::kNone : ash::MountError::kInvalidPath,
-      Volume::CreateForSshfsCrostini(path, base::FilePath("/home/testuser")));
-  return true;
+  return DoMountEvent(
+      Volume::CreateForSshfsCrostini(path, base::FilePath("/home/testuser")),
+      ok ? ash::MountError::kNone : ash::MountError::kInvalidPath);
 }
 
-void VolumeManager::AddVolumeForTesting(base::FilePath path,
+bool VolumeManager::AddVolumeForTesting(base::FilePath path,
                                         VolumeType volume_type,
                                         ash::DeviceType device_type,
                                         bool read_only,
@@ -983,15 +969,15 @@
                                         std::string file_system_type,
                                         bool hidden,
                                         bool watchable) {
-  AddVolumeForTesting(Volume::CreateForTesting(
+  return AddVolumeForTesting(Volume::CreateForTesting(
       std::move(path), volume_type, device_type, read_only,
       std::move(device_path), std::move(drive_label),
       std::move(file_system_type), hidden, watchable));
 }
 
-void VolumeManager::AddVolumeForTesting(std::unique_ptr<Volume> volume) {
+bool VolumeManager::AddVolumeForTesting(std::unique_ptr<Volume> volume) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DoMountEvent(ash::MountError::kNone, std::move(volume));
+  return DoMountEvent(std::move(volume));
 }
 
 void VolumeManager::RemoveVolumeForTesting(
@@ -1014,8 +1000,7 @@
   // Raise mount event.
   // We can pass ash::MountError::kNone even when authentication is failed
   // or network is unreachable. These two errors will be handled later.
-  DoMountEvent(ash::MountError::kNone,
-               Volume::CreateForDrive(GetDriveMountPointPath()));
+  DoMountEvent(Volume::CreateForDrive(GetDriveMountPointPath()));
 }
 
 void VolumeManager::OnFileSystemBeingUnmounted() {
@@ -1119,7 +1104,7 @@
 
   switch (event) {
     case ash::disks::DiskMountManager::MOUNTING: {
-      DoMountEvent(error_code, std::move(volume));
+      DoMountEvent(std::move(volume), error_code);
       return;
     }
 
@@ -1301,7 +1286,7 @@
       break;
   }
 
-  DoMountEvent(mount_error, std::move(volume));
+  DoMountEvent(std::move(volume), mount_error);
 
   // The fusebox_mounter_ is enabled by a chrome flag.
   if (!fusebox_mounter_)
@@ -1353,7 +1338,7 @@
   DCHECK(result);
 
   // Mount the fusebox FSP storage device in files app.
-  DoMountEvent(ash::MountError::kNone, std::move(volume));
+  DoMountEvent(std::move(volume));
 }
 
 void VolumeManager::ConvertFuseBoxFSPVolumeIdToFSPIfNeeded(
@@ -1441,18 +1426,13 @@
 
   // Need to mount all roots declared in in arc_media_view_util.cc.
   if (enabled) {
-    DoMountEvent(ash::MountError::kNone,
-                 Volume::CreateForMediaView(arc::kImagesRootDocumentId));
-    DoMountEvent(ash::MountError::kNone,
-                 Volume::CreateForMediaView(arc::kVideosRootDocumentId));
-    DoMountEvent(ash::MountError::kNone,
-                 Volume::CreateForMediaView(arc::kAudioRootDocumentId));
-    DoMountEvent(ash::MountError::kNone,
-                 Volume::CreateForMediaView(arc::kDocumentsRootDocumentId));
+    DoMountEvent(Volume::CreateForMediaView(arc::kImagesRootDocumentId));
+    DoMountEvent(Volume::CreateForMediaView(arc::kVideosRootDocumentId));
+    DoMountEvent(Volume::CreateForMediaView(arc::kAudioRootDocumentId));
+    DoMountEvent(Volume::CreateForMediaView(arc::kDocumentsRootDocumentId));
     if (!base::FeatureList::IsEnabled(arc::kEnableVirtioBlkForData))
-      DoMountEvent(ash::MountError::kNone,
-                   Volume::CreateForAndroidFiles(
-                       base::FilePath(util::kAndroidFilesPath)));
+      DoMountEvent(Volume::CreateForAndroidFiles(
+          base::FilePath(util::kAndroidFilesPath)));
   } else {
     DoUnmountEvent(*Volume::CreateForMediaView(arc::kImagesRootDocumentId));
     DoUnmountEvent(*Volume::CreateForMediaView(arc::kVideosRootDocumentId));
@@ -1570,7 +1550,7 @@
 
   // Mount the MTP storage device in files app.
   std::unique_ptr<Volume> volume = Volume::CreateForMTP(path, label, read_only);
-  DoMountEvent(ash::MountError::kNone, std::move(volume));
+  DoMountEvent(std::move(volume));
 
   // The fusebox_mounter_ is enabled by a chrome flag.
   if (!fusebox_mounter_)
@@ -1614,7 +1594,7 @@
   DCHECK(result);
 
   // Mount the fusebox MTP storage device in files app.
-  DoMountEvent(ash::MountError::kNone, std::move(volume));
+  DoMountEvent(std::move(volume));
 }
 
 void VolumeManager::OnRemovableStorageDetached(
@@ -1675,10 +1655,8 @@
     const std::vector<std::string>& mime_types) {
   arc::ArcDocumentsProviderRootMap::GetForArcBrowserContext()->RegisterRoot(
       authority, document_id, root_id, read_only, mime_types);
-  DoMountEvent(
-      ash::MountError::kNone,
-      Volume::CreateForDocumentsProvider(authority, root_id, document_id, title,
-                                         summary, icon_url, read_only));
+  DoMountEvent(Volume::CreateForDocumentsProvider(
+      authority, root_id, document_id, title, summary, icon_url, read_only));
 }
 
 void VolumeManager::OnDocumentsProviderRootRemoved(
@@ -1694,8 +1672,7 @@
 
 void VolumeManager::AddSmbFsVolume(const base::FilePath& mount_point,
                                    const std::string& display_name) {
-  DoMountEvent(ash::MountError::kNone,
-               Volume::CreateForSmb(mount_point, display_name));
+  DoMountEvent(Volume::CreateForSmb(mount_point, display_name));
 }
 
 void VolumeManager::RemoveSmbFsVolume(const base::FilePath& mount_point) {
@@ -1722,10 +1699,9 @@
         break;
       }
       case ash::MountType::kDevice: {
-        DoMountEvent(ash::MountError::kNone,
-                     Volume::CreateForRemovable(
-                         mount_point, disk_mount_manager_->FindDiskBySourcePath(
-                                          mount_point.source_path)));
+        DoMountEvent(Volume::CreateForRemovable(
+            mount_point, disk_mount_manager_->FindDiskBySourcePath(
+                             mount_point.source_path)));
         break;
       }
       case ash::MountType::kNetworkStorage: {
@@ -1761,8 +1737,9 @@
     }
 
     // Mount from the tail of chain.
-    for (size_t i = chain.size(); i > 0; --i) {
-      DoMountEvent(ash::MountError::kNone, std::move(chain[i - 1]));
+    while (!chain.empty()) {
+      DoMountEvent(std::move(chain.back()));
+      chain.pop_back();
     }
   }
 }
@@ -1775,8 +1752,8 @@
   storage_monitor::StorageMonitor::GetInstance()->AddObserver(this);
 }
 
-void VolumeManager::DoMountEvent(ash::MountError error_code,
-                                 std::unique_ptr<Volume> volume_ptr) {
+bool VolumeManager::DoMountEvent(std::unique_ptr<Volume> volume_ptr,
+                                 ash::MountError error_code) {
   DCHECK(volume_ptr);
   const Volume& volume = *volume_ptr;
 
@@ -1789,28 +1766,34 @@
     bool from_current_profile =
         profile_->GetPath().IsParent(volume.source_path());
     for (const auto& mounted_volume : mounted_volumes_) {
+      DCHECK(mounted_volume);
       if (mounted_volume->mount_path().IsParent(volume.source_path())) {
         from_current_profile = true;
         break;
       }
     }
     if (!from_current_profile)
-      return;
+      return false;
   }
 
   // Filter out removable disks if forbidden by policy for this profile.
   if (volume.type() == VOLUME_TYPE_REMOVABLE_DISK_PARTITION &&
       profile_->GetPrefs()->GetBoolean(
           disks::prefs::kExternalStorageDisabled)) {
-    return;
+    return false;
   }
 
+  bool inserted = false;
+
   if (error_code == ash::MountError::kNone ||
       volume.mount_condition() != ash::disks::MountCondition::kNone) {
     const auto [it, ok] = mounted_volumes_.insert(std::move(volume_ptr));
-    if (!ok) {
-      LOG(WARNING) << "crbug.com/1354029: Volume '" << volume.volume_id()
-                   << "' is already registered";
+    if (ok) {
+      inserted = true;
+      VLOG(1) << "Added volume '" << volume.volume_id() << "'";
+      UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType", volume.type(),
+                                NUM_VOLUME_TYPE);
+    } else {
       DCHECK(volume_ptr);
       DCHECK_EQ((*it)->volume_id(), volume.volume_id());
 
@@ -1840,15 +1823,16 @@
 
       // Replace the Volume in |mounted_volumes_|.
       const_cast<std::unique_ptr<Volume>&>(*it) = std::move(volume_ptr);
+      VLOG(1) << "Replaced volume '" << volume.volume_id() << "'";
     }
 
     DCHECK_EQ(&volume, it->get());
-    UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType", volume.type(),
-                              NUM_VOLUME_TYPE);
   }
 
   for (auto& observer : observers_)
     observer.OnVolumeMounted(error_code, volume);
+
+  return inserted;
 }
 
 void VolumeManager::DoUnmountEvent(Volumes::const_iterator it,
@@ -1862,6 +1846,8 @@
   if (error_code == ash::MountError::kNone)
     node_to_delete = mounted_volumes_.extract(std::move(it));
 
+  VLOG_IF(1, node_to_delete) << "Removed volume '" << volume.volume_id() << "'";
+
   for (auto& observer : observers_)
     observer.OnVolumeUnmounted(error_code, volume);
 }
@@ -1870,6 +1856,17 @@
   return drive_integration_service_->GetMountPointPath();
 }
 
+void VolumeManager::DoUnmountEvent(base::StringPiece volume_id,
+                                   ash::MountError error_code) {
+  Volumes::const_iterator it = mounted_volumes_.find(volume_id);
+  if (it == mounted_volumes_.end()) {
+    LOG(WARNING) << "Cannot find volume '" << volume_id << "' to unmount it";
+    return;
+  }
+
+  DoUnmountEvent(std::move(it), error_code);
+}
+
 void VolumeManager::OnSshfsCrostiniUnmountCallback(
     const base::FilePath& sshfs_mount_path,
     RemoveSshfsCrostiniVolumeCallback callback,
diff --git a/chrome/browser/ash/file_manager/volume_manager.h b/chrome/browser/ash/file_manager/volume_manager.h
index 6d0fbd2..1d2c549 100644
--- a/chrome/browser/ash/file_manager/volume_manager.h
+++ b/chrome/browser/ash/file_manager/volume_manager.h
@@ -448,7 +448,7 @@
 
   // For testing purposes, adds a volume info pointing to |path|, with TESTING
   // type. Assumes that the mount point is already registered.
-  void AddVolumeForTesting(base::FilePath path,
+  bool AddVolumeForTesting(base::FilePath path,
                            VolumeType volume_type,
                            ash::DeviceType device_type,
                            bool read_only,
@@ -459,7 +459,7 @@
                            bool watchable = false);
 
   // For testing purposes, adds the volume info to the volume manager.
-  void AddVolumeForTesting(std::unique_ptr<Volume> volume);
+  bool AddVolumeForTesting(std::unique_ptr<Volume> volume);
 
   void RemoveVolumeForTesting(
       const base::FilePath& path,
@@ -585,18 +585,23 @@
   void OnStorageMonitorInitialized();
   void DoAttachMtpStorage(const storage_monitor::StorageInfo& info,
                           device::mojom::MtpStorageInfoPtr mtp_storage_info);
-  void DoMountEvent(ash::MountError error_code, std::unique_ptr<Volume> volume);
 
+  // Adds |volume| to the set |mounted_volumes_| if |error_code| is |kNone|.
+  // Returns true if the volume was actually added, ie if |error_code| is
+  // |kNone| and there was no previous volume with the same ID.
+  bool DoMountEvent(std::unique_ptr<Volume> volume,
+                    ash::MountError error_code = ash::MountError::kNone);
+
+  // Removes the Volume at position |it| if |error_code| is |kNone|.
+  // Precondition: it != mounted_volumes_.end()
   void DoUnmountEvent(Volumes::const_iterator it,
                       ash::MountError error_code = ash::MountError::kNone);
 
+  // Removes the Volume with the given ID if |error_code| is |kNone|.
   void DoUnmountEvent(base::StringPiece volume_id,
-                      ash::MountError error_code = ash::MountError::kNone) {
-    const Volumes::const_iterator it = mounted_volumes_.find(volume_id);
-    if (it != mounted_volumes_.end())
-      DoUnmountEvent(it, error_code);
-  }
+                      ash::MountError error_code = ash::MountError::kNone);
 
+  // Removes the Volume with the same ID as |volume| if |error_code| is |kNone|.
   void DoUnmountEvent(const Volume& volume,
                       ash::MountError error_code = ash::MountError::kNone) {
     DoUnmountEvent(volume.volume_id(), error_code);
diff --git a/chrome/browser/ash/file_system_provider/operations/get_actions.cc b/chrome/browser/ash/file_system_provider/operations/get_actions.cc
index 883e676..4051cead 100644
--- a/chrome/browser/ash/file_system_provider/operations/get_actions.cc
+++ b/chrome/browser/ash/file_system_provider/operations/get_actions.cc
@@ -28,7 +28,7 @@
   for (const auto& idl_action : params->actions) {
     Action action;
     action.id = idl_action.id;
-    action.title = idl_action.title.get() ? *idl_action.title : std::string();
+    action.title = idl_action.title.value_or(std::string());
     result.push_back(action);
   }
 
diff --git a/chrome/browser/ash/file_system_provider/operations/get_metadata.cc b/chrome/browser/ash/file_system_provider/operations/get_metadata.cc
index ca5f07f..245a1e6b 100644
--- a/chrome/browser/ash/file_system_provider/operations/get_metadata.cc
+++ b/chrome/browser/ash/file_system_provider/operations/get_metadata.cc
@@ -65,15 +65,15 @@
   }
 
   if (fields & ProvidedFileSystemInterface::METADATA_FIELD_MIME_TYPE &&
-      params->metadata.mime_type.get()) {
+      params->metadata.mime_type) {
     output->mime_type =
-        std::make_unique<std::string>(*params->metadata.mime_type.get());
+        std::make_unique<std::string>(*params->metadata.mime_type);
   }
 
   if (fields & ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL &&
-      params->metadata.thumbnail.get()) {
+      params->metadata.thumbnail) {
     output->thumbnail =
-        std::make_unique<std::string>(*params->metadata.thumbnail.get());
+        std::make_unique<std::string>(*params->metadata.thumbnail);
   }
 
   return true;
@@ -93,7 +93,7 @@
   }
 
   if (fields & ProvidedFileSystemInterface::METADATA_FIELD_NAME &&
-      (!metadata.name.get() || !ValidateName(*metadata.name, root_entry))) {
+      (!metadata.name || !ValidateName(*metadata.name, root_entry))) {
     return false;
   }
 
@@ -117,13 +117,13 @@
   // accepted. Note, that there is a warning in custom bindings for it.
 
   if (fields & ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL &&
-      metadata.thumbnail.get()) {
+      metadata.thumbnail) {
     // Sanity check for the thumbnail format. Note, that another, more
     // granural check is done in custom bindings. Note, this is an extra check
     // only for the security reasons.
     const std::string expected_prefix = "data:";
     std::string thumbnail_prefix =
-        metadata.thumbnail.get()->substr(0, expected_prefix.size());
+        metadata.thumbnail->substr(0, expected_prefix.size());
     std::transform(thumbnail_prefix.begin(),
                    thumbnail_prefix.end(),
                    thumbnail_prefix.begin(),
diff --git a/chrome/browser/ash/file_system_provider/operations/get_metadata_unittest.cc b/chrome/browser/ash/file_system_provider/operations/get_metadata_unittest.cc
index d924a51..da4aa6d 100644
--- a/chrome/browser/ash/file_system_provider/operations/get_metadata_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/get_metadata_unittest.cc
@@ -133,11 +133,11 @@
   // Correct metadata for non-root.
   {
     EntryMetadata metadata;
-    metadata.name = std::make_unique<std::string>(kValidFileName);
+    metadata.name = kValidFileName;
     metadata.modification_time = std::make_unique<ModificationTime>();
     metadata.modification_time->additional_properties.SetString(
         "value", "invalid-date-time");  // Invalid modification time is OK.
-    metadata.thumbnail = std::make_unique<std::string>(kValidThumbnailUrl);
+    metadata.thumbnail = kValidThumbnailUrl;
     EXPECT_TRUE(ValidateIDLEntryMetadata(
         metadata,
         ProvidedFileSystemInterface::METADATA_FIELD_NAME |
@@ -149,7 +149,7 @@
   // Correct metadata for non-root (without thumbnail).
   {
     EntryMetadata metadata;
-    metadata.name = std::make_unique<std::string>(kValidFileName);
+    metadata.name = kValidFileName;
     metadata.modification_time = std::make_unique<ModificationTime>();
     metadata.modification_time->additional_properties.SetString(
         "value", "invalid-date-time");  // Invalid modification time is OK.
@@ -164,7 +164,7 @@
   // Correct metadata for root.
   {
     EntryMetadata metadata;
-    metadata.name = std::make_unique<std::string>();
+    metadata.name.emplace();
     metadata.modification_time = std::make_unique<ModificationTime>();
     metadata.modification_time->additional_properties.SetString(
         "value", "invalid-date-time");  // Invalid modification time is OK.
@@ -179,7 +179,7 @@
   // Invalid characters in the name.
   {
     EntryMetadata metadata;
-    metadata.name = std::make_unique<std::string>("hello/world");
+    metadata.name = "hello/world";
     EXPECT_FALSE(ValidateIDLEntryMetadata(
         metadata, ProvidedFileSystemInterface::METADATA_FIELD_NAME,
         false /* root_path */));
@@ -188,7 +188,7 @@
   // Empty name for non-root.
   {
     EntryMetadata metadata;
-    metadata.name = std::make_unique<std::string>();
+    metadata.name.emplace();
     EXPECT_FALSE(ValidateIDLEntryMetadata(
         metadata, ProvidedFileSystemInterface::METADATA_FIELD_NAME,
         false /* root_path */));
@@ -205,7 +205,7 @@
   // Invalid thumbnail.
   {
     EntryMetadata metadata;
-    metadata.thumbnail = std::make_unique<std::string>("http://invalid-scheme");
+    metadata.thumbnail = "http://invalid-scheme";
     EXPECT_FALSE(ValidateIDLEntryMetadata(
         metadata, ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL,
         false /* root_path */));
@@ -214,7 +214,7 @@
   // Empty string for thumbnail.
   {
     EntryMetadata metadata;
-    metadata.thumbnail = std::make_unique<std::string>();
+    metadata.thumbnail.emplace();
     EXPECT_FALSE(ValidateIDLEntryMetadata(
         metadata, ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL,
         false /* root_path */));
diff --git a/chrome/browser/ash/power/smart_charging/smart_charging_manager.cc b/chrome/browser/ash/power/smart_charging/smart_charging_manager.cc
index 7753ad0..ea0bea7 100644
--- a/chrome/browser/ash/power/smart_charging/smart_charging_manager.cc
+++ b/chrome/browser/ash/power/smart_charging/smart_charging_manager.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/constants/devicetype.h"
 #include "chromeos/dbus/power_manager/backlight.pb.h"
+#include "chromeos/dbus/power_manager/user_charging_event.pb.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/core/session_manager_observer.h"
 #include "components/viz/host/host_frame_sink_manager.h"
@@ -32,6 +33,12 @@
 namespace power {
 
 namespace {
+
+using PastEvent = power_manager::PastChargingEvents::Event;
+using PastChargingEvents = power_manager::PastChargingEvents;
+using EventReason = power_manager::UserChargingEvent::Event::Reason;
+using UserChargingEvent = power_manager::UserChargingEvent;
+
 constexpr int kBucketSize = 15;
 
 // Interval at which data should be logged.
@@ -386,7 +393,7 @@
     features.set_screen_brightness_percent(
         static_cast<int>(screen_brightness_percent_.value()));
 
-  features.set_duration_recent_video_playing(
+  features.set_duration_recent_video_playing_minutes(
       ukm::GetExponentialBucketMinForUserTiming(
           DurationRecentVideoPlaying().InMinutes()));
 
@@ -395,7 +402,7 @@
   base::Time::Exploded now_exploded;
   now.LocalExplode(&now_exploded);
 
-  features.set_time_of_the_day(ukm::GetLinearBucketMin(
+  features.set_time_of_the_day_minutes(ukm::GetLinearBucketMin(
       static_cast<int64_t>(now_exploded.hour * 60 + now_exploded.minute),
       kBucketSize));
   features.set_day_of_week(static_cast<UserChargingEvent::Features::DayOfWeek>(
@@ -433,17 +440,18 @@
 
   if (!last_charge_plugged_in.has_time() || !last_charge_unplugged.has_time())
     return;
-  features.set_time_since_last_charge(ukm::GetExponentialBucketMinForCounts1000(
-      now.ToDeltaSinceWindowsEpoch().InMinutes() -
-      last_charge_unplugged.time()));
-  features.set_duration_of_last_charge(
+  features.set_time_since_last_charge_minutes(
+      ukm::GetExponentialBucketMinForCounts1000(
+          now.ToDeltaSinceWindowsEpoch().InMinutes() -
+          last_charge_unplugged.time()));
+  features.set_duration_of_last_charge_minutes(
       ukm::GetExponentialBucketMinForCounts1000(last_charge_unplugged.time() -
                                                 last_charge_plugged_in.time()));
   features.set_battery_percentage_before_last_charge(
       last_charge_plugged_in.battery_percent());
   features.set_battery_percentage_of_last_charge(
       last_charge_unplugged.battery_percent());
-  features.set_timezone_difference_from_last_charge(
+  features.set_timezone_difference_from_last_charge_hours(
       (now.UTCMidnight() - now.LocalMidnight()).InHours() -
       last_charge_unplugged.timezone());
 }
@@ -472,7 +480,7 @@
 }
 
 void SmartChargingManager::OnTimerFired() {
-  LogEvent(UserChargingEvent_Event::PERIODIC_LOG);
+  LogEvent(power_manager::UserChargingEvent_Event::PERIODIC_LOG);
 }
 
 void SmartChargingManager::OnReceiveScreenBrightnessPercent(
diff --git a/chrome/browser/ash/power/smart_charging/smart_charging_manager.h b/chrome/browser/ash/power/smart_charging/smart_charging_manager.h
index 717c5e7..b1f03bd 100644
--- a/chrome/browser/ash/power/smart_charging/smart_charging_manager.h
+++ b/chrome/browser/ash/power/smart_charging/smart_charging_manager.h
@@ -13,9 +13,9 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/ash/power/ml/boot_clock.h"
 #include "chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.h"
-#include "chrome/browser/ash/power/smart_charging/user_charging_event.pb.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/dbus/power/power_manager_client.h"
+#include "chromeos/dbus/power_manager/user_charging_event.pb.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/core/session_manager_observer.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -30,9 +30,6 @@
 class RecentEventsCounter;
 }  // namespace ml
 
-using PastEvent = PastChargingEvents::Event;
-using EventReason = UserChargingEvent::Event::Reason;
-
 // SmartChargingManager logs battery percentage and other features related to
 // user charging events. It is currently used to log data and will be
 // extended to do inference in the future.
@@ -88,10 +85,10 @@
   friend class SmartChargingManagerTest;
 
   // Populates the UserChargingEvent proto for logging/inference.
-  void PopulateUserChargingEventProto(UserChargingEvent* proto);
+  void PopulateUserChargingEventProto(power_manager::UserChargingEvent* proto);
 
   // Log the event.
-  void LogEvent(const EventReason& reason);
+  void LogEvent(const power_manager::UserChargingEvent::Event::Reason& reason);
 
   // Called when the periodic timer triggers.
   void OnTimerFired();
@@ -115,16 +112,20 @@
   void MaybeSaveToDisk(const base::FilePath& profile_path);
 
   // Calls after saving from disk completes.
-  void OnLoadProtoFromDiskComplete(std::unique_ptr<PastChargingEvents> proto);
+  void OnLoadProtoFromDiskComplete(
+      std::unique_ptr<power_manager::PastChargingEvents> proto);
 
   // Adds a past events given it's reason to |past_events_|.
-  void AddPastEvent(const EventReason& reason);
+  void AddPastEvent(
+      const power_manager::UserChargingEvent::Event::Reason& reason);
 
   // Updates and deletes events.
   void UpdatePastEvents();
 
   // Gets the "plug in" and "unplug" events of the last charge.
-  std::tuple<PastEvent, PastEvent> GetLastChargeEvents();
+  std::tuple<power_manager::PastChargingEvents::Event,
+             power_manager::PastChargingEvents::Event>
+  GetLastChargeEvents();
 
   base::ScopedObservation<ui::UserActivityDetector, ui::UserActivityObserver>
       user_activity_observation_{this};
@@ -168,8 +169,8 @@
 
   // TODO(crbug.com/1028853): This is for testing only. Need to remove when ukm
   // logger is available.
-  UserChargingEvent user_charging_event_for_test_;
-  std::vector<PastEvent> past_events_;
+  power_manager::UserChargingEvent user_charging_event_for_test_;
+  std::vector<power_manager::PastChargingEvents::Event> past_events_;
 
   absl::optional<double> battery_percent_;
   absl::optional<double> screen_brightness_percent_;
diff --git a/chrome/browser/ash/power/smart_charging/smart_charging_manager_unittest.cc b/chrome/browser/ash/power/smart_charging/smart_charging_manager_unittest.cc
index 8ded5ac2..2e1de9a1 100644
--- a/chrome/browser/ash/power/smart_charging/smart_charging_manager_unittest.cc
+++ b/chrome/browser/ash/power/smart_charging/smart_charging_manager_unittest.cc
@@ -9,15 +9,20 @@
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/time/clock.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/ash/power/smart_charging/user_charging_event.pb.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
+#include "chromeos/dbus/power_manager/user_charging_event.pb.h"
 #include "components/session_manager/core/session_manager.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 
 namespace ash {
 namespace power {
 namespace {
+
+using PastEvent = power_manager::PastChargingEvents::Event;
+using EventReason = power_manager::UserChargingEvent::Event::Reason;
+using UserChargingEvent = power_manager::UserChargingEvent;
+
 PastEvent CreateEvent(int time,
                       int battery_percent,
                       int timezone,
@@ -506,8 +511,8 @@
   const auto features = GetUserChargingEvent().features();
 
   EXPECT_TRUE(features.halt_from_last_charge());
-  EXPECT_EQ(features.time_since_last_charge(), 38);
-  EXPECT_EQ(features.duration_of_last_charge(), 58);
+  EXPECT_EQ(features.time_since_last_charge_minutes(), 38);
+  EXPECT_EQ(features.duration_of_last_charge_minutes(), 58);
   EXPECT_EQ(features.battery_percentage_before_last_charge(), 23);
   EXPECT_EQ(features.battery_percentage_of_last_charge(), 80);
 }
diff --git a/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.cc b/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.cc
index ed51848..9c48124e0 100644
--- a/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.cc
+++ b/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.h"
 
-#include "chrome/browser/ash/power/smart_charging/user_charging_event.pb.h"
+#include "chromeos/dbus/power_manager/user_charging_event.pb.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
@@ -12,6 +12,12 @@
 namespace ash {
 namespace power {
 
+namespace {
+
+using UserChargingEvent = power_manager::UserChargingEvent;
+
+}  // namespace
+
 void SmartChargingUkmLogger::LogEvent(
     const UserChargingEvent& user_charging_event) const {
   const ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
@@ -26,14 +32,14 @@
     ukm_smart_charging.SetBatteryPercentage(features.battery_percentage());
   }
 
-  if (features.has_time_since_last_charge()) {
+  if (features.has_time_since_last_charge_minutes()) {
     ukm_smart_charging.SetTimeSinceLastCharge(
-        features.time_since_last_charge());
+        features.time_since_last_charge_minutes());
   }
 
-  if (features.has_duration_of_last_charge()) {
+  if (features.has_duration_of_last_charge_minutes()) {
     ukm_smart_charging.SetDurationOfLastCharge(
-        features.duration_of_last_charge());
+        features.duration_of_last_charge_minutes());
   }
 
   if (features.has_battery_percentage_of_last_charge()) {
@@ -46,8 +52,8 @@
         features.battery_percentage_before_last_charge());
   }
 
-  if (features.has_time_of_the_day()) {
-    ukm_smart_charging.SetTimeOfTheDay(features.time_of_the_day());
+  if (features.has_time_of_the_day_minutes()) {
+    ukm_smart_charging.SetTimeOfTheDay(features.time_of_the_day_minutes());
   }
 
   if (features.has_day_of_week()) {
@@ -62,9 +68,9 @@
     ukm_smart_charging.SetMonth(features.month());
   }
 
-  if (features.has_timezone_difference_from_last_charge()) {
+  if (features.has_timezone_difference_from_last_charge_hours()) {
     ukm_smart_charging.SetTimezoneDifferenceFromLastCharge(
-        features.timezone_difference_from_last_charge());
+        features.timezone_difference_from_last_charge_hours());
   }
 
   if (features.has_device_type()) {
@@ -94,14 +100,14 @@
         features.num_recent_stylus_events());
   }
 
-  if (features.has_duration_recent_video_playing()) {
+  if (features.has_duration_recent_video_playing_minutes()) {
     ukm_smart_charging.SetDurationRecentVideoPlaying(
-        features.duration_recent_video_playing());
+        features.duration_recent_video_playing_minutes());
   }
 
-  if (features.has_duration_recent_audio_playing()) {
+  if (features.has_duration_recent_audio_playing_minutes()) {
     ukm_smart_charging.SetDurationRecentAudioPlaying(
-        features.duration_recent_audio_playing());
+        features.duration_recent_audio_playing_minutes());
   }
 
   if (features.has_screen_brightness_percent()) {
@@ -109,8 +115,8 @@
         features.screen_brightness_percent());
   }
 
-  if (features.has_voltage()) {
-    ukm_smart_charging.SetVoltage(features.voltage());
+  if (features.has_voltage_mv()) {
+    ukm_smart_charging.SetVoltage(features.voltage_mv());
   }
 
   if (features.has_halt_from_last_charge()) {
diff --git a/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.h b/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.h
index 06891d1b..a31c991 100644
--- a/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.h
+++ b/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.h
@@ -5,11 +5,11 @@
 #ifndef CHROME_BROWSER_ASH_POWER_SMART_CHARGING_SMART_CHARGING_UKM_LOGGER_H_
 #define CHROME_BROWSER_ASH_POWER_SMART_CHARGING_SMART_CHARGING_UKM_LOGGER_H_
 
+#include "chromeos/dbus/power_manager/user_charging_event.pb.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace ash {
 namespace power {
-class UserChargingEvent;
 
 class SmartChargingUkmLogger {
  public:
@@ -18,7 +18,8 @@
   SmartChargingUkmLogger(const SmartChargingUkmLogger&) = delete;
   SmartChargingUkmLogger& operator=(const SmartChargingUkmLogger&) = delete;
 
-  void LogEvent(const UserChargingEvent& user_charging_event) const;
+  void LogEvent(
+      const power_manager::UserChargingEvent& user_charging_event) const;
 };
 }  // namespace power
 }  // namespace ash
diff --git a/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger_unittest.cc b/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger_unittest.cc
index 32624f89..166c501 100644
--- a/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger_unittest.cc
+++ b/chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger_unittest.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/ash/power/smart_charging/smart_charging_ukm_logger.h"
 
-#include "chrome/browser/ash/power/smart_charging/user_charging_event.pb.h"
 #include "chrome/browser/ui/tabs/tab_ukm_test_helper.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chromeos/dbus/power_manager/user_charging_event.pb.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -14,7 +14,10 @@
 namespace power {
 
 namespace {
+
 using UkmEntry = ukm::builders::SmartCharging;
+using UserChargingEvent = power_manager::UserChargingEvent;
+
 }  // namespace
 
 class SmartChargingUkmLoggerTest : public ChromeRenderViewHostTestHarness {
@@ -46,25 +49,25 @@
   UserChargingEvent::Features* features =
       user_charging_event.mutable_features();
   features->set_battery_percentage(35);
-  features->set_time_since_last_charge(128);
-  features->set_duration_of_last_charge(47);
+  features->set_time_since_last_charge_minutes(128);
+  features->set_duration_of_last_charge_minutes(47);
   features->set_battery_percentage_of_last_charge(80);
   features->set_battery_percentage_before_last_charge(19);
-  features->set_time_of_the_day(620);
+  features->set_time_of_the_day_minutes(620);
   features->set_day_of_week(UserChargingEvent::Features::WED);
   features->set_day_of_month(27);
   features->set_month(UserChargingEvent::Features::AUG);
-  features->set_timezone_difference_from_last_charge(-5);
+  features->set_timezone_difference_from_last_charge_hours(-5);
   features->set_device_type(UserChargingEvent::Features::TABLET);
   features->set_device_mode(UserChargingEvent::Features::TABLET_MODE);
   features->set_num_recent_key_events(75);
   features->set_num_recent_mouse_events(235);
   features->set_num_recent_touch_events(139);
   features->set_num_recent_stylus_events(92);
-  features->set_duration_recent_video_playing(4);
-  features->set_duration_recent_audio_playing(8);
+  features->set_duration_recent_video_playing_minutes(4);
+  features->set_duration_recent_audio_playing_minutes(8);
   features->set_screen_brightness_percent(23);
-  features->set_voltage(3500);
+  features->set_voltage_mv(3500);
   features->set_halt_from_last_charge(true);
   features->set_is_charging(true);
 
@@ -82,7 +85,7 @@
       {UkmEntry::kDayOfMonthName, 27},
       {UkmEntry::kMonthName, 8},
       {UkmEntry::kTimezoneDifferenceFromLastChargeName, -5},
-      {UkmEntry::kDeviceTypeName, 3},
+      {UkmEntry::kDeviceTypeName, 4},
       {UkmEntry::kDeviceModeName, 3},
       {UkmEntry::kNumRecentKeyEventsName, 75},
       {UkmEntry::kNumRecentMouseEventsName, 235},
diff --git a/chrome/browser/ash/power/smart_charging/user_charging_event.proto b/chrome/browser/ash/power/smart_charging/user_charging_event.proto
deleted file mode 100644
index 9cf8635a..0000000
--- a/chrome/browser/ash/power/smart_charging/user_charging_event.proto
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2019 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.
-
-syntax = "proto2";
-
-package ash.power;
-
-option optimize_for = LITE_RUNTIME;
-
-// UserChargingEvent contains information about users activities that are
-// related to charging.
-message UserChargingEvent {
-  message Features {
-    enum DayOfWeek {
-      SUN = 0;
-      MON = 1;
-      TUE = 2;
-      WED = 3;
-      THU = 4;
-      FRI = 5;
-      SAT = 6;
-    }
-
-    enum Month {
-      JAN = 1;
-      FEB = 2;
-      MAR = 3;
-      APR = 4;
-      MAY = 5;
-      JUN = 6;
-      JUL = 7;
-      AUG = 8;
-      SEP = 9;
-      OCT = 10;
-      NOV = 11;
-      DEC = 12;
-    }
-
-    enum DeviceType {
-      UNKNOWN = 0;
-      LAPTOP = 1;
-      CLAMSHELL = 2;
-      TABLET = 3;
-    }
-
-    enum DeviceMode {
-      UNKNOWN_MODE = 0;
-      CLOSED_LID_MODE = 1;
-      LAPTOP_MODE = 2;
-      TABLET_MODE = 3;
-    }
-
-    // Percentage of the battery.
-    optional int32 battery_percentage = 1;
-    // Time since the last time user unplugged the charger in minutes.
-    optional int32 time_since_last_charge = 2;
-    // Duration of the last time the device was charged in minutes.
-    optional int32 duration_of_last_charge = 3;
-    // The percentage of the battery that the last charge reached.
-    optional int32 battery_percentage_of_last_charge = 4;
-    // The percentage of the battery at the beginning of the last charge.
-    optional int32 battery_percentage_before_last_charge = 5;
-    // Time of the event in minutes since midnight in the local time zone.
-    optional int32 time_of_the_day = 6;
-    // Logging event's day of week.
-    optional DayOfWeek day_of_week = 7;
-    // Logging event's day of month.
-    optional int32 day_of_month = 8;
-    // Logging event's month.
-    optional Month month = 9;
-    // Timezone difference from the last charge. It is equal to
-    // current_timezone - timezone_from_the_last_charge. The valid range of time
-    // zone value will be -12 (UTC -12:00) to +14 (UTC +14:00).
-    optional double timezone_difference_from_last_charge = 10;
-    // Type of the device.
-    optional DeviceType device_type = 11;
-    // Mode of the device.
-    optional DeviceMode device_mode = 12;
-    // Number of various events in past 30 minutes.
-    optional int32 num_recent_key_events = 13;
-    optional int32 num_recent_mouse_events = 14;
-    optional int32 num_recent_touch_events = 15;
-    optional int32 num_recent_stylus_events = 16;
-    // Duration of video and audio playing in the last 30 minutes.
-    optional int32 duration_recent_video_playing = 17;
-    optional int32 duration_recent_audio_playing = 18;
-    // Brightness of the screen in percent.
-    optional int32 screen_brightness_percent = 19;
-    // Charge voltage in mV.
-    optional int32 voltage = 20;
-    // Whether there's any shutdown/suspend action between the last charge
-    // and current event.
-    optional bool halt_from_last_charge = 21;
-    // Whether the device is being charged or not.
-    optional bool is_charging = 22;
-  }
-
-  message Event {
-    enum Reason {
-      // User plugs in the charger.
-      CHARGER_PLUGGED_IN = 1;
-      // User unplugs the charger.
-      CHARGER_UNPLUGGED = 2;
-      // Logging at a regular time interval.
-      PERIODIC_LOG = 3;
-      // Device goes into shutdown mode.
-      SHUTDOWN = 4;
-      // Device goes into suspend mode.
-      SUSPEND = 5;
-    }
-
-    // Unique number that represent the event.
-    optional int32 event_id = 1;
-    // Reason for the event.
-    optional Reason reason = 2;
-  }
-
-  optional Features features = 1;
-  optional Event event = 2;
-}
-
-// PastChargingEvents contain a list of events that have information about "past
-// charging events". It will only store the plug/unplug pair of the last charge
-// and a recent plug/halt event if any.
-message PastChargingEvents {
-  message Event {
-    // Time of the event in minutes since Windows epoch.
-    optional int32 time = 1;
-    // Battery percentage of the device.
-    optional int32 battery_percent = 2;
-    // Timezone of the device.
-    optional int32 timezone = 3;
-    // Reason for the event.
-    optional UserChargingEvent.Event.Reason reason = 4;
-  }
-  // A list containing past charging events.
-  repeated Event events = 1;
-}
diff --git a/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.cc b/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.cc
index 134c2eb..04904470 100644
--- a/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.cc
+++ b/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.cc
@@ -26,6 +26,51 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
+namespace {
+blink::mojom::CrosWindowInfoPtr CrosWindowInfo(
+    const base::UnguessableToken& id,
+    apps::InstanceRegistry& registry) {
+  auto window = blink::mojom::CrosWindowInfo::New();
+  registry.ForOneInstance(id, [&window](const apps::InstanceUpdate& update) {
+    aura::Window* target = update.Window()->GetToplevelWindow();
+    views::Widget* widget =
+        views::Widget::GetTopLevelWidgetForNativeView(target);
+    if (!target || !widget) {
+      return;
+    }
+
+    window->id = update.InstanceId();
+    window->title =
+        base::UTF16ToUTF8(widget->widget_delegate()->GetWindowTitle());
+    window->app_id = update.AppId();
+    window->bounds = target->bounds();
+
+    // Set window state (states are mutually exclusive)
+    if (widget->IsFullscreen()) {
+      window->window_state = blink::mojom::WindowState::kFullscreen;
+    } else if (widget->IsMaximized()) {
+      window->window_state = blink::mojom::WindowState::kMaximized;
+    } else if (widget->IsMinimized()) {
+      window->window_state = blink::mojom::WindowState::kMinimized;
+    } else {
+      window->window_state = blink::mojom::WindowState::kNormal;
+    }
+    // Instance registry references the activatable component of a window
+    // which itself does not have focus but contains the child focusable. To
+    // detect focus on the window, we assert that the focused window has our
+    // activatable as its top level parent
+    window->is_focused = target == aura::client::GetFocusClient(target)
+                                       ->GetFocusedWindow()
+                                       ->GetToplevelWindow();
+    window->visibility_state = widget->IsVisible()
+                                   ? blink::mojom::VisibilityState::kShown
+                                   : blink::mojom::VisibilityState::kHidden;
+  });
+
+  return window;
+}
+}  // namespace
+
 namespace ash {
 
 WindowManagementImpl::WindowManagementImpl(
@@ -43,7 +88,12 @@
 
 void WindowManagementImpl::DispatchWindowClosedEvent(
     const base::UnguessableToken& id) {
-  observer_->DispatchWindowClosedEvent(std::move(id));
+  // When dispatching close event, the update instance in the instance registry
+  // corresponding to the `id` no longer exists. We cannot use instance registry
+  // to create the CroswindowInfoPtr so we create one manually.
+  auto crosWindowInfoPtr = blink::mojom::CrosWindowInfo::New();
+  crosWindowInfoPtr->id = id;
+  observer_->DispatchWindowClosedEvent(std::move(crosWindowInfoPtr));
 }
 
 void WindowManagementImpl::DispatchAcceleratorEvent(
@@ -62,43 +112,11 @@
 
   apps::AppServiceProxy* proxy =
       apps::AppServiceProxyFactory::GetForProfile(profile);
-  proxy->InstanceRegistry().ForEachInstance(
-      [&windows](const apps::InstanceUpdate& update) {
-        auto window = blink::mojom::CrosWindowInfo::New();
-        aura::Window* target = update.Window()->GetToplevelWindow();
-        views::Widget* widget =
-            views::Widget::GetTopLevelWidgetForNativeView(target);
-        if (!target || !widget) {
-          return;
-        }
-        window->id = update.InstanceId();
-        window->title =
-            base::UTF16ToUTF8(widget->widget_delegate()->GetWindowTitle());
-        window->app_id = update.AppId();
-        window->bounds = target->bounds();
-
-        // Set window state (states are mutually exclusive)
-        if (widget->IsFullscreen()) {
-          window->window_state = blink::mojom::WindowState::kFullscreen;
-        } else if (widget->IsMaximized()) {
-          window->window_state = blink::mojom::WindowState::kMaximized;
-        } else if (widget->IsMinimized()) {
-          window->window_state = blink::mojom::WindowState::kMinimized;
-        } else {
-          window->window_state = blink::mojom::WindowState::kNormal;
-        }
-        // Instance registry references the activatable component of a window
-        // which itself does not have focus but contains the child focusable. To
-        // detect focus on the window, we assert that the focused window has our
-        // activatable as its top level parent
-        window->is_focused = target == aura::client::GetFocusClient(target)
-                                           ->GetFocusedWindow()
-                                           ->GetToplevelWindow();
-        window->visibility_state = widget->IsVisible()
-                                       ? blink::mojom::VisibilityState::kShown
-                                       : blink::mojom::VisibilityState::kHidden;
-        windows.push_back(std::move(window));
-      });
+  auto& instance_registry = proxy->InstanceRegistry();
+  instance_registry.ForEachInstance([&windows, &instance_registry](
+                                        const apps::InstanceUpdate& update) {
+    windows.push_back(CrosWindowInfo(update.InstanceId(), instance_registry));
+  });
   std::move(callback).Run(std::move(windows));
 }
 
diff --git a/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.h b/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.h
index 5a119ed8..769728ba 100644
--- a/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.h
+++ b/chrome/browser/ash/system_extensions/api/window_management/window_management_impl.h
@@ -16,7 +16,7 @@
 
 namespace aura {
 class Window;
-}
+}  // namespace aura
 
 namespace ash {
 
diff --git a/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc
index 5398a10..877aa2d 100644
--- a/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/projector_app/projector_app_integration_browsertest.cc
@@ -8,6 +8,7 @@
 #include "ash/webui/media_app_ui/test/media_app_ui_browsertest.h"
 #include "ash/webui/projector_app/buildflags.h"
 #include "ash/webui/projector_app/projector_app_client.h"
+#include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "base/run_loop.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.h"
@@ -38,7 +39,7 @@
 };
 
 IN_PROC_BROWSER_TEST_P(ProjectorAppIntegrationTest, ProjectorApp) {
-  const GURL url("chrome://projector/app/");
+  const GURL url(ash::kChromeUITrustedProjectorUrl);
   EXPECT_NO_FATAL_FAILURE(ExpectSystemWebAppValid(
       ash::SystemWebAppType::PROJECTOR, url, "Screencast"));
 }
diff --git a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
index 4dd21ac..01df6d4 100644
--- a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
@@ -30,7 +30,7 @@
 ProjectorSystemWebAppDelegate::ProjectorSystemWebAppDelegate(Profile* profile)
     : ash::SystemWebAppDelegate(ash::SystemWebAppType::PROJECTOR,
                                 "Projector",
-                                GURL(ash::kChromeUITrustedProjectorAppUrl),
+                                GURL(ash::kChromeUITrustedProjectorUrl),
                                 profile) {}
 
 ProjectorSystemWebAppDelegate::~ProjectorSystemWebAppDelegate() = default;
@@ -38,8 +38,8 @@
 std::unique_ptr<WebAppInstallInfo>
 ProjectorSystemWebAppDelegate::GetWebAppInfo() const {
   auto info = std::make_unique<WebAppInstallInfo>();
-  info->start_url = GURL(ash::kChromeUITrustedProjectorAppUrl);
-  info->scope = GURL(ash::kChromeUITrustedProjectorAppUrl);
+  info->start_url = GURL(ash::kChromeUITrustedProjectorUrl);
+  info->scope = GURL(ash::kChromeUITrustedProjectorUrl);
 
   info->title = l10n_util::GetStringUTF16(IDS_PROJECTOR_APP_NAME);
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 5bd20553..287925a 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -79,7 +79,6 @@
     ":print_job_info_proto",
     ":screen_brightness_event_proto",
     ":user_activity_event_proto",
-    ":user_charging_event_proto",
     "../ash/guest_os:guest_os_diagnostics_mojom",
     "//apps",
     "//ash",
@@ -3787,10 +3786,6 @@
   sources = [ "../ash/power/ml/screen_brightness_event.proto" ]
 }
 
-proto_library("user_charging_event_proto") {
-  sources = [ "../ash/power/smart_charging/user_charging_event.proto" ]
-}
-
 proto_library("user_activity_event_proto") {
   sources = [ "../ash/power/ml/user_activity_event.proto" ]
 }
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 7c60690..42dd95c45 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -876,9 +876,9 @@
 // false if optional display id is given but in bad format. Otherwise returns
 // true and fills |display_id| with either the primary display id when the
 // optional arg is not given or the parsed display id out of the arg
-bool GetDisplayIdFromOptionalArg(const std::unique_ptr<std::string>& arg,
+bool GetDisplayIdFromOptionalArg(const absl::optional<std::string>& arg,
                                  int64_t* display_id) {
-  if (arg.get() && !arg->empty()) {
+  if (arg && !arg->empty()) {
     return base::StringToInt64(*arg, display_id);
   }
 
@@ -4428,8 +4428,7 @@
         static_cast<int>(ash::AppType::ARC_APP)) {
       std::string* package_name = window->GetProperty(ash::kArcPackageNameKey);
       if (package_name) {
-        window_info.arc_package_name =
-            std::make_unique<std::string>(*package_name);
+        window_info.arc_package_name = *package_name;
       } else {
         LOG(ERROR) << "The package name for window " << window->GetTitle()
                    << " (ID: " << window->GetId()
@@ -4439,12 +4438,11 @@
     std::string* full_restore_window_app_id =
         window->GetProperty(app_restore::kAppIdKey);
     if (full_restore_window_app_id) {
-      window_info.full_restore_window_app_id =
-          std::make_unique<std::string>(*full_restore_window_app_id);
+      window_info.full_restore_window_app_id = *full_restore_window_app_id;
     }
     std::string* app_id = window->GetProperty(ash::kAppIDKey);
     if (app_id)
-      window_info.app_id = std::make_unique<std::string>(*app_id);
+      window_info.app_id = *app_id;
 
     auto* widget = views::Widget::GetWidgetForNativeWindow(window);
     // Frame information
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.cc b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
index 4657693..5455998 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
@@ -797,9 +797,8 @@
   file_manager_private::CopyOrMoveProgressStatus status;
   // Send started event.
   status.type = file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_BEGIN;
-  status.source_url = std::make_unique<std::string>(source_url.spec());
-  status.destination_url =
-      std::make_unique<std::string>(destination_url.spec());
+  status.source_url = source_url.spec();
+  status.destination_url = destination_url.spec();
   // Use the bytes copied member to store space needed for this event.
   status.size = space_needed;
 
@@ -817,13 +816,12 @@
     // Send success event.
     status.type =
         file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_SUCCESS;
-    status.source_url = std::make_unique<std::string>(source_url.spec());
-    status.destination_url =
-        std::make_unique<std::string>(destination_url.spec());
+    status.source_url = source_url.spec();
+    status.destination_url = destination_url.spec();
   } else {
     // Send error event.
     status.type = file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_ERROR;
-    status.error = std::make_unique<std::string>(FileErrorToErrorName(error));
+    status.error = FileErrorToErrorName(error);
   }
 
   notification_manager_->HandleCopyEvent(copy_id, status);
@@ -843,22 +841,19 @@
 
   file_manager_private::CopyOrMoveProgressStatus status;
   status.type = CopyOrMoveProgressTypeToCopyOrMoveProgressStatusType(type);
-  status.source_url = std::make_unique<std::string>(source_url.spec());
+  status.source_url = source_url.spec();
   if (type == FileManagerCopyOrMoveHookDelegate::ProgressType::kError) {
     // For cross-filesystems moves, no destination_url is provided when an error
     // occurs. This translates into to a non-valid destination GURL.
     // status.destination_url should never be used in this case.
-    status.destination_url =
-        std::make_unique<std::string>(destination_url.possibly_invalid_spec());
+    status.destination_url = destination_url.possibly_invalid_spec();
   } else if (type != FileManagerCopyOrMoveHookDelegate::ProgressType::
                          kEndRemoveSource) {
-    status.destination_url =
-        std::make_unique<std::string>(destination_url.spec());
+    status.destination_url = destination_url.spec();
   }
 
   if (type == FileManagerCopyOrMoveHookDelegate::ProgressType::kError) {
-    status.error = std::make_unique<std::string>(
-        FileErrorToErrorName(base::File::FILE_ERROR_FAILED));
+    status.error = FileErrorToErrorName(base::File::FILE_ERROR_FAILED);
   }
   if (type == FileManagerCopyOrMoveHookDelegate::ProgressType::kProgress)
     status.size = size;
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
index 3b8e5e01..87ef3e1 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
@@ -248,8 +248,7 @@
         handler->package_name, handler->activity_name));
     if (it != icons->end()) {
       app.icon_set = std::make_unique<api::file_manager_private::IconSet>();
-      app.icon_set->icon32x32_url =
-          std::make_unique<std::string>(it->second.icon16_dataurl->data.spec());
+      app.icon_set->icon32x32_url = it->second.icon16_dataurl->data.spec();
     }
     results.push_back(std::move(app));
   }
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index b1dfb2d..2f1ad68 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -193,16 +193,14 @@
             api::file_manager_private::ENTRY_PROPERTY_NAME_CONTENTMIMETYPE) !=
             names_.end() &&
         metadata->mime_type.get()) {
-      properties_->content_mime_type =
-          std::make_unique<std::string>(*metadata->mime_type);
+      properties_->content_mime_type = *metadata->mime_type;
     }
 
     if (names_.find(
             api::file_manager_private::ENTRY_PROPERTY_NAME_THUMBNAILURL) !=
             names_.end() &&
         metadata->thumbnail.get()) {
-      properties_->thumbnail_url =
-          std::make_unique<std::string>(*metadata->thumbnail);
+      properties_->thumbnail_url = *metadata->thumbnail;
     }
 
     CompleteGetEntryProperties(base::File::FILE_OK);
@@ -521,8 +519,8 @@
   DCHECK(0 <= processed_count_ && processed_count_ < properties_list_.size());
 
   if (error == base::File::FILE_OK) {
-    properties->external_file_url = std::make_unique<std::string>(
-        chromeos::FileSystemURLToExternalFileURL(url).spec());
+    properties->external_file_url =
+        chromeos::FileSystemURLToExternalFileURL(url).spec();
   }
   properties_list_[index] = std::move(*properties);
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_media_parser_util.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_media_parser_util.cc
index 1729617..72d984c 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_media_parser_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_media_parser_util.cc
@@ -14,13 +14,6 @@
 namespace {
 
 template <class T>
-void SetValueScopedPtr(T value, std::unique_ptr<T>* destination) {
-  DCHECK(destination);
-  if (value >= 0)
-    destination->reset(new T(value));
-}
-
-template <class T>
 void SetValueOptional(T value, absl::optional<T>* destination) {
   DCHECK(destination);
   if (value >= 0)
@@ -28,11 +21,11 @@
 }
 
 template <>
-void SetValueScopedPtr(std::string value,
-                       std::unique_ptr<std::string>* destination) {
+void SetValueOptional(std::string value,
+                      absl::optional<std::string>* destination) {
   DCHECK(destination);
   if (!value.empty())
-    *destination = std::make_unique<std::string>(std::move(value));
+    *destination = std::move(value);
 }
 
 void ChangeAudioMimePrefixToVideo(std::string* mime_type) {
@@ -65,14 +58,14 @@
 
   SetValueOptional(metadata->duration, &media_metadata.duration);
   SetValueOptional(metadata->rotation, &media_metadata.rotation);
-  SetValueScopedPtr(std::move(metadata->artist), &media_metadata.artist);
-  SetValueScopedPtr(std::move(metadata->album), &media_metadata.album);
-  SetValueScopedPtr(std::move(metadata->comment), &media_metadata.comment);
-  SetValueScopedPtr(std::move(metadata->copyright), &media_metadata.copyright);
+  SetValueOptional(std::move(metadata->artist), &media_metadata.artist);
+  SetValueOptional(std::move(metadata->album), &media_metadata.album);
+  SetValueOptional(std::move(metadata->comment), &media_metadata.comment);
+  SetValueOptional(std::move(metadata->copyright), &media_metadata.copyright);
   SetValueOptional(metadata->disc, &media_metadata.disc);
-  SetValueScopedPtr(std::move(metadata->genre), &media_metadata.genre);
-  SetValueScopedPtr(std::move(metadata->language), &media_metadata.language);
-  SetValueScopedPtr(std::move(metadata->title), &media_metadata.title);
+  SetValueOptional(std::move(metadata->genre), &media_metadata.genre);
+  SetValueOptional(std::move(metadata->language), &media_metadata.language);
+  SetValueOptional(std::move(metadata->title), &media_metadata.title);
   SetValueOptional(metadata->track, &media_metadata.track);
 
   for (const chrome::mojom::MediaStreamInfoPtr& info : metadata->raw_tags) {
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index 4d3662ca..a19c72e 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -1046,9 +1046,8 @@
 
   result.name = linux_package_info.name;
   result.version = linux_package_info.version;
-  result.summary = std::make_unique<std::string>(linux_package_info.summary);
-  result.description =
-      std::make_unique<std::string>(linux_package_info.description);
+  result.summary = linux_package_info.summary;
+  result.description = linux_package_info.description;
 
   Respond(ArgumentList(extensions::api::file_manager_private_internal::
                            GetLinuxPackageInfo::Results::Create(result)));
@@ -1145,7 +1144,7 @@
   for (const auto& action : actions) {
     Action item;
     item.id = action.id;
-    item.title = std::make_unique<std::string>(action.title);
+    item.title = action.title;
     items.push_back(std::move(item));
   }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
index 570776a..911e941 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
@@ -203,8 +203,7 @@
   GetSelectedFileInfoInternal(profile, std::move(params));
 }
 
-std::unique_ptr<std::string> GetShareUrlFromAlternateUrl(
-    const GURL& alternate_url) {
+std::string GetShareUrlFromAlternateUrl(const GURL& alternate_url) {
   // Set |share_url| to a modified version of |alternate_url| that opens the
   // sharing dialog for files and folders (add ?userstoinvite="" to the URL).
   GURL::Replacements replacements;
@@ -213,8 +212,7 @@
       "userstoinvite=%22%22";
   replacements.SetQueryStr(new_query);
 
-  return std::make_unique<std::string>(
-      alternate_url.ReplaceComponents(replacements).spec());
+  return alternate_url.ReplaceComponents(replacements).spec();
 }
 
 extensions::api::file_manager_private::VmType VmTypeToJs(
@@ -331,16 +329,13 @@
         metadata->last_viewed_by_me_time.ToJsTime();
   }
   if (!metadata->content_mime_type.empty()) {
-    properties_->content_mime_type =
-        std::make_unique<std::string>(metadata->content_mime_type);
+    properties_->content_mime_type = metadata->content_mime_type;
   }
   if (!metadata->custom_icon_url.empty()) {
-    properties_->custom_icon_url =
-        std::make_unique<std::string>(std::move(metadata->custom_icon_url));
+    properties_->custom_icon_url = std::move(metadata->custom_icon_url);
   }
   if (!metadata->alternate_url.empty()) {
-    properties_->alternate_url =
-        std::make_unique<std::string>(std::move(metadata->alternate_url));
+    properties_->alternate_url = std::move(metadata->alternate_url);
     properties_->share_url =
         GetShareUrlFromAlternateUrl(GURL(*properties_->alternate_url));
   }
@@ -364,10 +359,9 @@
       metadata->can_pin == drivefs::mojom::FileMetadata::CanPinStatus::kOk;
 
   if (drivefs::IsAFile(metadata->type)) {
-    properties_->thumbnail_url = std::make_unique<std::string>(
-        base::StrCat({"drivefs:", file_system_url_.ToGURL().spec()}));
-    properties_->cropped_thumbnail_url =
-        std::make_unique<std::string>(*properties_->thumbnail_url);
+    properties_->thumbnail_url =
+        base::StrCat({"drivefs:", file_system_url_.ToGURL().spec()});
+    properties_->cropped_thumbnail_url = *properties_->thumbnail_url;
   }
 
   if (metadata->folder_feature) {
@@ -396,12 +390,10 @@
   DCHECK(output);
   using ash::file_system_provider::IconSet;
   if (input.HasIcon(IconSet::IconSize::SIZE_16x16)) {
-    output->icon16x16_url = std::make_unique<std::string>(
-        input.GetIcon(IconSet::IconSize::SIZE_16x16).spec());
+    output->icon16x16_url = input.GetIcon(IconSet::IconSize::SIZE_16x16).spec();
   }
   if (input.HasIcon(IconSet::IconSize::SIZE_32x32)) {
-    output->icon32x32_url = std::make_unique<std::string>(
-        input.GetIcon(IconSet::IconSize::SIZE_32x32).spec());
+    output->icon32x32_url = input.GetIcon(IconSet::IconSize::SIZE_32x32).spec();
   }
 }
 
@@ -419,12 +411,10 @@
   volume_metadata->profile.is_current_profile = true;
 
   if (!volume.source_path().empty()) {
-    volume_metadata->source_path =
-        std::make_unique<std::string>(volume.source_path().AsUTF8Unsafe());
+    volume_metadata->source_path = volume.source_path().AsUTF8Unsafe();
   }
   if (!volume.remote_mount_path().empty()) {
-    volume_metadata->remote_mount_path =
-        std::make_unique<std::string>(volume.remote_mount_path().value());
+    volume_metadata->remote_mount_path = volume.remote_mount_path().value();
   }
 
   switch (volume.source()) {
@@ -450,20 +440,15 @@
   volume_metadata->watchable = volume.watchable();
 
   if (volume.type() == VOLUME_TYPE_PROVIDED) {
-    volume_metadata->provider_id =
-        std::make_unique<std::string>(volume.provider_id().ToString());
-    volume_metadata->file_system_id =
-        std::make_unique<std::string>(volume.file_system_id());
+    volume_metadata->provider_id = volume.provider_id().ToString();
+    volume_metadata->file_system_id = volume.file_system_id();
   }
 
   FillIconSet(&volume_metadata->icon_set, volume.icon_set());
 
-  volume_metadata->volume_label =
-      std::make_unique<std::string>(volume.volume_label());
-  volume_metadata->disk_file_system_type =
-      std::make_unique<std::string>(volume.file_system_type());
-  volume_metadata->drive_label =
-      std::make_unique<std::string>(volume.drive_label());
+  volume_metadata->volume_label = volume.volume_label();
+  volume_metadata->disk_file_system_type = volume.file_system_type();
+  volume_metadata->drive_label = volume.drive_label();
 
   switch (volume.type()) {
     case VOLUME_TYPE_GOOGLE_DRIVE:
@@ -543,8 +528,7 @@
         volume_metadata->device_type = file_manager_private::DEVICE_TYPE_MOBILE;
         break;
     }
-    volume_metadata->device_path = std::make_unique<std::string>(
-        volume.storage_device_path().AsUTF8Unsafe());
+    volume_metadata->device_path = volume.storage_device_path().AsUTF8Unsafe();
     volume_metadata->is_parent_device = volume.is_parent();
   } else {
     volume_metadata->device_type =
diff --git a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc
index 720c9932..cb72043 100644
--- a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc
@@ -1092,11 +1092,11 @@
   std::string copy_file_dest_url =
       "filesystem:chrome://file-manager/external/Downloads-test-user/NewFolder/"
       "file.txt";
-  status.destination_url = std::make_unique<std::string>(copy_file_dest_url);
+  status.destination_url = copy_file_dest_url;
   status.size = copy_size;
   std::string copy_file_src_url =
       "filesystem:chrome://file-manager/external/Downloads-test-user/file.txt";
-  status.source_url = std::make_unique<std::string>(copy_file_src_url);
+  status.source_url = copy_file_src_url;
   status.type = file_manager_private::COPY_OR_MOVE_PROGRESS_STATUS_TYPE_BEGIN;
 
   // Send the copy begin event.
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc
index c6565ecb..67395cf 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc
+++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc
@@ -58,7 +58,7 @@
     watcher_item.entry_path = watcher->entry_path.value();
     watcher_item.recursive = watcher->recursive;
     if (!watcher->last_tag.empty()) {
-      watcher_item.last_tag = std::make_unique<std::string>(watcher->last_tag);
+      watcher_item.last_tag = watcher->last_tag;
     }
     item.watchers.push_back(std::move(watcher_item));
   }
diff --git a/chrome/browser/chromeos/extensions/ime_menu_event_router.cc b/chrome/browser/chromeos/extensions/ime_menu_event_router.cc
index 8650d735..19bace6f 100644
--- a/chrome/browser/chromeos/extensions/ime_menu_event_router.cc
+++ b/chrome/browser/chromeos/extensions/ime_menu_event_router.cc
@@ -74,7 +74,7 @@
   for (const auto& item : items) {
     input_method_private::MenuItem menu_item;
     menu_item.id = item.id;
-    menu_item.label = std::make_unique<std::string>(item.label);
+    menu_item.label = item.label;
     switch (item.style) {
       case input_method::InputMethodManager::MENU_ITEM_STYLE_CHECK:
         menu_item.style = input_method_private::ParseMenuItemStyle("check");
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.cc b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.cc
index cad6212e..25251ac1 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.cc
@@ -104,8 +104,7 @@
   result.progress_percent = ptr->progress_percent;
 
   if (ptr->output.has_value() && !ptr->output.value().empty()) {
-    result.output =
-        std::make_unique<std::string>(std::move(ptr->output.value()));
+    result.output = std::move(ptr->output);
   }
 
   switch (ptr->routine_update_union->which()) {
@@ -169,18 +168,13 @@
     return;
   }
 
-  absl::optional<std::string> expected_power_type = absl::nullopt;
-  if (params->request.expected_power_type) {
-    expected_power_type = *params->request.expected_power_type.get();
-  }
-
   auto cb =
       base::BindOnce(&DiagnosticsApiRunRoutineFunctionBase::OnResult, this);
 
   GetRemoteService()->RunAcPowerRoutine(
       converters::ConvertAcPowerStatusRoutineType(
           params->request.expected_status),
-      expected_power_type, std::move(cb));
+      params->request.expected_power_type, std::move(cb));
 }
 
 // OsDiagnosticsRunBatteryCapacityRoutineFunction ------------------------------
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.cc b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.cc
index cb362c9a..5fd6b69 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.cc
@@ -63,12 +63,10 @@
   auto& battery_info = ptr->battery_result->get_battery_info();
 
   // Protect accessing the serial number by a permission.
-  std::unique_ptr<std::string> serial_number;
+  absl::optional<std::string> serial_number;
   if (extension()->permissions_data()->HasAPIPermission(
-          extensions::mojom::APIPermissionID::kChromeOSTelemetrySerialNumber) &&
-      battery_info->serial_number.has_value()) {
-    serial_number = std::make_unique<std::string>(
-        std::move(battery_info->serial_number.value()));
+          extensions::mojom::APIPermissionID::kChromeOSTelemetrySerialNumber)) {
+    serial_number = std::move(battery_info->serial_number);
   }
 
   api::os_telemetry::BatteryInfo result =
@@ -184,8 +182,7 @@
   }
 
   api::os_telemetry::OemData result;
-  result.oem_data =
-      std::make_unique<std::string>(std::move(ptr->oem_data.value()));
+  result.oem_data = std::move(ptr->oem_data);
 
   Respond(ArgumentList(api::os_telemetry::GetOemData::Results::Create(result)));
 }
@@ -283,25 +280,14 @@
   api::os_telemetry::VpdInfo result;
 
   const auto& vpd_info = ptr->vpd_result->get_vpd_info();
-  if (vpd_info->first_power_date.has_value()) {
-    result.activate_date =
-        std::make_unique<std::string>(vpd_info->first_power_date.value());
-  }
-  if (vpd_info->model_name.has_value()) {
-    result.model_name =
-        std::make_unique<std::string>(vpd_info->model_name.value());
-  }
-  if (vpd_info->sku_number.has_value()) {
-    result.sku_number =
-        std::make_unique<std::string>(vpd_info->sku_number.value());
-  }
+  result.activate_date = vpd_info->first_power_date;
+  result.model_name = vpd_info->model_name;
+  result.sku_number = vpd_info->sku_number;
 
   // Protect accessing the serial number by a permission.
   if (extension()->permissions_data()->HasAPIPermission(
-          extensions::mojom::APIPermissionID::kChromeOSTelemetrySerialNumber) &&
-      vpd_info->serial_number.has_value()) {
-    result.serial_number =
-        std::make_unique<std::string>(vpd_info->serial_number.value());
+          extensions::mojom::APIPermissionID::kChromeOSTelemetrySerialNumber)) {
+    result.serial_number = vpd_info->serial_number;
   }
 
   Respond(ArgumentList(api::os_telemetry::GetVpdInfo::Results::Create(result)));
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.cc b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.cc
index 7fa8e5e6..9bbc322 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.cc
@@ -30,9 +30,7 @@
 telemetry_api::CpuCStateInfo UncheckedConvertPtr(
     telemetry_service::ProbeCpuCStateInfoPtr input) {
   telemetry_api::CpuCStateInfo result;
-  if (input->name.has_value()) {
-    result.name = std::make_unique<std::string>(input->name.value());
-  }
+  result.name = input->name;
   if (input->time_in_state_since_last_boot_us) {
     result.time_in_state_since_last_boot_us =
         input->time_in_state_since_last_boot_us->value;
@@ -64,10 +62,7 @@
 telemetry_api::PhysicalCpuInfo UncheckedConvertPtr(
     telemetry_service::ProbePhysicalCpuInfoPtr input) {
   telemetry_api::PhysicalCpuInfo result;
-  if (input->model_name.has_value()) {
-    result.model_name =
-        std::make_unique<std::string>(input->model_name.value());
-  }
+  result.model_name = input->model_name;
   result.logical_cpus = ConvertPtrVector<telemetry_api::LogicalCpuInfo>(
       std::move(input->logical_cpus));
   return result;
@@ -76,22 +71,10 @@
 telemetry_api::BatteryInfo UncheckedConvertPtr(
     telemetry_service::ProbeBatteryInfoPtr input) {
   telemetry_api::BatteryInfo result;
-  if (input->vendor.has_value()) {
-    result.vendor =
-        std::make_unique<std::string>(std::move(input->vendor.value()));
-  }
-  if (input->model_name.has_value()) {
-    result.model_name =
-        std::make_unique<std::string>(std::move(input->model_name.value()));
-  }
-  if (input->technology.has_value()) {
-    result.technology =
-        std::make_unique<std::string>(std::move(input->technology.value()));
-  }
-  if (input->status.has_value()) {
-    result.status =
-        std::make_unique<std::string>(std::move(input->status.value()));
-  }
+  result.vendor = std::move(input->vendor);
+  result.model_name = std::move(input->model_name);
+  result.technology = std::move(input->technology);
+  result.status = std::move(input->status);
   if (input->cycle_count) {
     result.cycle_count = input->cycle_count->value;
   }
@@ -116,10 +99,7 @@
   if (input->temperature) {
     result.temperature = input->temperature->value;
   }
-  if (input->manufacture_date.has_value()) {
-    result.manufacture_date =
-        std::make_unique<std::string>(input->manufacture_date.value());
-  }
+  result.manufacture_date = std::move(input->manufacture_date);
 
   return result;
 }
@@ -128,25 +108,10 @@
     telemetry_service::ProbeOsVersionPtr input) {
   telemetry_api::OsVersionInfo result;
 
-  if (input->release_milestone) {
-    result.release_milestone =
-        std::make_unique<std::string>(input->release_milestone.value());
-  }
-
-  if (input->build_number) {
-    result.build_number =
-        std::make_unique<std::string>(input->build_number.value());
-  }
-
-  if (input->patch_number) {
-    result.patch_number =
-        std::make_unique<std::string>(input->patch_number.value());
-  }
-
-  if (input->release_channel) {
-    result.release_channel =
-        std::make_unique<std::string>(input->release_channel.value());
-  }
+  result.release_milestone = input->release_milestone;
+  result.build_number = input->build_number;
+  result.patch_number = input->patch_number;
+  result.release_channel = input->release_channel;
 
   return result;
 }
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc
index 3a50c87..ce060645 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc
@@ -198,7 +198,7 @@
   ASSERT_TRUE(result.vendor);
   EXPECT_EQ(kVendor, *result.vendor);
   // serial_number is not converted in ConvertPtr().
-  EXPECT_TRUE(result.serial_number == nullptr);
+  EXPECT_FALSE(result.serial_number);
 
   ASSERT_TRUE(result.charge_full_design);
   EXPECT_EQ(kChargeFullDesign,
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index 07bbbd0..c9912c3 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -1501,17 +1501,10 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DownloadItem* item = download_manager_->GetDownload(download_id);
   if (item) {
-    if (!target_info->target_path.empty() &&
-        IsOpenInBrowserPreferreredForFile(target_info->target_path) &&
-        target_info->is_filetype_handled_safely)
-      DownloadItemModel(item).SetShouldPreferOpeningInBrowser(true);
-
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-    if (item->GetOriginalMimeType() == "application/x-x509-user-cert")
-      DownloadItemModel(item).SetShouldPreferOpeningInBrowser(true);
-#endif
-
-    DownloadItemModel(item).SetDangerLevel(target_info->danger_level);
+    DownloadItemModel model(item);
+    model.DetermineAndSetShouldPreferOpeningInBrowser(
+        target_info->target_path, target_info->is_filetype_handled_safely);
+    model.SetDangerLevel(target_info->danger_level);
   }
   if (ShouldBlockFile(item, target_info->danger_type)) {
     MaybeReportDangerousDownloadBlocked(
diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h
index d994ade8..b10d4ec 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.h
+++ b/chrome/browser/download/chrome_download_manager_delegate.h
@@ -192,6 +192,9 @@
   void ScheduleCancelForEphemeralWarning(const std::string& guid);
 #endif
 
+  // Returns true if |path| should open in the browser.
+  virtual bool IsOpenInBrowserPreferreredForFile(const base::FilePath& path);
+
  protected:
   virtual safe_browsing::DownloadProtectionService*
       GetDownloadProtectionService();
@@ -288,9 +291,6 @@
       content::DownloadTargetCallback callback,
       std::unique_ptr<DownloadTargetInfo> target_info);
 
-  // Returns true if |path| should open in the browser.
-  bool IsOpenInBrowserPreferreredForFile(const base::FilePath& path);
-
   void MaybeSendDangerousDownloadOpenedReport(download::DownloadItem* download,
                                               bool show_download_in_folder);
 
diff --git a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
index 5370484..a766255 100644
--- a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
@@ -28,6 +28,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/download/download_core_service_factory.h"
+#include "chrome/browser/download/download_core_service_impl.h"
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/download/download_target_info.h"
@@ -247,6 +249,37 @@
   friend class ChromeDownloadManagerDelegateTest;
 };
 
+// A DownloadCoreService that returns the TestChromeDownloadManagerDelegate.
+class TestDownloadCoreService : public DownloadCoreServiceImpl {
+ public:
+  explicit TestDownloadCoreService(Profile* profile);
+  ~TestDownloadCoreService() override;
+
+  void set_download_manager_delegate(ChromeDownloadManagerDelegate* delegate) {
+    delegate_ = delegate;
+  }
+
+  ChromeDownloadManagerDelegate* GetDownloadManagerDelegate() override;
+
+  raw_ptr<ChromeDownloadManagerDelegate> delegate_;
+};
+
+TestDownloadCoreService::TestDownloadCoreService(Profile* profile)
+    : DownloadCoreServiceImpl(profile) {}
+
+TestDownloadCoreService::~TestDownloadCoreService() = default;
+
+ChromeDownloadManagerDelegate*
+TestDownloadCoreService::GetDownloadManagerDelegate() {
+  return delegate_;
+}
+
+static std::unique_ptr<KeyedService> CreateTestDownloadCoreService(
+    content::BrowserContext* browser_context) {
+  return std::make_unique<TestDownloadCoreService>(
+      Profile::FromBrowserContext(browser_context));
+}
+
 class ChromeDownloadManagerDelegateTest
     : public ChromeRenderViewHostTestHarness {
  public:
@@ -327,6 +360,11 @@
   delegate_ =
       std::make_unique<::testing::NiceMock<TestChromeDownloadManagerDelegate>>(
           profile());
+  DownloadCoreServiceFactory::GetInstance()->SetTestingFactory(
+      profile(), base::BindRepeating(&CreateTestDownloadCoreService));
+  static_cast<TestDownloadCoreService*>(
+      DownloadCoreServiceFactory::GetForBrowserContext(profile()))
+      ->set_download_manager_delegate(delegate_.get());
   download_prefs()->SkipSanitizeDownloadTargetPathForTesting();
   download_prefs()->SetDownloadPath(test_download_dir_);
   delegate_->SetDownloadManager(download_manager_.get());
diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc
index 5cff6077..f2290f1 100644
--- a/chrome/browser/download/download_item_model.cc
+++ b/chrome/browser/download/download_item_model.cc
@@ -12,6 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/observer_list.h"
+#include "base/run_loop.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/supports_user_data.h"
@@ -28,6 +29,7 @@
 #include "chrome/browser/download/download_history.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/download/download_stats.h"
+#include "chrome/browser/download/download_target_determiner.h"
 #include "chrome/browser/download/offline_item_utils.h"
 #include "chrome/browser/enterprise/connectors/common.h"
 #include "chrome/browser/enterprise/connectors/connectors_manager.h"
@@ -93,7 +95,7 @@
 
   // Whether the download should be opened in the browser vs. the system handler
   // for the file type.
-  bool should_prefer_opening_in_browser_;
+  absl::optional<bool> should_prefer_opening_in_browser_;
 
   // Danger level of the file determined based on the file type and whether
   // there was a user action associated with the download.
@@ -142,7 +144,6 @@
 DownloadItemModelData::DownloadItemModelData()
     : should_show_in_shelf_(true),
       was_ui_notified_(false),
-      should_prefer_opening_in_browser_(false),
       danger_level_(DownloadFileType::NOT_DANGEROUS),
       is_being_revived_(false) {}
 
@@ -434,9 +435,29 @@
   data->ephemeral_warning_ui_shown_time_ = ephemeral_warning_ui_shown_time;
 }
 
-bool DownloadItemModel::ShouldPreferOpeningInBrowser() const {
-  const DownloadItemModelData* data = DownloadItemModelData::Get(download_);
-  return data && data->should_prefer_opening_in_browser_;
+bool DownloadItemModel::ShouldPreferOpeningInBrowser() {
+  const DownloadItemModelData* data =
+      DownloadItemModelData::GetOrCreate(download_);
+#if !BUILDFLAG(IS_ANDROID)
+  if (!data->should_prefer_opening_in_browser_ && IsBubbleV2Enabled()) {
+    base::FilePath path = GetTargetFilePath();
+    std::string mime_type = GetMimeType();
+    base::RunLoop run_loop;
+    DownloadTargetDeterminer::DetermineIfHandledSafelyHelper(
+        download_, path, mime_type,
+        base::BindOnce(
+            [](base::OnceClosure quit_run_loop,
+               base::WeakPtr<DownloadUIModel> model, const base::FilePath& path,
+               bool is_handled_safely) {
+              model->DetermineAndSetShouldPreferOpeningInBrowser(
+                  path, is_handled_safely);
+              std::move(quit_run_loop).Run();
+            },
+            run_loop.QuitClosure(), GetWeakPtr(), path));
+    run_loop.Run();
+  }
+#endif  // !BUILDFLAG(IS_ANDROID)
+  return data->should_prefer_opening_in_browser_.value_or(false);
 }
 
 void DownloadItemModel::SetShouldPreferOpeningInBrowser(bool preference) {
@@ -1044,3 +1065,33 @@
 
   return true;
 }
+
+void DownloadItemModel::DetermineAndSetShouldPreferOpeningInBrowser(
+    const base::FilePath& target_path,
+    bool is_filetype_handled_safely) {
+  DownloadCoreService* download_core_service =
+      DownloadCoreServiceFactory::GetForBrowserContext(
+          content::DownloadItemUtils::GetBrowserContext(download_));
+  if (!download_core_service)
+    return;
+
+  ChromeDownloadManagerDelegate* delegate =
+      download_core_service->GetDownloadManagerDelegate();
+  if (!delegate)
+    return;
+
+  if (!target_path.empty() &&
+      delegate->IsOpenInBrowserPreferreredForFile(target_path) &&
+      is_filetype_handled_safely) {
+    SetShouldPreferOpeningInBrowser(true);
+    return;
+  }
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+  if (download_->GetOriginalMimeType() == "application/x-x509-user-cert") {
+    SetShouldPreferOpeningInBrowser(true);
+    return;
+  }
+#endif
+  SetShouldPreferOpeningInBrowser(false);
+}
diff --git a/chrome/browser/download/download_item_model.h b/chrome/browser/download/download_item_model.h
index 9351923..73b7a22 100644
--- a/chrome/browser/download/download_item_model.h
+++ b/chrome/browser/download/download_item_model.h
@@ -69,7 +69,7 @@
   void SetWasUIWarningShown(bool should_notify) override;
   absl::optional<base::Time> GetEphemeralWarningUiShownTime() const override;
   void SetEphemeralWarningUiShownTime(absl::optional<base::Time> time) override;
-  bool ShouldPreferOpeningInBrowser() const override;
+  bool ShouldPreferOpeningInBrowser() override;
   void SetShouldPreferOpeningInBrowser(bool preference) override;
   safe_browsing::DownloadFileType::DangerLevel GetDangerLevel() const override;
   void SetDangerLevel(
@@ -126,6 +126,9 @@
 #endif
 
   bool ShouldShowDropdown() const override;
+  void DetermineAndSetShouldPreferOpeningInBrowser(
+      const base::FilePath& target_path,
+      bool is_filetype_handled_safely) override;
 
   // download::DownloadItem::Observer implementation.
   void OnDownloadUpdated(download::DownloadItem* download) override;
diff --git a/chrome/browser/download/download_item_model_unittest.cc b/chrome/browser/download/download_item_model_unittest.cc
index 5646432..05ca350 100644
--- a/chrome/browser/download/download_item_model_unittest.cc
+++ b/chrome/browser/download/download_item_model_unittest.cc
@@ -20,7 +20,10 @@
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "chrome/browser/download/chrome_download_manager_delegate.h"
 #include "chrome/browser/download/download_commands.h"
+#include "chrome/browser/download/download_core_service_factory.h"
+#include "chrome/browser/download/download_core_service_impl.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -98,6 +101,55 @@
 }
 const RerouteInfo kTestRerouteInfo = MakeTestRerouteInfo(kTestProvider);
 
+// A DownloadCoreService that returns the TestChromeDownloadManagerDelegate.
+class TestDownloadCoreService : public DownloadCoreServiceImpl {
+ public:
+  explicit TestDownloadCoreService(Profile* profile);
+  ~TestDownloadCoreService() override;
+
+  void set_download_manager_delegate(ChromeDownloadManagerDelegate* delegate) {
+    delegate_ = delegate;
+  }
+
+  ChromeDownloadManagerDelegate* GetDownloadManagerDelegate() override;
+
+  raw_ptr<ChromeDownloadManagerDelegate> delegate_;
+};
+
+TestDownloadCoreService::TestDownloadCoreService(Profile* profile)
+    : DownloadCoreServiceImpl(profile) {}
+
+TestDownloadCoreService::~TestDownloadCoreService() = default;
+
+ChromeDownloadManagerDelegate*
+TestDownloadCoreService::GetDownloadManagerDelegate() {
+  return delegate_;
+}
+
+static std::unique_ptr<KeyedService> CreateTestDownloadCoreService(
+    content::BrowserContext* browser_context) {
+  return std::make_unique<TestDownloadCoreService>(
+      Profile::FromBrowserContext(browser_context));
+}
+
+class TestChromeDownloadManagerDelegate : public ChromeDownloadManagerDelegate {
+ public:
+  explicit TestChromeDownloadManagerDelegate(Profile* profile)
+      : ChromeDownloadManagerDelegate(profile) {}
+  ~TestChromeDownloadManagerDelegate() override;
+
+  // ChromeDownloadManagerDelegate override:
+  bool IsOpenInBrowserPreferreredForFile(const base::FilePath& path) override;
+};
+
+TestChromeDownloadManagerDelegate::~TestChromeDownloadManagerDelegate() =
+    default;
+
+bool TestChromeDownloadManagerDelegate::IsOpenInBrowserPreferreredForFile(
+    const base::FilePath& path) {
+  return true;
+}
+
 }  // namespace
 
 class DownloadItemModelTest : public testing::Test {
@@ -117,6 +169,13 @@
   void SetUp() override {
     ASSERT_TRUE(testing_profile_manager_.SetUp());
     profile_ = testing_profile_manager_.CreateTestingProfile("testing_profile");
+    delegate_ =
+        std::make_unique<NiceMock<TestChromeDownloadManagerDelegate>>(profile_);
+    DownloadCoreServiceFactory::GetInstance()->SetTestingFactory(
+        profile_, base::BindRepeating(&CreateTestDownloadCoreService));
+    static_cast<TestDownloadCoreService*>(
+        DownloadCoreServiceFactory::GetForBrowserContext(profile_))
+        ->set_download_manager_delegate(delegate_.get());
   }
 
  protected:
@@ -198,6 +257,7 @@
   base::SimpleTestClock clock_;
   TestingProfileManager testing_profile_manager_;
   raw_ptr<TestingProfile> profile_;
+  std::unique_ptr<NiceMock<TestChromeDownloadManagerDelegate>> delegate_;
 
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -740,6 +800,13 @@
 
 #if !BUILDFLAG(IS_ANDROID)
 
+TEST_F(DownloadItemModelTest, ShouldPreferOpeningInBrowser_V2On) {
+  SetupDownloadItemDefaults();
+  SetupCompletedDownloadItem(base::Hours(1));
+  SetIsBubbleV2Enabled(true);
+  EXPECT_TRUE(model().ShouldPreferOpeningInBrowser());
+}
+
 TEST_F(DownloadItemModelTest, InProgressOrCompletedBubbleUIInfo_V2On) {
   SetupDownloadItemDefaults();
 
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc
index 37ab859..95beafc 100644
--- a/chrome/browser/download/download_target_determiner.cc
+++ b/chrome/browser/download/download_target_determiner.cc
@@ -750,6 +750,26 @@
 }  // namespace
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
+void DownloadTargetDeterminer::DetermineIfHandledSafelyHelper(
+    download::DownloadItem* download,
+    const base::FilePath& local_path,
+    const std::string& mime_type,
+    base::OnceCallback<void(bool)> callback) {
+  if (blink::IsSupportedMimeType(mime_type)) {
+    std::move(callback).Run(true);
+    return;
+  }
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+  IsHandledBySafePlugin(content::DownloadItemUtils::GetBrowserContext(download),
+                        net::FilePathToFileURL(local_path), mime_type,
+                        RETRY_IF_STALE_PLUGIN_LIST, std::move(callback));
+
+#else
+  std::move(callback).Run(false);
+#endif
+}
+
 DownloadTargetDeterminer::Result
     DownloadTargetDeterminer::DoDetermineIfHandledSafely() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -762,25 +782,13 @@
   if (mime_type_.empty())
     return CONTINUE;
 
-  if (blink::IsSupportedMimeType(mime_type_)) {
-    is_filetype_handled_safely_ = true;
-    return CONTINUE;
-  }
-
-#if BUILDFLAG(ENABLE_PLUGINS)
-  IsHandledBySafePlugin(
-      content::DownloadItemUtils::GetBrowserContext(download_),
-      net::FilePathToFileURL(local_path_), mime_type_,
-      RETRY_IF_STALE_PLUGIN_LIST,
+  DetermineIfHandledSafelyHelper(
+      download_, local_path_, mime_type_,
       base::BindOnce(&DownloadTargetDeterminer::DetermineIfHandledSafelyDone,
                      weak_ptr_factory_.GetWeakPtr()));
   return QUIT_DOLOOP;
-#else
-  return CONTINUE;
-#endif
 }
 
-#if BUILDFLAG(ENABLE_PLUGINS)
 void DownloadTargetDeterminer::DetermineIfHandledSafelyDone(
     bool is_handled_safely) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -789,7 +797,6 @@
   is_filetype_handled_safely_ = is_handled_safely;
   DoLoop();
 }
-#endif
 
 DownloadTargetDeterminer::Result
     DownloadTargetDeterminer::DoDetermineIfAdobeReaderUpToDate() {
diff --git a/chrome/browser/download/download_target_determiner.h b/chrome/browser/download/download_target_determiner.h
index f0212392..85b565f4 100644
--- a/chrome/browser/download/download_target_determiner.h
+++ b/chrome/browser/download/download_target_determiner.h
@@ -93,6 +93,15 @@
   static bool IsAdobeReaderUpToDate();
 #endif
 
+  // Determine if the file type can be handled safely by the browser if it were
+  // to be opened via a file:// URL. Execute the callback with the determined
+  // value.
+  static void DetermineIfHandledSafelyHelper(
+      download::DownloadItem* download,
+      const base::FilePath& local_path,
+      const std::string& mime_type,
+      base::OnceCallback<void(bool)> callback);
+
  private:
   // The main workflow is controlled via a set of state transitions. Each state
   // has an associated handler. The handler for STATE_FOO is DoFoo. Each handler
@@ -252,11 +261,9 @@
   // - STATE_DETERMINE_IF_ADOBE_READER_UP_TO_DATE.
   Result DoDetermineIfHandledSafely();
 
-#if BUILDFLAG(ENABLE_PLUGINS)
   // Callback invoked when a decision is available about whether the file type
   // can be handled safely by the browser.
   void DetermineIfHandledSafelyDone(bool is_handled_safely);
-#endif
 
   // Determine if Adobe Reader is up to date. Only do the check on Windows for
   // .pdf file targets.
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index 25ce5d54..cfa17a4 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -451,7 +451,7 @@
 void DownloadUIModel::SetEphemeralWarningUiShownTime(
     absl::optional<base::Time> time) {}
 
-bool DownloadUIModel::ShouldPreferOpeningInBrowser() const {
+bool DownloadUIModel::ShouldPreferOpeningInBrowser() {
   return true;
 }
 
@@ -1758,3 +1758,7 @@
 bool DownloadUIModel::ShouldShowDropdown() const {
   return true;
 }
+
+void DownloadUIModel::DetermineAndSetShouldPreferOpeningInBrowser(
+    const base::FilePath& target_path,
+    bool is_filetype_handled_safely) {}
diff --git a/chrome/browser/download/download_ui_model.h b/chrome/browser/download/download_ui_model.h
index 2b3ebd23..0c9cc5c2 100644
--- a/chrome/browser/download/download_ui_model.h
+++ b/chrome/browser/download/download_ui_model.h
@@ -337,7 +337,7 @@
 
   // Returns |true| if opening in the browser is preferred for this download. If
   // |false|, the download should be opened with the system default application.
-  virtual bool ShouldPreferOpeningInBrowser() const;
+  virtual bool ShouldPreferOpeningInBrowser();
 
   // Change what's returned by ShouldPreferOpeningInBrowser to |preference|.
   virtual void SetShouldPreferOpeningInBrowser(bool preference);
@@ -519,6 +519,15 @@
   // Whether the dropdown menu button should be shown or not.
   virtual bool ShouldShowDropdown() const;
 
+  // Determines if a download should be preferably opened in the browser instead
+  // of the platform. Use |is_filetype_handled_safely| indicating if opening a
+  // file of this type is safe in the current BrowserContext, |target_path| to
+  // see if files of this type should be opened in the browser, and set whether
+  // the download should be preferred opening in the browser.
+  virtual void DetermineAndSetShouldPreferOpeningInBrowser(
+      const base::FilePath& target_path,
+      bool is_filetype_handled_safely);
+
  protected:
   // Returns the MIME type of the download.
   virtual std::string GetMimeType() const;
diff --git a/chrome/browser/extensions/activity_log/activity_actions.cc b/chrome/browser/extensions/activity_log/activity_actions.cc
index 9332035c..338530a 100644
--- a/chrome/browser/extensions/activity_log/activity_actions.cc
+++ b/chrome/browser/extensions/activity_log/activity_actions.cc
@@ -167,21 +167,20 @@
       break;
   }
 
-  result.extension_id = std::make_unique<std::string>(extension_id());
+  result.extension_id = extension_id();
   result.time = time().ToJsTime();
   result.count = count();
-  result.api_call = std::make_unique<std::string>(api_name());
-  result.args = std::make_unique<std::string>(Serialize(args()));
+  result.api_call = api_name();
+  result.args = Serialize(args());
   if (action_id() != -1)
-    result.activity_id = std::make_unique<std::string>(
-        base::StringPrintf("%" PRId64, action_id()));
+    result.activity_id = base::StringPrintf("%" PRId64, action_id());
   if (page_url().is_valid()) {
     if (!page_title().empty())
-      result.page_title = std::make_unique<std::string>(page_title());
-    result.page_url = std::make_unique<std::string>(SerializePageUrl());
+      result.page_title = page_title();
+    result.page_url = SerializePageUrl();
   }
   if (arg_url().is_valid())
-    result.arg_url = std::make_unique<std::string>(SerializeArgUrl());
+    result.arg_url = SerializeArgUrl();
 
   if (other()) {
     std::unique_ptr<ExtensionActivity::Other> other_field(
@@ -192,12 +191,12 @@
     }
     if (const base::Value::Dict* web_request =
             other()->FindDict(constants::kActionWebRequest)) {
-      other_field->web_request = std::make_unique<std::string>(
-          ActivityLogPolicy::Util::Serialize(*web_request));
+      other_field->web_request =
+          ActivityLogPolicy::Util::Serialize(*web_request);
     }
     const std::string* extra = other()->FindString(constants::kActionExtra);
     if (extra)
-      other_field->extra = std::make_unique<std::string>(*extra);
+      other_field->extra = *extra;
     if (absl::optional<int> dom_verb =
             other()->FindInt(constants::kActionDomVerb)) {
       switch (static_cast<DomActionType::Type>(dom_verb.value())) {
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc
index 605134e..70583e3 100644
--- a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc
@@ -39,10 +39,10 @@
   ExtensionActivity result = action->ConvertToExtensionActivity();
   ASSERT_EQ(api::activity_log_private::EXTENSION_ACTIVITY_TYPE_API_CALL,
             result.activity_type);
-  ASSERT_EQ(kExtensionId, *(result.extension_id.get()));
-  ASSERT_EQ(kApiCall, *(result.api_call.get()));
-  ASSERT_EQ(kArgs, *(result.args.get()));
-  ASSERT_EQ(NULL, result.activity_id.get());
+  ASSERT_EQ(kExtensionId, *result.extension_id);
+  ASSERT_EQ(kApiCall, *result.api_call);
+  ASSERT_EQ(kArgs, *result.args);
+  EXPECT_FALSE(result.activity_id);
 }
 
 TEST_F(ActivityLogApiUnitTest, ConvertDomAction) {
@@ -61,11 +61,11 @@
                               DomActionType::INSERTED);
   action->mutable_other().Set(activity_log_constants::kActionPrerender, false);
   ExtensionActivity result = action->ConvertToExtensionActivity();
-  ASSERT_EQ(kExtensionId, *(result.extension_id.get()));
-  ASSERT_EQ("http://www.google.com/", *(result.page_url.get()));
-  ASSERT_EQ("Title", *(result.page_title.get()));
-  ASSERT_EQ(kApiCall, *(result.api_call.get()));
-  ASSERT_EQ(kArgs, *(result.args.get()));
+  ASSERT_EQ(kExtensionId, *result.extension_id);
+  ASSERT_EQ("http://www.google.com/", *result.page_url);
+  ASSERT_EQ("Title", *result.page_title);
+  ASSERT_EQ(kApiCall, *result.api_call);
+  ASSERT_EQ(kArgs, *result.args);
   std::unique_ptr<ExtensionActivity::Other> other(std::move(result.other));
   ASSERT_EQ(api::activity_log_private::EXTENSION_ACTIVITY_DOM_VERB_INSERTED,
             other->dom_verb);
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_util.cc b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
index cee78f3f..22a5cc49 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_util.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
@@ -60,11 +60,9 @@
 }
 
 // Gets the string corresponding to |type| from |profile|.
-std::unique_ptr<std::string> GetStringFromProfile(
-    const autofill::AutofillProfile& profile,
-    const autofill::ServerFieldType& type) {
-  return std::make_unique<std::string>(
-      base::UTF16ToUTF8(profile.GetRawInfo(type)));
+std::string GetStringFromProfile(const autofill::AutofillProfile& profile,
+                                 const autofill::ServerFieldType& type) {
+  return base::UTF16ToUTF8(profile.GetRawInfo(type));
 }
 
 autofill_private::AddressEntry ProfileToAddressEntry(
@@ -73,7 +71,7 @@
   autofill_private::AddressEntry address;
 
   // Add all address fields to the entry.
-  address.guid = std::make_unique<std::string>(profile.guid());
+  address.guid = profile.guid();
   address.full_names = GetValueList(profile, autofill::NAME_FULL);
   address.honorific =
       GetStringFromProfile(profile, autofill::NAME_HONORIFIC_PREFIX);
@@ -95,8 +93,7 @@
   address.phone_numbers =
       GetValueList(profile, autofill::PHONE_HOME_WHOLE_NUMBER);
   address.email_addresses = GetValueList(profile, autofill::EMAIL_ADDRESS);
-  address.language_code =
-      std::make_unique<std::string>(profile.language_code());
+  address.language_code = profile.language_code();
 
   // Parse |label| so that it can be used to create address metadata.
   std::u16string separator =
@@ -108,8 +105,8 @@
   std::unique_ptr<autofill_private::AutofillMetadata> metadata(
       new autofill_private::AutofillMetadata);
   metadata->summary_label = base::UTF16ToUTF8(label_pieces[0]);
-  metadata->summary_sublabel = std::make_unique<std::string>(
-      base::UTF16ToUTF8(label.substr(label_pieces[0].size())));
+  metadata->summary_sublabel =
+      base::UTF16ToUTF8(label.substr(label_pieces[0].size()));
   address.metadata = std::move(metadata);
 
   return address;
@@ -122,65 +119,34 @@
   // A null |country| means "insert a space here", so we add a country w/o a
   // |name| or |country_code| to the list and let the UI handle it.
   if (country) {
-    entry.name =
-        std::make_unique<std::string>(base::UTF16ToUTF8(country->name()));
-    entry.country_code = std::make_unique<std::string>(country->country_code());
+    entry.name = base::UTF16ToUTF8(country->name());
+    entry.country_code = country->country_code();
   }
 
   return entry;
 }
 
-std::string CardNetworkToIconResourceIdString(const std::string& network) {
-  if (network == autofill::kAmericanExpressCard)
-    return "chrome://theme/IDR_AUTOFILL_CC_AMEX";
-  if (network == autofill::kDinersCard)
-    return "chrome://theme/IDR_AUTOFILL_CC_DINERS";
-  if (network == autofill::kDiscoverCard)
-    return "chrome://theme/IDR_AUTOFILL_CC_DISCOVER";
-  if (network == autofill::kEloCard)
-    return "chrome://theme/IDR_AUTOFILL_CC_ELO";
-  if (network == autofill::kJCBCard)
-    return "chrome://theme/IDR_AUTOFILL_CC_JCB";
-  if (network == autofill::kMasterCard)
-    return "chrome://theme/IDR_AUTOFILL_CC_MASTERCARD";
-  if (network == autofill::kMirCard)
-    return "chrome://theme/IDR_AUTOFILL_CC_MIR";
-  if (network == autofill::kTroyCard)
-    return "chrome://theme/IDR_AUTOFILL_CC_TROY";
-  if (network == autofill::kUnionPay)
-    return "chrome://theme/IDR_AUTOFILL_CC_UNIONPAY";
-  if (network == autofill::kVisaCard)
-    return "chrome://theme/IDR_AUTOFILL_CC_VISA";
-
-  return "chrome://theme/IDR_AUTOFILL_CC_GENERIC";
-}
-
 autofill_private::CreditCardEntry CreditCardToCreditCardEntry(
     const autofill::CreditCard& credit_card,
     const autofill::PersonalDataManager& personal_data) {
   autofill_private::CreditCardEntry card;
 
   // Add all credit card fields to the entry.
-  card.guid = std::make_unique<std::string>(
-      credit_card.record_type() == autofill::CreditCard::LOCAL_CARD
-          ? credit_card.guid()
-          : credit_card.server_id());
-  card.name = std::make_unique<std::string>(base::UTF16ToUTF8(
-      credit_card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL)));
-  card.card_number = std::make_unique<std::string>(
-      base::UTF16ToUTF8(credit_card.GetRawInfo(autofill::CREDIT_CARD_NUMBER)));
-  card.expiration_month = std::make_unique<std::string>(base::UTF16ToUTF8(
-      credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH)));
-  card.expiration_year = std::make_unique<std::string>(base::UTF16ToUTF8(
-      credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR)));
-  card.network = std::make_unique<std::string>(
-      base::UTF16ToUTF8(credit_card.NetworkForDisplay()));
+  card.guid = credit_card.record_type() == autofill::CreditCard::LOCAL_CARD
+                  ? credit_card.guid()
+                  : credit_card.server_id();
+  card.name = base::UTF16ToUTF8(
+      credit_card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
+  card.card_number =
+      base::UTF16ToUTF8(credit_card.GetRawInfo(autofill::CREDIT_CARD_NUMBER));
+  card.expiration_month = base::UTF16ToUTF8(
+      credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH));
+  card.expiration_year = base::UTF16ToUTF8(
+      credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR));
+  card.network = base::UTF16ToUTF8(credit_card.NetworkForDisplay());
   if (!credit_card.nickname().empty()) {
-    card.nickname = std::make_unique<std::string>(
-        base::UTF16ToUTF8(credit_card.nickname()));
+    card.nickname = base::UTF16ToUTF8(credit_card.nickname());
   }
-  card.image_src = std::make_unique<std::string>(
-      CardNetworkToIconResourceIdString(credit_card.network()));
 
   // Create card metadata and add it to |card|.
   std::unique_ptr<autofill_private::AutofillMetadata> metadata(
@@ -188,8 +154,7 @@
   std::pair<std::u16string, std::u16string> label_pieces =
       credit_card.LabelPieces();
   metadata->summary_label = base::UTF16ToUTF8(label_pieces.first);
-  metadata->summary_sublabel =
-      std::make_unique<std::string>(base::UTF16ToUTF8(label_pieces.second));
+  metadata->summary_sublabel = base::UTF16ToUTF8(label_pieces.second);
   metadata->is_local =
       credit_card.record_type() == autofill::CreditCard::LOCAL_CARD;
   metadata->is_cached =
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
index 8e3b369..de4b24ce 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
@@ -117,12 +117,11 @@
   bookmark_manager_private::BookmarkNodeDataElement element;
   // Add id and parentId so we can associate the data with existing nodes on the
   // client side.
-  element.id = std::make_unique<std::string>(base::NumberToString(node.id()));
-  element.parent_id =
-      std::make_unique<std::string>(base::NumberToString(node.parent()->id()));
+  element.id = base::NumberToString(node.id());
+  element.parent_id = base::NumberToString(node.parent()->id());
 
   if (node.is_url())
-    element.url = std::make_unique<std::string>(node.url().spec());
+    element.url = node.url().spec();
 
   element.title = base::UTF16ToUTF8(node.GetTitle());
   for (const auto& child : node.children()) {
@@ -141,7 +140,7 @@
   bookmark_manager_private::BookmarkNodeDataElement node_element;
 
   if (element.is_url)
-    node_element.url = std::make_unique<std::string>(element.url.spec());
+    node_element.url = element.url.spec();
   node_element.title = base::UTF16ToUTF8(element.title);
   for (size_t i = 0; i < element.children.size(); ++i) {
     node_element.children.push_back(
diff --git a/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.cc b/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.cc
index d40d6b4..5020fd2 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.cc
+++ b/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.cc
@@ -63,15 +63,13 @@
 
   const BookmarkNode* parent = node->parent();
   if (parent) {
-    out_bookmark_tree_node->parent_id =
-        std::make_unique<std::string>(base::NumberToString(parent->id()));
+    out_bookmark_tree_node->parent_id = base::NumberToString(parent->id());
     out_bookmark_tree_node->index =
         static_cast<int>(parent->GetIndexOf(node).value());
   }
 
   if (!node->is_folder()) {
-    out_bookmark_tree_node->url =
-        std::make_unique<std::string>(node->url().spec());
+    out_bookmark_tree_node->url = node->url().spec();
   } else {
     // Javascript Date wants milliseconds since the epoch, ToDoubleT is seconds.
     base::Time t = node->date_folder_modified();
diff --git a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
index 8e6796a0..a62c125 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
+++ b/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
@@ -132,7 +132,7 @@
     std::string* error) {
   int64_t parent_id;
 
-  if (!details.parent_id.get()) {
+  if (!details.parent_id) {
     // Optional, default to "other bookmarks".
     parent_id = model->other_node()->id();
   } else if (!base::StringToInt64(*details.parent_id, &parent_id)) {
@@ -156,11 +156,11 @@
   }
 
   std::u16string title;  // Optional.
-  if (details.title.get())
+  if (details.title)
     title = base::UTF8ToUTF16(*details.title);
 
   std::string url_string;  // Optional.
-  if (details.url.get())
+  if (details.url)
     url_string = *details.url;
 
   GURL url(url_string);
@@ -332,7 +332,7 @@
   api::bookmarks::OnChanged::ChangeInfo change_info;
   change_info.title = base::UTF16ToUTF8(node->GetTitle());
   if (node->is_url())
-    change_info.url = std::make_unique<std::string>(node->url().spec());
+    change_info.url = node->url().spec();
 
   DispatchEvent(events::BOOKMARKS_ON_CHANGED,
                 api::bookmarks::OnChanged::kEventName,
@@ -627,7 +627,7 @@
     return Error(bookmark_api_constants::kModifySpecialError);
 
   const BookmarkNode* parent = nullptr;
-  if (!params->destination.parent_id.get()) {
+  if (!params->destination.parent_id) {
     // Optional, defaults to current parent.
     parent = node->parent();
   } else {
@@ -671,14 +671,14 @@
   // Optional but we need to distinguish non present from an empty title.
   std::u16string title;
   bool has_title = false;
-  if (params->changes.title.get()) {
+  if (params->changes.title) {
     title = base::UTF8ToUTF16(*params->changes.title);
     has_title = true;
   }
 
   // Optional.
   std::string url_string;
-  if (params->changes.url.get())
+  if (params->changes.url)
     url_string = *params->changes.url;
   GURL url(url_string);
   if (!url_string.empty() && !url.is_valid())
diff --git a/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc b/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc
index 894f563..1210f3f5 100644
--- a/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc
+++ b/chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.cc
@@ -53,14 +53,13 @@
     base_icu::UChar32 code_point = key_sym & ~BRLAPI_KEY_SYM_UNICODE;
     if (!base::IsValidCharacter(code_point))
       return;
-    event->standard_key_char = std::make_unique<std::string>();
-    base::WriteUnicodeCharacter(code_point, event->standard_key_char.get());
+    event->standard_key_char.emplace();
+    base::WriteUnicodeCharacter(code_point, &*event->standard_key_char);
   } else if (key_sym >= kMinFunctionKey && key_sym <= kMaxFunctionKey) {
     // Function keys are 0-based here, so we need to add one to get e.g.
     // 'F1' for the first key.
     int function_key_number = key_sym - kMinFunctionKey + 1;
-    event->standard_key_code = std::make_unique<std::string>(
-        base::StringPrintf("F%d", function_key_number));
+    event->standard_key_code = base::StringPrintf("F%d", function_key_number);
   } else {
     // Explicitly map the keys that brlapi provides.
     const char* code_string;
@@ -110,7 +109,7 @@
       default:
         return;
     }
-    event->standard_key_code = std::make_unique<std::string>(code_string);
+    event->standard_key_code = code_string;
   }
   MapModifierFlags(code, event);
   event->command = KEY_COMMAND_STANDARD_KEY;
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
index c0a6fb4..e8fcccf 100644
--- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
+++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
@@ -507,7 +507,7 @@
     // TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
     LOG(WARNING) << "PIN request succeeded";
     api::certificate_provider::PinResponseDetails details;
-    details.user_input = std::make_unique<std::string>(value);
+    details.user_input = value;
     create_results.Append(base::Value::FromUniquePtrValue(details.ToValue()));
   } else {
     // TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
index d4af072..70d0fa11 100644
--- a/chrome/browser/extensions/api/chrome_extensions_api_client.cc
+++ b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
@@ -120,7 +120,7 @@
     content::WebContents* web_contents) const {
   favicon::CreateContentFaviconDriverForWebContents(web_contents);
 #if BUILDFLAG(ENABLE_PRINTING)
-  printing::InitializePrinting(web_contents);
+  printing::InitializePrintingForWebContents(web_contents);
 #endif
 #if BUILDFLAG(ENABLE_PDF)
   pdf::PDFWebContentsHelper::CreateForWebContentsWithClient(
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_api.cc b/chrome/browser/extensions/api/content_settings/content_settings_api.cc
index d6d0093..7e27441 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_api.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_api.cc
@@ -130,7 +130,7 @@
   }
 
   GURL secondary_url(primary_url);
-  if (params->details.secondary_url.get()) {
+  if (params->details.secondary_url) {
     secondary_url = GURL(*params->details.secondary_url);
     if (!secondary_url.is_valid()) {
       return RespondNow(
@@ -196,7 +196,7 @@
     return RespondNow(Error(primary_error));
 
   ContentSettingsPattern secondary_pattern = ContentSettingsPattern::Wildcard();
-  if (params->details.secondary_pattern.get()) {
+  if (params->details.secondary_pattern) {
     std::string secondary_error;
     secondary_pattern = content_settings_helpers::ParseExtensionPattern(
         *params->details.secondary_pattern, &secondary_error);
diff --git a/chrome/browser/extensions/api/context_menus/context_menus_api.cc b/chrome/browser/extensions/api/context_menus/context_menus_api.cc
index 7cb0d79..f0a05b9 100644
--- a/chrome/browser/extensions/api/context_menus/context_menus_api.cc
+++ b/chrome/browser/extensions/api/context_menus/context_menus_api.cc
@@ -36,7 +36,7 @@
       api::context_menus::Create::Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  if (params->create_properties.id.get()) {
+  if (params->create_properties.id) {
     id.string_uid = *params->create_properties.id;
   } else {
     if (BackgroundInfo::HasLazyContext(extension()))
diff --git a/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h b/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h
index d212348..302d51e51 100644
--- a/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h
+++ b/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h
@@ -111,7 +111,7 @@
 
   // Title.
   std::string title;
-  if (create_properties.title.get())
+  if (create_properties.title)
     title = *create_properties.title;
 
   MenuItem::Type type = GetType(create_properties.type, MenuItem::NORMAL);
@@ -189,7 +189,7 @@
   }
 
   // Title.
-  if (update_properties.title.get()) {
+  if (update_properties.title) {
     std::string title(*update_properties.title);
     if (title.empty() && item->type() != MenuItem::SEPARATOR) {
       *error = kTitleNeededError;
diff --git a/chrome/browser/extensions/api/cookies/cookies_api.cc b/chrome/browser/extensions/api/cookies/cookies_api.cc
index 5863121..fc30a3b 100644
--- a/chrome/browser/extensions/api/cookies/cookies_api.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_api.cc
@@ -85,11 +85,6 @@
       ->GetCookieManagerForBrowserProcess();
 }
 
-template <typename T>
-T OrDefault(const std::unique_ptr<T>& ptr, T fallback) {
-  return ptr.get() ? *ptr : fallback;
-}
-
 }  // namespace
 
 CookiesEventRouter::CookieChangeListener::CookieChangeListener(
@@ -241,15 +236,14 @@
   if (!ParseUrl(extension(), parsed_args_->details.url, &url_, true, &error))
     return RespondNow(Error(std::move(error)));
 
-  std::string store_id =
-      OrDefault(parsed_args_->details.store_id, std::string());
+  std::string store_id = parsed_args_->details.store_id.value_or(std::string());
   network::mojom::CookieManager* cookie_manager = ParseStoreCookieManager(
       browser_context(), include_incognito_information(), &store_id, &error);
   if (!cookie_manager)
     return RespondNow(Error(std::move(error)));
 
-  if (!parsed_args_->details.store_id.get())
-    parsed_args_->details.store_id = std::make_unique<std::string>(store_id);
+  if (!parsed_args_->details.store_id)
+    parsed_args_->details.store_id = store_id;
 
   DCHECK(!url_.is_empty() && url_.is_valid());
   cookies_helpers::GetCookieListFromManager(
@@ -292,21 +286,20 @@
   EXTENSION_FUNCTION_VALIDATE(parsed_args_.get());
 
   std::string error;
-  if (parsed_args_->details.url.get() &&
+  if (parsed_args_->details.url &&
       !ParseUrl(extension(), *parsed_args_->details.url, &url_, false,
                 &error)) {
     return RespondNow(Error(std::move(error)));
   }
 
-  std::string store_id =
-      OrDefault(parsed_args_->details.store_id, std::string());
+  std::string store_id = parsed_args_->details.store_id.value_or(std::string());
   network::mojom::CookieManager* cookie_manager = ParseStoreCookieManager(
       browser_context(), include_incognito_information(), &store_id, &error);
   if (!cookie_manager)
     return RespondNow(Error(std::move(error)));
 
-  if (!parsed_args_->details.store_id.get())
-    parsed_args_->details.store_id = std::make_unique<std::string>(store_id);
+  if (!parsed_args_->details.store_id)
+    parsed_args_->details.store_id = store_id;
 
   DCHECK(url_.is_empty() || url_.is_valid());
   if (url_.is_empty()) {
@@ -374,15 +367,14 @@
   if (!ParseUrl(extension(), parsed_args_->details.url, &url_, true, &error))
     return RespondNow(Error(std::move(error)));
 
-  std::string store_id =
-      OrDefault(parsed_args_->details.store_id, std::string());
+  std::string store_id = parsed_args_->details.store_id.value_or(std::string());
   network::mojom::CookieManager* cookie_manager = ParseStoreCookieManager(
       browser_context(), include_incognito_information(), &store_id, &error);
   if (!cookie_manager)
     return RespondNow(Error(std::move(error)));
 
-  if (!parsed_args_->details.store_id.get())
-    parsed_args_->details.store_id = std::make_unique<std::string>(store_id);
+  if (!parsed_args_->details.store_id)
+    parsed_args_->details.store_id = store_id;
 
   base::Time expiration_time;
   if (parsed_args_->details.expiration_date) {
@@ -416,19 +408,19 @@
   // TODO(crbug.com/1144181): Add support for SameParty attribute.
   std::unique_ptr<net::CanonicalCookie> cc(
       net::CanonicalCookie::CreateSanitizedCookie(
-          url_,                                                    //
-          OrDefault(parsed_args_->details.name, std::string()),    //
-          OrDefault(parsed_args_->details.value, std::string()),   //
-          OrDefault(parsed_args_->details.domain, std::string()),  //
-          OrDefault(parsed_args_->details.path, std::string()),    //
-          base::Time(),                                            //
-          expiration_time,                                         //
-          base::Time(),                                            //
-          parsed_args_->details.secure.value_or(false),            //
-          parsed_args_->details.http_only.value_or(false),         //
-          same_site,                                               //
-          net::COOKIE_PRIORITY_DEFAULT,                            //
-          /*same_party=*/false,                                    //
+          url_,                                                  //
+          parsed_args_->details.name.value_or(std::string()),    //
+          parsed_args_->details.value.value_or(std::string()),   //
+          parsed_args_->details.domain.value_or(std::string()),  //
+          parsed_args_->details.path.value_or(std::string()),    //
+          base::Time(),                                          //
+          expiration_time,                                       //
+          base::Time(),                                          //
+          parsed_args_->details.secure.value_or(false),          //
+          parsed_args_->details.http_only.value_or(false),       //
+          same_site,                                             //
+          net::COOKIE_PRIORITY_DEFAULT,                          //
+          /*same_party=*/false,                                  //
           /*partition_key=*/absl::nullopt));
   if (!cc) {
     // Return error through callbacks so that the proper error message
@@ -475,7 +467,7 @@
   state_ = GET_COMPLETED;
 
   if (!success_) {
-    std::string name = OrDefault(parsed_args_->details.name, std::string());
+    std::string name = parsed_args_->details.name.value_or(std::string());
     Respond(Error(ErrorUtils::FormatErrorMessage(
         cookies_api_constants::kCookieSetFailedError, name)));
     return;
@@ -487,7 +479,7 @@
     // Return the first matching cookie. Relies on the fact that the
     // CookieMonster returns them in canonical order (longest path, then
     // earliest creation time).
-    std::string name = OrDefault(parsed_args_->details.name, std::string());
+    std::string name = parsed_args_->details.name.value_or(std::string());
     if (cookie_with_access_result.cookie.Name() == name) {
       api::cookies::Cookie api_cookie = cookies_helpers::CreateCookie(
           cookie_with_access_result.cookie, *parsed_args_->details.store_id);
@@ -514,15 +506,14 @@
   if (!ParseUrl(extension(), parsed_args_->details.url, &url_, true, &error))
     return RespondNow(Error(std::move(error)));
 
-  std::string store_id =
-      OrDefault(parsed_args_->details.store_id, std::string());
+  std::string store_id = parsed_args_->details.store_id.value_or(std::string());
   network::mojom::CookieManager* cookie_manager = ParseStoreCookieManager(
       browser_context(), include_incognito_information(), &store_id, &error);
   if (!cookie_manager)
     return RespondNow(Error(std::move(error)));
 
-  if (!parsed_args_->details.store_id.get())
-    parsed_args_->details.store_id = std::make_unique<std::string>(store_id);
+  if (!parsed_args_->details.store_id)
+    parsed_args_->details.store_id = store_id;
 
   network::mojom::CookieDeletionFilterPtr filter(
       network::mojom::CookieDeletionFilter::New());
diff --git a/chrome/browser/extensions/api/cookies/cookies_helpers.cc b/chrome/browser/extensions/api/cookies/cookies_helpers.cc
index 0a79f0ae..07276de0 100644
--- a/chrome/browser/extensions/api/cookies/cookies_helpers.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_helpers.cc
@@ -42,7 +42,7 @@
 
 void AppendCookieToVectorIfMatchAndHasHostPermission(
     const net::CanonicalCookie cookie,
-    const GetAll::Params::Details* details,
+    GetAll::Params::Details* details,
     const Extension* extension,
     std::vector<Cookie>* match_vector) {
   // Ignore any cookie whose domain doesn't match the extension's
@@ -176,7 +176,7 @@
 
 void AppendMatchingCookiesFromCookieListToVector(
     const net::CookieList& all_cookies,
-    const GetAll::Params::Details* details,
+    GetAll::Params::Details* details,
     const Extension* extension,
     std::vector<Cookie>* match_vector) {
   for (const net::CanonicalCookie& cookie : all_cookies) {
@@ -187,7 +187,7 @@
 
 void AppendMatchingCookiesFromCookieAccessResultListToVector(
     const net::CookieAccessResultList& all_cookies_with_access_result,
-    const GetAll::Params::Details* details,
+    GetAll::Params::Details* details,
     const Extension* extension,
     std::vector<Cookie>* match_vector) {
   for (const net::CookieWithAccessResult& cookie_with_access_result :
@@ -207,20 +207,19 @@
   }
 }
 
-MatchFilter::MatchFilter(const GetAll::Params::Details* details)
-    : details_(details) {
+MatchFilter::MatchFilter(GetAll::Params::Details* details) : details_(details) {
   DCHECK(details_);
 }
 
 bool MatchFilter::MatchesCookie(
     const net::CanonicalCookie& cookie) {
-  if (details_->name.get() && *details_->name != cookie.Name())
+  if (details_->name && *details_->name != cookie.Name())
     return false;
 
   if (!MatchesDomain(cookie.Domain()))
     return false;
 
-  if (details_->path.get() && *details_->path != cookie.Path())
+  if (details_->path && *details_->path != cookie.Path())
     return false;
 
   if (details_->secure && *details_->secure != cookie.IsSecure())
@@ -233,7 +232,7 @@
 }
 
 bool MatchFilter::MatchesDomain(const std::string& domain) {
-  if (!details_->domain.get())
+  if (!details_->domain)
     return true;
 
   // Add a leading '.' character to the filter domain if it doesn't exist.
diff --git a/chrome/browser/extensions/api/cookies/cookies_helpers.h b/chrome/browser/extensions/api/cookies/cookies_helpers.h
index a4df1f4..6cdb14e0e 100644
--- a/chrome/browser/extensions/api/cookies/cookies_helpers.h
+++ b/chrome/browser/extensions/api/cookies/cookies_helpers.h
@@ -82,7 +82,7 @@
 // and are allowed by extension host permissions.
 void AppendMatchingCookiesFromCookieListToVector(
     const net::CookieList& all_cookies,
-    const api::cookies::GetAll::Params::Details* details,
+    api::cookies::GetAll::Params::Details* details,
     const Extension* extension,
     std::vector<api::cookies::Cookie>* match_vector);
 
@@ -90,7 +90,7 @@
 // results).
 void AppendMatchingCookiesFromCookieAccessResultListToVector(
     const net::CookieAccessResultList& all_cookies_with_access_result,
-    const api::cookies::GetAll::Params::Details* details,
+    api::cookies::GetAll::Params::Details* details,
     const Extension* extension,
     std::vector<api::cookies::Cookie>* match_vector);
 
@@ -109,7 +109,7 @@
   // Takes the details dictionary argument given by the user as input.
   // This class does not take ownership of the lifetime of the Details
   // object.
-  explicit MatchFilter(const api::cookies::GetAll::Params::Details* details);
+  explicit MatchFilter(api::cookies::GetAll::Params::Details* details);
 
   // Returns true if the given cookie matches the properties in the match
   // filter.
@@ -125,7 +125,7 @@
   // 'foo.bar.com', '.foo.bar.com', and 'baz.foo.bar.com'.
   bool MatchesDomain(const std::string& domain);
 
-  raw_ptr<const api::cookies::GetAll::Params::Details> details_;
+  raw_ptr<api::cookies::GetAll::Params::Details> details_;
 };
 
 }  // namespace cookies_helpers
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index 377c963a..e98d6c8 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -82,10 +82,8 @@
 
 void CopyDebuggee(Debuggee* dst, const Debuggee& src) {
   dst->tab_id = src.tab_id;
-  if (src.extension_id)
-    dst->extension_id = std::make_unique<std::string>(*src.extension_id);
-  if (src.target_id)
-    dst->target_id = std::make_unique<std::string>(*src.target_id);
+  dst->extension_id = src.extension_id;
+  dst->target_id = src.target_id;
 }
 
 bool ExtensionMayAttachToTargetProfile(Profile* extension_profile,
diff --git a/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc b/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc
index bccbd61..dab4b974 100644
--- a/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc
+++ b/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc
@@ -36,7 +36,7 @@
                 const std::string& id) {
   std::vector<extensions::api::events::Rule> add_rules;
   add_rules.emplace_back();
-  add_rules[0].id = std::make_unique<std::string>(id);
+  add_rules[0].id = id;
   std::string error = registry->AddRules(kExtensionId, std::move(add_rules));
   EXPECT_TRUE(error.empty());
 }
diff --git a/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc b/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
index 30a7f8e..2bb8860 100644
--- a/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
+++ b/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
@@ -77,7 +77,7 @@
                       TestRulesRegistry* registry) {
     std::vector<api::events::Rule> add_rules;
     add_rules.emplace_back();
-    add_rules[0].id = std::make_unique<std::string>(rule_id);
+    add_rules[0].id = rule_id;
     return registry->AddRules(extension_id, std::move(add_rules));
   }
 
@@ -194,7 +194,7 @@
   std::vector<const api::events::Rule*> gotten_rules;
   registry_->GetRules(extension1_->id(), rules_to_get, &gotten_rules);
   ASSERT_EQ(1u, gotten_rules.size());
-  ASSERT_TRUE(gotten_rules[0]->id.get());
+  ASSERT_TRUE(gotten_rules[0]->id);
   EXPECT_EQ(kRuleId, *(gotten_rules[0]->id));
 }
 
@@ -208,8 +208,8 @@
   std::vector<const api::events::Rule*> gotten_rules;
   registry_->GetAllRules(extension1_->id(), &gotten_rules);
   EXPECT_EQ(2u, gotten_rules.size());
-  ASSERT_TRUE(gotten_rules[0]->id.get());
-  ASSERT_TRUE(gotten_rules[1]->id.get());
+  ASSERT_TRUE(gotten_rules[0]->id);
+  ASSERT_TRUE(gotten_rules[1]->id);
   EXPECT_TRUE((kRuleId == *(gotten_rules[0]->id) &&
                kRule2Id == *(gotten_rules[1]->id)) ||
               (kRuleId == *(gotten_rules[1]->id) &&
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
index 8ad2c0d0..0e046a5 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
@@ -133,7 +133,7 @@
     action_dict.SetStringKey(keys::kInstanceTypeKey, keys::kCancelRequestType);
 
     api::events::Rule rule;
-    rule.id = std::make_unique<std::string>(kRuleId1);
+    rule.id = kRuleId1;
     rule.priority = 100;
     rule.actions.push_back(action_dict.CreateDeepCopy());
     http_condition_dict->Set(keys2::kSchemesKey, std::move(scheme_http));
@@ -154,7 +154,7 @@
     action_dict.SetStringKey(keys::kInstanceTypeKey, keys::kCancelRequestType);
 
     api::events::Rule rule;
-    rule.id = std::make_unique<std::string>(kRuleId2);
+    rule.id = kRuleId2;
     rule.priority = 100;
     rule.actions.push_back(action_dict.CreateDeepCopy());
     rule.conditions.push_back(condition_dict.CreateDeepCopy());
@@ -172,7 +172,7 @@
     action_dict.SetStringKey(keys::kRedirectUrlKey, destination);
 
     api::events::Rule rule;
-    rule.id = std::make_unique<std::string>(kRuleId3);
+    rule.id = kRuleId3;
     rule.priority = 100;
     rule.actions.push_back(action_dict.CreateDeepCopy());
     rule.conditions.push_back(condition_dict.CreateDeepCopy());
@@ -194,7 +194,7 @@
     action_dict.SetIntKey(keys::kLowerPriorityThanKey, 150);
 
     api::events::Rule rule;
-    rule.id = std::make_unique<std::string>(kRuleId4);
+    rule.id = kRuleId4;
     rule.priority = 200;
     rule.actions.push_back(action_dict.CreateDeepCopy());
     rule.conditions.push_back(condition_dict.CreateDeepCopy());
@@ -223,7 +223,7 @@
     action_dict.SetStringKey(keys::kInstanceTypeKey, keys::kCancelRequestType);
 
     api::events::Rule rule;
-    rule.id = std::make_unique<std::string>(rule_id);
+    rule.id = rule_id;
     rule.priority = 1;
     rule.actions.push_back(action_dict.CreateDeepCopy());
     for (auto it = attributes.cbegin(); it != attributes.cend(); ++it)
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
index abef5b1..3a859b1 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
@@ -1115,7 +1115,7 @@
   properties.extension_id = extension->id();
   properties.path_suffix = "manifest.json";
   properties.message = kErrorMessage;
-  properties.manifest_key = std::make_unique<std::string>("name");
+  properties.manifest_key = "name";
 
   scoped_refptr<ExtensionFunction> function(
       new api::DeveloperPrivateRequestFileSourceFunction());
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_mangle.cc b/chrome/browser/extensions/api/developer_private/developer_private_mangle.cc
index ec253fa..2d29802 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_mangle.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_mangle.cc
@@ -66,15 +66,13 @@
   result.terminated =
       info.state == api::developer_private::EXTENSION_STATE_TERMINATED;
 
-  if (info.path)
-    result.path = std::make_unique<std::string>(*info.path);
+  result.path = info.path;
   if (info.options_page)
-    result.options_url = std::make_unique<std::string>(info.options_page->url);
-  if (info.launch_url)
-    result.app_launch_url = std::make_unique<std::string>(*info.launch_url);
+    result.options_url = info.options_page->url;
+  result.app_launch_url = info.launch_url;
   if (!info.home_page.url.empty())
-    result.homepage_url = std::make_unique<std::string>(info.home_page.url);
-  result.update_url = std::make_unique<std::string>(info.update_url);
+    result.homepage_url = info.home_page.url;
+  result.update_url = info.update_url;
   for (const std::string& str_warning : info.install_warnings) {
     api::developer_private::InstallWarning warning;
     warning.message = str_warning;
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
index ac0befd..57c9ba41 100644
--- a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
+++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
@@ -129,8 +129,7 @@
   PopulateErrorBase(error, &result);
   result.manifest_key = base::UTF16ToUTF8(error.manifest_key());
   if (!error.manifest_specific().empty()) {
-    result.manifest_specific = std::make_unique<std::string>(
-        base::UTF16ToUTF8(error.manifest_specific()));
+    result.manifest_specific = base::UTF16ToUTF8(error.manifest_specific());
   }
   return result;
 }
@@ -528,8 +527,7 @@
       break;
   }
   if (blocklist_text != -1) {
-    info->blacklist_text =
-        std::make_unique<std::string>(l10n_util::GetStringUTF8(blocklist_text));
+    info->blacklist_text = l10n_util::GetStringUTF8(blocklist_text);
   }
 
   if (extension_system_->extension_service()->allowlist()->ShouldDisplayWarning(
@@ -647,8 +645,7 @@
 
   // Launch url.
   if (extension.is_app()) {
-    info->launch_url = std::make_unique<std::string>(
-        AppLaunchInfo::GetFullLaunchURL(&extension).spec());
+    info->launch_url = AppLaunchInfo::GetFullLaunchURL(&extension).spec();
   }
 
   // Location.
@@ -678,8 +675,7 @@
   else if (extension.is_shared_module())
     location_text = IDS_EXTENSIONS_INSTALL_LOCATION_SHARED_MODULE;
   if (location_text != -1) {
-    info->location_text =
-        std::make_unique<std::string>(l10n_util::GetStringUTF8(location_text));
+    info->location_text = l10n_util::GetStringUTF8(location_text);
   }
 
   // Runtime/Manifest errors.
@@ -724,9 +720,9 @@
 
   // Path.
   if (Manifest::IsUnpackedLocation(extension.location())) {
-    info->path = std::make_unique<std::string>(extension.path().AsUTF8Unsafe());
-    info->prettified_path = std::make_unique<std::string>(
-        extensions::path_util::PrettifyPath(extension.path()).AsUTF8Unsafe());
+    info->path = extension.path().AsUTF8Unsafe();
+    info->prettified_path =
+        extensions::path_util::PrettifyPath(extension.path()).AsUTF8Unsafe();
   }
 
   AddPermissionsInfo(browser_context_, extension, &info->permissions);
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc
index dd15647f..6f43ecc 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api.cc
@@ -1045,7 +1045,7 @@
           render_frame_host() ? render_frame_host()->GetRoutingID() : -1,
           traffic_annotation));
   base::FilePath creator_suggested_filename;
-  if (options.filename.get()) {
+  if (options.filename) {
     // Strip "%" character as it affects environment variables.
     std::string filename;
     base::ReplaceChars(*options.filename, "%", "_", &filename);
@@ -1078,7 +1078,7 @@
   std::string method_string = downloads::ToString(options.method);
   if (!method_string.empty())
     download_params->set_method(method_string);
-  if (options.body.get()) {
+  if (options.body) {
     download_params->set_post_body(
         network::ResourceRequestBody::CreateFromBytes(options.body->data(),
                                                       options.body->size()));
diff --git a/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.cc b/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.cc
index c6a397e..021d9bc 100644
--- a/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.cc
+++ b/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.cc
@@ -97,12 +97,10 @@
 
       network_details.mac_address = result->get_network_details()->mac_address;
       if (ipv4_address.has_value()) {
-        network_details.ipv4 =
-            std::make_unique<std::string>(ipv4_address->ToString());
+        network_details.ipv4 = ipv4_address->ToString();
       }
       if (ipv6_address.has_value()) {
-        network_details.ipv6 =
-            std::make_unique<std::string>(ipv6_address->ToString());
+        network_details.ipv6 = ipv6_address->ToString();
       }
 
       Respond(WithArguments(
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.cc b/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.cc
index 0506c7c..9a5b0b2b 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.cc
@@ -113,8 +113,7 @@
     response.presence = ConvertPresenceValue(file_system_item.presence);
 
     if (file_system_item.sha256_hash) {
-      response.sha256_hash = std::make_unique<std::string>(
-          EncodeHash(file_system_item.sha256_hash.value()));
+      response.sha256_hash = EncodeHash(*file_system_item.sha256_hash);
     }
 
     if (file_system_item.executable_metadata) {
@@ -124,19 +123,13 @@
       response.is_running = executable_metadata.is_running;
 
       if (executable_metadata.public_key_sha256) {
-        response.public_key_sha256 = std::make_unique<std::string>(
-            EncodeHash(executable_metadata.public_key_sha256.value()));
+        response.public_key_sha256 =
+            EncodeHash(*executable_metadata.public_key_sha256);
       }
 
-      if (executable_metadata.product_name) {
-        response.product_name = std::make_unique<std::string>(
-            executable_metadata.product_name.value());
-      }
+      response.product_name = executable_metadata.product_name;
 
-      if (executable_metadata.version) {
-        response.version =
-            std::make_unique<std::string>(executable_metadata.version.value());
-      }
+      response.version = executable_metadata.version;
     }
 
     api_responses.push_back(std::move(response));
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
index 6a140ab..c60ade6 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
@@ -378,30 +378,19 @@
     const enterprise_signals::DeviceInfo& device_signals) {
   api::enterprise_reporting_private::DeviceInfo device_info;
 
-  device_info.os_name = std::move(device_signals.os_name);
-  device_info.os_version = std::move(device_signals.os_version);
-  device_info.security_patch_level =
-      std::move(device_signals.security_patch_level);
-  device_info.device_host_name = std::move(device_signals.device_host_name);
-  device_info.device_model = std::move(device_signals.device_model);
-  device_info.serial_number = std::move(device_signals.serial_number);
+  device_info.os_name = device_signals.os_name;
+  device_info.os_version = device_signals.os_version;
+  device_info.security_patch_level = device_signals.security_patch_level;
+  device_info.device_host_name = device_signals.device_host_name;
+  device_info.device_model = device_signals.device_model;
+  device_info.serial_number = device_signals.serial_number;
   device_info.screen_lock_secured =
       ToInfoSettingValue(device_signals.screen_lock_secured);
   device_info.disk_encrypted =
       ToInfoSettingValue(device_signals.disk_encrypted);
-  device_info.mac_addresses = std::move(device_signals.mac_addresses);
-  if (device_signals.windows_machine_domain.has_value()) {
-    device_info.windows_machine_domain = std::make_unique<std::string>(
-        device_signals.windows_machine_domain.value());
-  } else {
-    device_info.windows_machine_domain = nullptr;
-  }
-  if (device_signals.windows_user_domain.has_value()) {
-    device_info.windows_user_domain = std::make_unique<std::string>(
-        device_signals.windows_user_domain.value());
-  } else {
-    device_info.windows_user_domain = nullptr;
-  }
+  device_info.mac_addresses = device_signals.mac_addresses;
+  device_info.windows_machine_domain = device_signals.windows_machine_domain;
+  device_info.windows_user_domain = device_signals.windows_user_domain;
   if (device_signals.secure_boot_enabled.has_value()) {
     device_info.secure_boot_enabled =
         ToInfoSettingValue(device_signals.secure_boot_enabled.value());
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index 9451e82..b728699 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -1423,8 +1423,7 @@
             fake_file_item.file_path.AsUTF8Unsafe());
   EXPECT_EQ(parsed_file_system_signal->presence,
             enterprise_reporting_private::PRESENCE_VALUE_FOUND);
-  EXPECT_EQ(*parsed_file_system_signal->sha256_hash.get(),
-            "c29tZSBoYXNoZWQgdmFsdWU");
+  EXPECT_EQ(*parsed_file_system_signal->sha256_hash, "c29tZSBoYXNoZWQgdmFsdWU");
 
   histogram_tester_.ExpectUniqueSample(
       "Enterprise.DeviceSignals.Collection.Success", signal_name(), 1);
diff --git a/chrome/browser/extensions/api/file_system/file_system_api_unittest.cc b/chrome/browser/extensions/api/file_system/file_system_api_unittest.cc
index c035756..b4b1fe2 100644
--- a/chrome/browser/extensions/api/file_system/file_system_api_unittest.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_api_unittest.cc
@@ -42,7 +42,7 @@
   AcceptOption option;
 
   if (!description.empty())
-    option.description = std::make_unique<std::string>(description);
+    option.description = description;
 
   if (!mime_types.empty()) {
     option.mime_types =
@@ -161,21 +161,21 @@
 }
 
 TEST(FileSystemApiUnitTest, FileSystemChooseEntryFunctionSuggestionTest) {
-  std::string opt_name;
+  absl::optional<std::string> opt_name;
   base::FilePath suggested_name;
   base::FilePath::StringType suggested_extension;
 
   opt_name = std::string("normal_path.txt");
-  FileSystemChooseEntryFunction::BuildSuggestion(&opt_name, &suggested_name,
-      &suggested_extension);
+  FileSystemChooseEntryFunction::BuildSuggestion(opt_name, &suggested_name,
+                                                 &suggested_extension);
   EXPECT_FALSE(suggested_name.IsAbsolute());
   EXPECT_EQ(suggested_name.MaybeAsASCII(), "normal_path.txt");
   EXPECT_EQ(suggested_extension, ToStringType("txt"));
 
   // We should provide just the basename, i.e., "path".
   opt_name = std::string("/a/bad/path");
-  FileSystemChooseEntryFunction::BuildSuggestion(&opt_name, &suggested_name,
-      &suggested_extension);
+  FileSystemChooseEntryFunction::BuildSuggestion(opt_name, &suggested_name,
+                                                 &suggested_extension);
   EXPECT_FALSE(suggested_name.IsAbsolute());
   EXPECT_EQ(suggested_name.MaybeAsASCII(), "path");
   EXPECT_TRUE(suggested_extension.empty());
@@ -184,8 +184,8 @@
   // TODO(thorogood): Fix this test on Windows.
   // Filter out absolute paths with no basename.
   opt_name = std::string("/");
-  FileSystemChooseEntryFunction::BuildSuggestion(&opt_name, &suggested_name,
-      &suggested_extension);
+  FileSystemChooseEntryFunction::BuildSuggestion(opt_name, &suggested_name,
+                                                 &suggested_extension);
   EXPECT_FALSE(suggested_name.IsAbsolute());
   EXPECT_TRUE(suggested_name.MaybeAsASCII().empty());
   EXPECT_TRUE(suggested_extension.empty());
diff --git a/chrome/browser/extensions/api/gcm/gcm_api.cc b/chrome/browser/extensions/api/gcm/gcm_api.cc
index 851d1d1..dbf07dc 100644
--- a/chrome/browser/extensions/api/gcm/gcm_api.cc
+++ b/chrome/browser/extensions/api/gcm/gcm_api.cc
@@ -219,10 +219,9 @@
   api::gcm::OnMessage::Message message_arg;
   message_arg.data.additional_properties = message.data;
   if (!message.sender_id.empty())
-    message_arg.from = std::make_unique<std::string>(message.sender_id);
+    message_arg.from = message.sender_id;
   if (!message.collapse_key.empty()) {
-    message_arg.collapse_key =
-        std::make_unique<std::string>(message.collapse_key);
+    message_arg.collapse_key = message.collapse_key;
   }
 
   std::unique_ptr<Event> event(
@@ -244,8 +243,7 @@
     const std::string& app_id,
     const gcm::GCMClient::SendErrorDetails& send_error_details) {
   api::gcm::OnSendError::Error error;
-  error.message_id =
-      std::make_unique<std::string>(send_error_details.message_id);
+  error.message_id = send_error_details.message_id;
   error.error_message = GcmResultToError(send_error_details.result);
   error.details.additional_properties = send_error_details.additional_data;
 
diff --git a/chrome/browser/extensions/api/history/history_api.cc b/chrome/browser/extensions/api/history/history_api.cc
index 2fec4ae2..a71d4bb5 100644
--- a/chrome/browser/extensions/api/history/history_api.cc
+++ b/chrome/browser/extensions/api/history/history_api.cc
@@ -66,9 +66,8 @@
   HistoryItem history_item;
 
   history_item.id = base::NumberToString(row.id());
-  history_item.url = std::make_unique<std::string>(row.url().spec());
-  history_item.title =
-      std::make_unique<std::string>(base::UTF16ToUTF8(row.title()));
+  history_item.url = row.url().spec();
+  history_item.title = base::UTF16ToUTF8(row.title());
   history_item.last_visit_time = MilliSecondsFromTime(row.last_visit());
   history_item.typed_count = row.typed_count();
   history_item.visit_count = row.visit_count();
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index a18afaf..8cb12104 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -903,7 +903,7 @@
     OAuth2Info& oauth2_info =
         const_cast<OAuth2Info&>(OAuth2ManifestHandler::GetOAuth2Info(*ext));
     if ((fields_to_set & CLIENT_ID) != 0)
-      oauth2_info.client_id = std::make_unique<std::string>("client1");
+      oauth2_info.client_id = "client1";
     if ((fields_to_set & SCOPES) != 0) {
       oauth2_info.scopes.push_back("scope1");
       oauth2_info.scopes.push_back("scope2");
@@ -995,7 +995,7 @@
         api::identity::GetAuthTokenResult::FromValue(*result_value);
     ASSERT_TRUE(result);
 
-    EXPECT_NE(nullptr, result->token);
+    EXPECT_TRUE(result->token);
     *access_token = *result->token;
     EXPECT_NE(nullptr, result->granted_scopes);
     std::set<std::string> granted_scopes_map(result->granted_scopes->begin(),
@@ -1018,7 +1018,7 @@
         api::identity::GetAuthTokenResult::FromValue(result_value);
     ASSERT_TRUE(result);
 
-    ASSERT_NE(nullptr, result->token);
+    ASSERT_TRUE(result->token);
     *access_token = *result->token;
     ASSERT_NE(nullptr, result->granted_scopes);
     std::set<std::string> granted_scopes_map(result->granted_scopes->begin(),
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
index 94a6292..6228972 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
@@ -302,7 +302,7 @@
   RecordFunctionResult(IdentityGetAuthTokenError(), remote_consent_approved_);
 
   api::identity::GetAuthTokenResult result;
-  result.token = std::make_unique<std::string>(access_token);
+  result.token = access_token;
   result.granted_scopes = std::make_unique<std::vector<std::string>>(
       granted_scopes.begin(), granted_scopes.end());
 
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.cc b/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.cc
index addf4a97..0d08890 100644
--- a/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.cc
+++ b/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.cc
@@ -90,7 +90,7 @@
     return RespondNow(Error(image_writer::error::kUrlInvalid));
 
   std::string hash;
-  if (params->options.get() && params->options->image_hash.get()) {
+  if (params->options.get() && params->options->image_hash) {
     hash = *params->options->image_hash;
   }
 
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
index 16a13841..b3941be 100644
--- a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
+++ b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
@@ -354,15 +354,14 @@
 
     // For legacy reasons, we still put a |requestID| into the keyData, even
     // though there is already a |requestID| argument in OnKeyEvent.
-    keyboard_event.request_id = std::make_unique<std::string>(request_id);
+    keyboard_event.request_id = request_id;
 
     // If the given key event is from VK, it means the key event was simulated.
     // Sets the |extension_id| value so that the IME extension can ignore it.
     auto* properties = event.properties();
     if (properties &&
         properties->find(ui::kPropertyFromVK) != properties->end())
-      keyboard_event.extension_id =
-          std::make_unique<std::string>(extension_id_);
+      keyboard_event.extension_id = extension_id_;
 
     keyboard_event.key = GetKeyFromEvent(event);
     keyboard_event.code = event.code() == ui::DomCode::NONE
@@ -492,8 +491,7 @@
       // TODO(b/163645900): Add app type later.
       ash::input_method::TextFieldContextualInfo info;
       ash::input_method::GetTextFieldAppTypeAndKey(info);
-      private_api_input_context.app_key =
-          std::make_unique<std::string>(info.app_key);
+      private_api_input_context.app_key = info.app_key;
 
       auto args(
           input_method_private::OnFocus::Create(private_api_input_context));
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
index 4e0ed4e..ba0d2b4e 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -307,8 +307,7 @@
         extensions::api::management::EXTENSION_INSTALL_TYPE_OTHER;
     info.is_app = true;
     info.type = extensions::api::management::EXTENSION_TYPE_HOSTED_APP;
-    info.app_launch_url =
-        std::make_unique<std::string>(registrar.GetAppStartUrl(app_id).spec());
+    info.app_launch_url = registrar.GetAppStartUrl(app_id).spec();
 
     info.icons =
         std::make_unique<std::vector<extensions::api::management::IconInfo>>();
diff --git a/chrome/browser/extensions/api/notifications/notifications_api.cc b/chrome/browser/extensions/api/notifications/notifications_api.cc
index 8a60ead..b59bff3 100644
--- a/chrome/browser/extensions/api/notifications/notifications_api.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_api.cc
@@ -239,7 +239,7 @@
 
   // Then, handle any optional data that's been provided.
   message_center::RichNotificationData optional_fields;
-  if (options->app_icon_mask_url.get()) {
+  if (options->app_icon_mask_url) {
     gfx::Image small_icon_mask;
     if (!NotificationBitmapToGfxImage(
             image_scale, bitmap_sizes.app_icon_mask_size,
@@ -560,7 +560,7 @@
 
   const std::string extension_id(extension_->id());
   std::string notification_id;
-  if (params_->notification_id.get() && !params_->notification_id->empty()) {
+  if (params_->notification_id && !params_->notification_id->empty()) {
     // If the caller provided a notificationId, use that.
     notification_id = *params_->notification_id;
   } else {
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
index 2ca19d6..d6e6def66 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
@@ -83,9 +83,8 @@
 constexpr char kPasswordCheckScriptsCacheStateUmaKey[] =
     "PasswordManager.BulkCheck.ScriptsCacheState";
 
-std::unique_ptr<std::string> GetChangePasswordUrl(const GURL& url) {
-  return std::make_unique<std::string>(
-      password_manager::CreateChangePasswordUrl(url).spec());
+std::string GetChangePasswordUrl(const GURL& url) {
+  return password_manager::CreateChangePasswordUrl(url).spec();
 }
 
 }  // namespace
@@ -477,7 +476,7 @@
                last_completed_weak_check_);
   if (!last_check_completed.is_null()) {
     result.elapsed_time_since_last_check =
-        std::make_unique<std::string>(FormatElapsedTime(last_check_completed));
+        FormatElapsedTime(last_check_completed);
   }
 
   State state = bulk_leak_check_service_adapter_.GetBulkLeakCheckState();
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc b/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
index ed4ccee..030b7902 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
@@ -108,6 +108,7 @@
 using ::testing::AllOf;
 using ::testing::AtLeast;
 using ::testing::ElementsAre;
+using ::testing::Eq;
 using ::testing::Field;
 using ::testing::IsEmpty;
 using ::testing::IsNull;
@@ -255,11 +256,6 @@
     const std::string& detailed_origin,
     const absl::optional<std::string>& change_password_url,
     const std::u16string& username) {
-  auto change_password_url_field_matcher =
-      change_password_url.has_value()
-          ? Field(&PasswordUiEntry::change_password_url,
-                  Pointee(change_password_url.value()))
-          : Field(&PasswordUiEntry::change_password_url, IsNull());
   return AllOf(Field(&PasswordUiEntry::username, base::UTF16ToASCII(username)),
                Field(&PasswordUiEntry::urls,
                      ExpectUrls(formatted_origin, detailed_origin)));
@@ -277,8 +273,9 @@
   auto change_password_url_field_matcher =
       change_password_url.has_value()
           ? Field(&PasswordUiEntry::change_password_url,
-                  Pointee(change_password_url.value()))
-          : Field(&PasswordUiEntry::change_password_url, IsNull());
+                  change_password_url.value())
+          : Field(&PasswordUiEntry::change_password_url,
+                  testing::Eq(absl::nullopt));
   return AllOf(Field(&PasswordUiEntry::username, base::UTF16ToASCII(username)),
                change_password_url_field_matcher,
                Field(&PasswordUiEntry::urls,
@@ -1080,7 +1077,7 @@
 // treated as no completed run yet.
 TEST_F(PasswordCheckDelegateTest, LastTimePasswordCheckCompletedNotSet) {
   PasswordCheckStatus status = delegate().GetPasswordCheckStatus();
-  EXPECT_THAT(status.elapsed_time_since_last_check, IsNull());
+  EXPECT_FALSE(status.elapsed_time_since_last_check);
 }
 
 // Checks that a non-default kLastTimePasswordCheckCompleted pref value is
@@ -1092,7 +1089,7 @@
 
   PasswordCheckStatus status = delegate().GetPasswordCheckStatus();
   EXPECT_THAT(status.elapsed_time_since_last_check,
-              Pointee(std::string("5 minutes ago")));
+              Eq(std::string("5 minutes ago")));
 }
 
 // Checks that a transition into the idle state after starting a check results
@@ -1104,7 +1101,7 @@
   service()->set_state_and_notify(BulkLeakCheckService::State::kIdle);
   PasswordCheckStatus status = delegate().GetPasswordCheckStatus();
   EXPECT_THAT(status.elapsed_time_since_last_check,
-              Pointee(std::string("Just now")));
+              Eq(std::string("Just now")));
 }
 
 // Checks that processing a credential by the leak check updates the progress
@@ -1217,8 +1214,8 @@
 
   RunUntilIdle();
 
-  EXPECT_EQ(delegate().GetCompromisedCredentials().at(0).change_password_url,
-            nullptr);
+  EXPECT_FALSE(
+      delegate().GetCompromisedCredentials().at(0).change_password_url);
   EXPECT_EQ(
       GURL(*delegate().GetCompromisedCredentials().at(1).change_password_url)
           .path(),
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
index 529f189..b2c057d 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
@@ -163,8 +163,7 @@
   entry.username = base::UTF16ToUTF8(credential.username);
   // TODO(crbug.com/1345899): Fill the note field after authentication in
   // OnRequestCredentialDetailsAuthResult
-  entry.note =
-      std::make_unique<std::string>(base::UTF16ToUTF8(credential.note.value));
+  entry.note = base::UTF16ToUTF8(credential.note.value);
   entry.id = id;
   entry.stored_in = extensions::StoreSetFromCredential(credential);
   entry.is_android_credential =
@@ -175,9 +174,8 @@
             credential.federation_origin,
             url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
 
-    entry.federation_text =
-        std::make_unique<std::string>(l10n_util::GetStringFUTF8(
-            IDS_PASSWORDS_VIA_FEDERATION, formatted_origin));
+    entry.federation_text = l10n_util::GetStringFUTF8(
+        IDS_PASSWORDS_VIA_FEDERATION, formatted_origin);
   }
   return entry;
 }
@@ -781,8 +779,7 @@
 
   api::passwords_private::PasswordUiEntry password_ui_entry =
       CreatePasswordUiEntryFromCredentialUiEntry(id, *credential);
-  password_ui_entry.password =
-      std::make_unique<std::string>(base::UTF16ToUTF8(credential->password));
+  password_ui_entry.password = base::UTF16ToUTF8(credential->password);
   std::move(callback).Run(std::move(password_ui_entry));
 
   EmitHistogramsForCredentialAccess(
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_browsertest.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_browsertest.cc
index ad6469bc..e3e3dd8db 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_browsertest.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_browsertest.cc
@@ -53,7 +53,7 @@
   const GURL url(kUrl);
   api::passwords_private::PasswordUiEntry credential;
   credential.username = kUsername;
-  credential.change_password_url = std::make_unique<std::string>(kUrl);
+  credential.change_password_url = kUrl;
   base::MockCallback<
       PasswordsPrivateDelegate::StartAutomatedPasswordChangeCallback>
       apc_callback;
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
index eab6822..12acc07 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
@@ -56,7 +56,6 @@
 using ::testing::Eq;
 using ::testing::IsNull;
 using ::testing::Ne;
-using ::testing::Pointee;
 using ::testing::Return;
 using ::testing::SizeIs;
 using ::testing::StrictMock;
@@ -383,13 +382,13 @@
   api::passwords_private::PasswordUiEntry expected_entry1;
   expected_entry1.urls.link = "https://example1.com/";
   expected_entry1.username = "username1";
-  expected_entry1.note = std::make_unique<std::string>();
+  expected_entry1.note.emplace();
   expected_entry1.stored_in =
       api::passwords_private::PASSWORD_STORE_SET_ACCOUNT;
   api::passwords_private::PasswordUiEntry expected_entry2;
   expected_entry2.urls.link = "http://example2.com/login";
   expected_entry2.username = "";
-  expected_entry2.note = std::make_unique<std::string>("note");
+  expected_entry2.note = "note";
   expected_entry2.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
   EXPECT_CALL(callback,
               Run(testing::UnorderedElementsAre(
@@ -475,7 +474,7 @@
   EXPECT_CALL(callback, Run(SizeIs(1)))
       .WillOnce([](const PasswordsPrivateDelegate::UiEntries& passwords) {
         EXPECT_EQ("new_user", passwords[0].username);
-        EXPECT_THAT(passwords[0].note, Pointee(Eq("")));
+        EXPECT_THAT(passwords[0].note, Eq(""));
       });
   delegate.GetSavedPasswordsList(callback.Get());
 }
@@ -502,7 +501,7 @@
         EXPECT_EQ(sample_form.username_value,
                   base::UTF8ToUTF16(passwords[0].username));
         EXPECT_THAT(passwords[0].note,
-                    Pointee(Eq(base::UTF16ToUTF8(sample_form.notes[1].value))));
+                    Eq(base::UTF16ToUTF8(sample_form.notes[1].value)));
       });
   delegate.GetSavedPasswordsList(callback.Get());
   int sample_form_id = delegate.GetIdForCredential(
@@ -511,7 +510,7 @@
   api::passwords_private::ChangeSavedPasswordParams params;
   params.password = "new_pass";
   params.username = "new_user";
-  params.note = std::make_unique<std::string>("new note");
+  params.note = "new note";
 
   sample_form.password_value = u"new_pass";
   sample_form.username_value = u"new_user";
@@ -529,7 +528,7 @@
   EXPECT_CALL(callback, Run(SizeIs(1)))
       .WillOnce([](const PasswordsPrivateDelegate::UiEntries& passwords) {
         EXPECT_EQ("new_user", passwords[0].username);
-        EXPECT_THAT(passwords[0].note, Pointee(Eq("new note")));
+        EXPECT_THAT(passwords[0].note, Eq("new note"));
       });
   delegate.GetSavedPasswordsList(callback.Get());
 }
@@ -755,7 +754,7 @@
   EXPECT_CALL(password_callback, Run)
       .WillOnce(
           [&](absl::optional<api::passwords_private::PasswordUiEntry> entry) {
-            EXPECT_THAT(entry->password, Pointee(Eq("test")));
+            EXPECT_THAT(entry->password, Eq("test"));
             EXPECT_THAT(entry->username, Eq("test@gmail.com"));
           });
 
@@ -958,7 +957,7 @@
   expected_entry.urls.link =
       "https://play.google.com/store/apps/details?id=example.com";
   expected_entry.username = "test@gmail.com";
-  expected_entry.note = std::make_unique<std::string>();
+  expected_entry.note.emplace();
   expected_entry.is_android_credential = true;
   expected_entry.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
   EXPECT_CALL(callback, Run(testing::ElementsAre(PasswordUiEntryDataEquals(
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc
index 0474aa26..2736f65 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc
@@ -74,7 +74,7 @@
     const std::string& folder_name) {
   api::passwords_private::PasswordExportProgress params;
   params.status = status;
-  params.folder_name = std::make_unique<std::string>(std::move(folder_name));
+  params.folder_name = std::move(folder_name);
 
   base::Value::List event_value;
   event_value.Append(base::Value::FromUniquePtrValue(params.ToValue()));
diff --git a/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc b/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc
index c84d1a9..f7314cb7 100644
--- a/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc
+++ b/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc
@@ -158,8 +158,7 @@
     content::WebContents* web_contents) {
   api::passwords_private::PasswordUiEntry entry = CreateEntry(42);
   if (plaintext_password_.has_value()) {
-    entry.password = std::make_unique<std::string>(
-        base::UTF16ToUTF8(plaintext_password_.value()));
+    entry.password = base::UTF16ToUTF8(plaintext_password_.value());
     std::move(callback).Run(std::move(entry));
   } else {
     std::move(callback).Run(std::move(absl::nullopt));
@@ -224,8 +223,7 @@
   credential.urls.link = "https://example.com";
   credential.urls.signon_realm = "https://example.com";
   credential.is_android_credential = false;
-  credential.change_password_url =
-      std::make_unique<std::string>("https://example.com/change-password");
+  credential.change_password_url = "https://example.com/change-password";
   credential.compromised_info =
       std::make_unique<api::passwords_private::CompromisedInfo>();
   // Mar 03 2020 12:00:00 UTC
@@ -248,8 +246,7 @@
   credential.urls.shown = "example.com";
   credential.urls.link = "https://example.com";
   credential.is_android_credential = false;
-  credential.change_password_url =
-      std::make_unique<std::string>("https://example.com/change-password");
+  credential.change_password_url = "https://example.com/change-password";
   credential.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE;
   std::vector<api::passwords_private::PasswordUiEntry> credentials;
   credentials.push_back(std::move(credential));
@@ -305,10 +302,8 @@
   status.state = api::passwords_private::PASSWORD_CHECK_STATE_RUNNING;
   status.already_processed = 5;
   status.remaining_in_queue = 10;
-  status.elapsed_time_since_last_check =
-      std::make_unique<std::string>(base::UTF16ToUTF8(
-          TimeFormat::Simple(TimeFormat::FORMAT_ELAPSED,
-                             TimeFormat::LENGTH_SHORT, base::Minutes(5))));
+  status.elapsed_time_since_last_check = base::UTF16ToUTF8(TimeFormat::Simple(
+      TimeFormat::FORMAT_ELAPSED, TimeFormat::LENGTH_SHORT, base::Minutes(5)));
   return status;
 }
 
diff --git a/chrome/browser/extensions/api/printing/printing_api.cc b/chrome/browser/extensions/api/printing/printing_api.cc
index 19d8faf..557ac887 100644
--- a/chrome/browser/extensions/api/printing/printing_api.cc
+++ b/chrome/browser/extensions/api/printing/printing_api.cc
@@ -41,7 +41,7 @@
 
 void PrintingSubmitJobFunction::OnPrintJobSubmitted(
     absl::optional<api::printing::SubmitJobStatus> status,
-    std::unique_ptr<std::string> job_id,
+    absl::optional<std::string> job_id,
     absl::optional<std::string> error) {
   if (error.has_value()) {
     Respond(Error(error.value()));
diff --git a/chrome/browser/extensions/api/printing/printing_api.h b/chrome/browser/extensions/api/printing/printing_api.h
index 160dfbd..962d998 100644
--- a/chrome/browser/extensions/api/printing/printing_api.h
+++ b/chrome/browser/extensions/api/printing/printing_api.h
@@ -30,7 +30,7 @@
  private:
   void OnPrintJobSubmitted(
       absl::optional<api::printing::SubmitJobStatus> status,
-      std::unique_ptr<std::string> job_id,
+      absl::optional<std::string> job_id,
       absl::optional<std::string> error);
   DECLARE_EXTENSION_FUNCTION("printing.submitJob", PRINTING_SUBMITJOB)
 };
diff --git a/chrome/browser/extensions/api/printing/printing_api_handler.cc b/chrome/browser/extensions/api/printing/printing_api_handler.cc
index 41ef5f1..8641db3 100644
--- a/chrome/browser/extensions/api/printing/printing_api_handler.cc
+++ b/chrome/browser/extensions/api/printing/printing_api_handler.cc
@@ -185,8 +185,8 @@
     if (!error)
       status = api::printing::SUBMIT_JOB_STATUS_USER_REJECTED;
     base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(callback), status, nullptr, std::move(error)));
+        FROM_HERE, base::BindOnce(std::move(callback), status, absl::nullopt,
+                                  std::move(error)));
     return;
   }
 
@@ -201,7 +201,7 @@
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(std::move(callback), api::printing::SUBMIT_JOB_STATUS_OK,
-                     std::make_unique<std::string>(cups_id), absl::nullopt));
+                     cups_id, absl::nullopt));
 
   DCHECK(!base::Contains(print_jobs_, cups_id));
   print_jobs_[cups_id] =
diff --git a/chrome/browser/extensions/api/printing/printing_api_handler.h b/chrome/browser/extensions/api/printing/printing_api_handler.h
index 614f51b..f8853b3 100644
--- a/chrome/browser/extensions/api/printing/printing_api_handler.h
+++ b/chrome/browser/extensions/api/printing/printing_api_handler.h
@@ -54,7 +54,7 @@
  public:
   using SubmitJobCallback = base::OnceCallback<void(
       absl::optional<api::printing::SubmitJobStatus> status,
-      std::unique_ptr<std::string> job_id,
+      absl::optional<std::string> job_id,
       absl::optional<std::string> error)>;
   using GetPrintersCallback =
       base::OnceCallback<void(std::vector<api::printing::Printer>)>;
diff --git a/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc b/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc
index a59aaec..3919799 100644
--- a/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc
+++ b/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc
@@ -182,7 +182,7 @@
     const std::string& title,
     const std::string& ticket,
     const std::string& content_type,
-    std::unique_ptr<std::string> document_blob_uuid) {
+    absl::optional<std::string> document_blob_uuid) {
   api::printing::SubmitJobRequest request;
   request.job.printer_id = printer_id;
   request.job.title = title;
@@ -336,9 +336,8 @@
     // Create Blob with given data.
     std::unique_ptr<content::BlobHandle> blob = CreateMemoryBackedBlob(
         testing_profile_, kPdfExample, /*content_type=*/"");
-    auto params = ConstructSubmitJobParams(
-        kPrinterId, /*title=*/"", kCjt, "application/pdf",
-        std::make_unique<std::string>(blob->GetUUID()));
+    auto params = ConstructSubmitJobParams(kPrinterId, /*title=*/"", kCjt,
+                                           "application/pdf", blob->GetUUID());
     ASSERT_TRUE(params);
 
     base::RunLoop run_loop;
@@ -401,7 +400,7 @@
 
   void OnJobSubmitted(base::RepeatingClosure run_loop_closure,
                       absl::optional<api::printing::SubmitJobStatus> status,
-                      std::unique_ptr<std::string> job_id,
+                      absl::optional<std::string> job_id,
                       absl::optional<std::string> error) {
     submit_job_status_ = status;
     job_id_ = std::move(job_id);
@@ -438,7 +437,7 @@
   std::unique_ptr<PrintingAPIHandler> printing_api_handler_;
   scoped_refptr<const Extension> extension_;
   absl::optional<api::printing::SubmitJobStatus> submit_job_status_;
-  std::unique_ptr<std::string> job_id_;
+  absl::optional<std::string> job_id_;
   absl::optional<base::Value> capabilities_;
   absl::optional<api::printing::PrinterStatus> printer_status_;
   absl::optional<std::string> error_;
@@ -685,7 +684,7 @@
 
   auto params =
       ConstructSubmitJobParams(kPrinterId, /*title=*/"", kCjt, "image/jpeg",
-                               /*document_blob_uuid=*/nullptr);
+                               /*document_blob_uuid=*/absl::nullopt);
   ASSERT_TRUE(params);
 
   base::RunLoop run_loop;
@@ -711,7 +710,7 @@
 
   auto params = ConstructSubmitJobParams(kPrinterId, /*title=*/"",
                                          kIncompleteCjt, "application/pdf",
-                                         /*document_blob_uuid=*/nullptr);
+                                         /*document_blob_uuid=*/absl::nullopt);
   ASSERT_TRUE(params);
 
   base::RunLoop run_loop;
@@ -732,7 +731,7 @@
 TEST_F(PrintingAPIHandlerUnittest, SubmitJob_InvalidPrinterId) {
   auto params = ConstructSubmitJobParams(kPrinterId, /*title=*/"", kCjt,
                                          "application/pdf",
-                                         /*document_blob_uuid=*/nullptr);
+                                         /*document_blob_uuid=*/absl::nullopt);
   ASSERT_TRUE(params);
 
   base::RunLoop run_loop;
@@ -756,7 +755,7 @@
 
   auto params = ConstructSubmitJobParams(kPrinterId, /*title=*/"", kCjt,
                                          "application/pdf",
-                                         /*document_blob_uuid=*/nullptr);
+                                         /*document_blob_uuid=*/absl::nullopt);
   ASSERT_TRUE(params);
 
   base::RunLoop run_loop;
@@ -782,7 +781,7 @@
 
   auto params = ConstructSubmitJobParams(kPrinterId, /*title=*/"", kCjt,
                                          "application/pdf",
-                                         /*document_blob_uuid=*/nullptr);
+                                         /*document_blob_uuid=*/absl::nullopt);
   ASSERT_TRUE(params);
 
   base::RunLoop run_loop;
@@ -807,9 +806,8 @@
   caps->capabilities = ConstructPrinterCapabilities();
   SetCaps(kPrinterId, std::move(caps));
 
-  auto params = ConstructSubmitJobParams(
-      kPrinterId, /*title=*/"", kCjt, "application/pdf",
-      std::make_unique<std::string>("invalid_uuid"));
+  auto params = ConstructSubmitJobParams(kPrinterId, /*title=*/"", kCjt,
+                                         "application/pdf", "invalid_uuid");
   ASSERT_TRUE(params);
 
   base::RunLoop run_loop;
@@ -837,9 +835,8 @@
   // Create Blob with given data.
   std::unique_ptr<content::BlobHandle> blob = CreateMemoryBackedBlob(
       testing_profile_, kPdfExample, /*content_type=*/"");
-  auto params = ConstructSubmitJobParams(
-      kPrinterId, /*title=*/"", kCjt, "application/pdf",
-      std::make_unique<std::string>(blob->GetUUID()));
+  auto params = ConstructSubmitJobParams(kPrinterId, /*title=*/"", kCjt,
+                                         "application/pdf", blob->GetUUID());
   ASSERT_TRUE(params);
 
   base::RunLoop run_loop;
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index b637991..0ab48d38 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -405,8 +405,7 @@
   params.url = url.spec();
   params.reason = reason;
   if (net_error_code < 0) {
-    params.net_error_code =
-        std::make_unique<std::string>(base::NumberToString(net_error_code));
+    params.net_error_code = base::NumberToString(net_error_code);
   }
   params.user_name = GetProfileUserName();
 
@@ -454,8 +453,7 @@
   params.url = url.spec();
   params.reason = reason;
   if (net_error_code < 0) {
-    params.net_error_code =
-        std::make_unique<std::string>(base::NumberToString(net_error_code));
+    params.net_error_code = base::NumberToString(net_error_code);
   }
   params.user_name = GetProfileUserName();
 
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc
index da0ba06..b2d47710 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc
@@ -21,8 +21,7 @@
   // Add all referrer chain entry fields to the entry.
   entry.url = referrer.url();
   if (referrer.has_main_frame_url()) {
-    entry.main_frame_url =
-        std::make_unique<std::string>(referrer.main_frame_url());
+    entry.main_frame_url = referrer.main_frame_url();
   }
   // This url type value is deprecated and should not be used.
   DCHECK_NE(
@@ -59,11 +58,10 @@
       entry.ip_addresses->emplace_back(ip_address);
   }
   if (referrer.has_referrer_url()) {
-    entry.referrer_url = std::make_unique<std::string>(referrer.referrer_url());
+    entry.referrer_url = referrer.referrer_url();
   }
   if (referrer.has_referrer_main_frame_url()) {
-    entry.referrer_main_frame_url =
-        std::make_unique<std::string>(referrer.referrer_main_frame_url());
+    entry.referrer_main_frame_url = referrer.referrer_main_frame_url();
   }
   if (referrer.has_is_retargeting())
     entry.is_retargeting = referrer.is_retargeting();
@@ -76,7 +74,7 @@
     entry.server_redirect_chain->reserve(referrer.server_redirect_chain_size());
     for (const auto& server_redirect : referrer.server_redirect_chain()) {
       safe_browsing_private::ServerRedirect result;
-      result.url = std::make_unique<std::string>(server_redirect.url());
+      result.url = server_redirect.url();
       entry.server_redirect_chain->emplace_back(std::move(result));
     }
   }
diff --git a/chrome/browser/extensions/api/sessions/sessions_api.cc b/chrome/browser/extensions/api/sessions/sessions_api.cc
index 7eba715..499d1c8 100644
--- a/chrome/browser/extensions/api/sessions/sessions_api.cc
+++ b/chrome/browser/extensions/api/sessions/sessions_api.cc
@@ -91,15 +91,13 @@
   const GURL& url = current_navigation.virtual_url();
   std::string title = base::UTF16ToUTF8(current_navigation.title());
 
-  tab_struct.session_id = std::make_unique<std::string>(session_id);
-  tab_struct.url = std::make_unique<std::string>(url.spec());
-  tab_struct.fav_icon_url =
-      std::make_unique<std::string>(current_navigation.favicon_url().spec());
+  tab_struct.session_id = session_id;
+  tab_struct.url = url.spec();
+  tab_struct.fav_icon_url = current_navigation.favicon_url().spec();
   if (!title.empty()) {
-    tab_struct.title = std::make_unique<std::string>(title);
+    tab_struct.title = title;
   } else {
-    tab_struct.title = std::make_unique<std::string>(
-        base::UTF16ToUTF8(url_formatter::FormatUrl(url)));
+    tab_struct.title = base::UTF16ToUTF8(url_formatter::FormatUrl(url));
   }
   tab_struct.index = index;
   tab_struct.pinned = pinned;
@@ -119,7 +117,7 @@
     const api::windows::WindowState& state) {
   std::unique_ptr<api::windows::Window> window_struct(new api::windows::Window);
   window_struct->tabs = std::move(tabs);
-  window_struct->session_id = std::make_unique<std::string>(session_id);
+  window_struct->session_id = session_id;
   window_struct->incognito = false;
   window_struct->always_on_top = false;
   window_struct->focused = false;
diff --git a/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc b/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc
index e9eb906..3890b35 100644
--- a/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc
+++ b/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc
@@ -47,8 +47,8 @@
   } else if (!profile_->IsSameOrParent(
                  ProfileManager::GetPrimaryUserProfile())) {
     out_pref->controlled_by = settings_api::CONTROLLED_BY_PRIMARY_USER;
-    out_pref->controlled_by_name = std::make_unique<std::string>(
-        user_manager::UserManager::Get()->GetPrimaryUser()->GetDisplayEmail());
+    out_pref->controlled_by_name =
+        user_manager::UserManager::Get()->GetPrimaryUser()->GetDisplayEmail();
     out_pref->enforcement = settings_api::ENFORCEMENT_ENFORCED;
   }
   // Time zone settings can be policy-bound (for all users), or primary-user
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 67dcebc2..6e85001 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -1007,11 +1007,10 @@
     pref_object->controlled_by =
         settings_api::ControlledBy::CONTROLLED_BY_PRIMARY_USER;
     pref_object->enforcement = settings_api::Enforcement::ENFORCEMENT_ENFORCED;
-    pref_object->controlled_by_name =
-        std::make_unique<std::string>(user_manager::UserManager::Get()
+    pref_object->controlled_by_name = user_manager::UserManager::Get()
                                           ->GetPrimaryUser()
                                           ->GetAccountId()
-                                          .GetUserEmail());
+                                          .GetUserEmail();
     return pref_object;
   }
 #endif
@@ -1051,8 +1050,8 @@
     pref_object->controlled_by =
         settings_api::ControlledBy::CONTROLLED_BY_OWNER;
     pref_object->enforcement = settings_api::Enforcement::ENFORCEMENT_ENFORCED;
-    pref_object->controlled_by_name = std::make_unique<std::string>(
-        user_manager::UserManager::Get()->GetOwnerAccountId().GetUserEmail());
+    pref_object->controlled_by_name =
+        user_manager::UserManager::Get()->GetOwnerAccountId().GetUserEmail();
     return pref_object;
   }
 
@@ -1079,9 +1078,8 @@
     pref_object->controlled_by =
         settings_api::ControlledBy::CONTROLLED_BY_EXTENSION;
     pref_object->enforcement = settings_api::Enforcement::ENFORCEMENT_ENFORCED;
-    pref_object->extension_id = std::make_unique<std::string>(extension->id());
-    pref_object->controlled_by_name =
-        std::make_unique<std::string>(extension->name());
+    pref_object->extension_id = extension->id();
+    pref_object->controlled_by_name = extension->name();
     bool can_be_disabled =
         !ExtensionSystem::Get(profile_)->management_policy()->MustRemainEnabled(
             extension, nullptr);
diff --git a/chrome/browser/extensions/api/side_panel/side_panel_service.cc b/chrome/browser/extensions/api/side_panel/side_panel_service.cc
index 63459fc..3308d8b 100644
--- a/chrome/browser/extensions/api/side_panel/side_panel_service.cc
+++ b/chrome/browser/extensions/api/side_panel/side_panel_service.cc
@@ -22,7 +22,7 @@
   auto path = SidePanelInfo::GetDefaultPath(&extension);
   api::side_panel::PanelOptions options;
   if (!path.empty()) {
-    options.path = std::make_unique<std::string>(std::string(path));
+    options.path = std::string(path);
     options.enabled = true;
   }
   return options;
diff --git a/chrome/browser/extensions/api/socket/tls_socket_unittest.cc b/chrome/browser/extensions/api/socket/tls_socket_unittest.cc
index 92183e60..d3dbc92 100644
--- a/chrome/browser/extensions/api/socket/tls_socket_unittest.cc
+++ b/chrome/browser/extensions/api/socket/tls_socket_unittest.cc
@@ -213,8 +213,8 @@
   auto socket = CreateTCPSocket();
   api::socket::SecureOptions options;
   options.tls_version = std::make_unique<api::socket::TLSVersionConstraints>();
-  options.tls_version->min = std::make_unique<std::string>("tls1.1");
-  options.tls_version->max = std::make_unique<std::string>("tls1.2");
+  options.tls_version->min = "tls1.1";
+  options.tls_version->max = "tls1.2";
   int net_error = net::ERR_FAILED;
   base::RunLoop run_loop;
   socket->UpgradeToTLS(
@@ -254,8 +254,8 @@
   auto socket = CreateTCPSocket();
   api::socket::SecureOptions options;
   options.tls_version = std::make_unique<api::socket::TLSVersionConstraints>();
-  options.tls_version->min = std::make_unique<std::string>("tls1.3");
-  options.tls_version->max = std::make_unique<std::string>("tls1.3");
+  options.tls_version->min = "tls1.3";
+  options.tls_version->max = "tls1.3";
   int net_error = net::ERR_FAILED;
   base::RunLoop run_loop;
   socket->UpgradeToTLS(
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc
index b9626cf..e7f79a9 100644
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc
+++ b/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc
@@ -140,7 +140,7 @@
         continue;
       }
 
-      if (params->query_info.title.get() &&
+      if (params->query_info.title &&
           !base::MatchPattern(visual_data->title(),
                               base::UTF8ToUTF16(*params->query_info.title))) {
         continue;
@@ -187,7 +187,7 @@
     color = tab_groups_util::ColorToColorId(params->update_properties.color);
 
   std::u16string title = visual_data->title();
-  if (params->update_properties.title.get())
+  if (params->update_properties.title)
     title = base::UTF8ToUTF16(*params->update_properties.title);
 
   TabStripModel* tab_strip_model =
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_util.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_util.cc
index d8eece1..39653c9 100644
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_util.cc
+++ b/chrome/browser/extensions/api/tab_groups/tab_groups_util.cc
@@ -45,8 +45,7 @@
   tab_group_object->id = GetGroupId(id);
   tab_group_object->collapsed = visual_data.is_collapsed();
   tab_group_object->color = ColorIdToColor(visual_data.color());
-  tab_group_object->title =
-      std::make_unique<std::string>(base::UTF16ToUTF8(visual_data.title()));
+  tab_group_object->title = base::UTF16ToUTF8(visual_data.title());
   tab_group_object->window_id = GetWindowIdOfGroup(id);
 
   return tab_group_object;
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 977261d..a48da0d 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -1105,9 +1105,7 @@
     }
   }
 
-  std::string title;
-  if (params->query_info.title.get())
-    title = *params->query_info.title;
+  std::string title = params->query_info.title.value_or(std::string());
 
   int window_id = extension_misc::kUnknownWindowId;
   if (params->query_info.window_id)
@@ -1501,7 +1499,7 @@
   // -favIconUrl
 
   // Navigate the tab to a new location if the url is different.
-  if (params->update_properties.url.get()) {
+  if (params->update_properties.url) {
     std::string updated_url = *params->update_properties.url;
     if (browser->profile()->IsIncognitoProfile() &&
         !IsURLAllowedInIncognito(GURL(updated_url), browser->profile())) {
diff --git a/chrome/browser/extensions/api/terminal/terminal_private_api.cc b/chrome/browser/extensions/api/terminal/terminal_private_api.cc
index 46d4aa2..76cdf7b 100644
--- a/chrome/browser/extensions/api/terminal/terminal_private_api.cc
+++ b/chrome/browser/extensions/api/terminal/terminal_private_api.cc
@@ -685,7 +685,7 @@
   auto& data = params->data;
   if (data) {
     if (data->url) {
-      url = data->url.get();
+      url = &*data->url;
     }
     if (data->as_tab) {
       as_tab = *data->as_tab;
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index f11df70..743a14b 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -500,12 +500,10 @@
   for (auto& item : updated_items.GetListDeprecated()) {
     keyboard_api::ClipboardItem clipboard_item;
     if (item.FindKey("imageData")) {
-      clipboard_item.image_data =
-          std::make_unique<std::string>(item.FindKey("imageData")->GetString());
+      clipboard_item.image_data = item.FindKey("imageData")->GetString();
     }
     if (item.FindKey("textData")) {
-      clipboard_item.text_data =
-          std::make_unique<std::string>(item.FindKey("textData")->GetString());
+      clipboard_item.text_data = item.FindKey("textData")->GetString();
     }
     if (item.FindKey("idToken")) {
       clipboard_item.id = item.FindKey("idToken")->GetString();
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
index 92adb54..d5a9b9633 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
@@ -536,8 +536,8 @@
   // Only set the parentDocumentId value if we have a parent.
   if (content::RenderFrameHost* parent_frame_host =
           render_frame_host->GetParentOrOuterDocument()) {
-    frame_details.parent_document_id = std::make_unique<std::string>(
-        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString());
+    frame_details.parent_document_id =
+        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString();
   }
   frame_details.frame_type =
       ExtensionApiFrameIdMap::GetFrameType(render_frame_host);
@@ -604,9 +604,9 @@
             // Only set the parentDocumentId value if we have a parent.
             if (content::RenderFrameHost* parent_frame_host =
                     render_frame_host->GetParentOrOuterDocument()) {
-              frame.parent_document_id = std::make_unique<std::string>(
+              frame.parent_document_id =
                   ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host)
-                      .ToString());
+                      .ToString();
             }
             frame.frame_type =
                 ExtensionApiFrameIdMap::GetFrameType(render_frame_host);
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc
index edba29b..471eddc5 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc
@@ -79,8 +79,8 @@
   // Only set the parentDocumentId value if we have a parent.
   if (content::RenderFrameHost* parent_frame_host =
           navigation_handle->GetParentFrameOrOuterDocument()) {
-    details.parent_document_id = std::make_unique<std::string>(
-        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString());
+    details.parent_document_id =
+        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString();
   }
   details.frame_type = ExtensionApiFrameIdMap::GetFrameType(navigation_handle);
   details.document_lifecycle =
@@ -188,8 +188,8 @@
   // Only set the parentDocumentId value if we have a parent.
   if (content::RenderFrameHost* parent_frame_host =
           frame_host->GetParentOrOuterDocument()) {
-    details.parent_document_id = std::make_unique<std::string>(
-        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString());
+    details.parent_document_id =
+        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString();
   }
   details.frame_type = ExtensionApiFrameIdMap::GetFrameType(frame_host);
   details.document_lifecycle =
@@ -220,8 +220,8 @@
   // Only set the parentDocumentId value if we have a parent.
   if (content::RenderFrameHost* parent_frame_host =
           frame_host->GetParentOrOuterDocument()) {
-    details.parent_document_id = std::make_unique<std::string>(
-        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString());
+    details.parent_document_id =
+        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString();
   }
   details.frame_type = ExtensionApiFrameIdMap::GetFrameType(frame_host);
   details.document_lifecycle =
@@ -291,8 +291,8 @@
   // Only set the parentDocumentId value if we have a parent.
   if (content::RenderFrameHost* parent_frame_host =
           frame_host->GetParentOrOuterDocument()) {
-    details.parent_document_id = std::make_unique<std::string>(
-        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString());
+    details.parent_document_id =
+        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString();
   }
   details.frame_type = ExtensionApiFrameIdMap::GetFrameType(frame_host);
   details.document_lifecycle =
@@ -325,8 +325,8 @@
   // Only set the parentDocumentId value if we have a parent.
   if (content::RenderFrameHost* parent_frame_host =
           navigation_handle->GetParentFrameOrOuterDocument()) {
-    details.parent_document_id = std::make_unique<std::string>(
-        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString());
+    details.parent_document_id =
+        ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString();
   }
   details.frame_type = ExtensionApiFrameIdMap::GetFrameType(navigation_handle);
   details.document_lifecycle =
diff --git a/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc b/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc
index eb49b55..52b4f6f 100644
--- a/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc
+++ b/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc
@@ -33,7 +33,7 @@
           GetSenderWebContents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
           params->instance_id));
 
-  if (params->create_properties.id.get()) {
+  if (params->create_properties.id) {
     id.string_uid = *params->create_properties.id;
   } else {
     // The Generated Id is added by web_view_internal_custom_bindings.js.
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index fb8672c6..68ef0e3 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -459,21 +459,17 @@
   tab_object->width = contents_size.width();
   tab_object->height = contents_size.height();
 
-  tab_object->url =
-      std::make_unique<std::string>(contents->GetLastCommittedURL().spec());
+  tab_object->url = contents->GetLastCommittedURL().spec();
   NavigationEntry* pending_entry = contents->GetController().GetPendingEntry();
   if (pending_entry) {
-    tab_object->pending_url =
-        std::make_unique<std::string>(pending_entry->GetVirtualURL().spec());
+    tab_object->pending_url = pending_entry->GetVirtualURL().spec();
   }
-  tab_object->title =
-      std::make_unique<std::string>(base::UTF16ToUTF8(contents->GetTitle()));
+  tab_object->title = base::UTF16ToUTF8(contents->GetTitle());
   // TODO(tjudkins) This should probably use the LastCommittedEntry() for
   // consistency.
   NavigationEntry* visible_entry = contents->GetController().GetVisibleEntry();
   if (visible_entry && visible_entry->GetFavicon().valid) {
-    tab_object->fav_icon_url =
-        std::make_unique<std::string>(visible_entry->GetFavicon().url.spec());
+    tab_object->fav_icon_url = visible_entry->GetFavicon().url.spec();
   }
   if (tab_strip) {
     WebContents* opener = tab_strip->GetOpenerOfWebContentsAt(tab_index);
@@ -574,8 +570,8 @@
       break;
     case TabMutedReason::EXTENSION:
       info->reason = api::tabs::MUTED_INFO_REASON_EXTENSION;
-      info->extension_id = std::make_unique<std::string>(
-          LastMuteMetadata::FromWebContents(contents)->extension_id);
+      info->extension_id =
+          LastMuteMetadata::FromWebContents(contents)->extension_id;
       DCHECK(!info->extension_id->empty());
       break;
   }
@@ -625,8 +621,7 @@
       tab->fav_icon_url.reset();
       break;
     case kScrubTabUrlToOrigin:
-      tab->url = std::make_unique<std::string>(
-          GURL(*tab->url).DeprecatedGetOriginAsURL().spec());
+      tab->url = GURL(*tab->url).DeprecatedGetOriginAsURL().spec();
       break;
     case kDontScrubTab:
       break;
@@ -639,8 +634,8 @@
         tab->pending_url.reset();
         break;
       case kScrubTabUrlToOrigin:
-        tab->pending_url = std::make_unique<std::string>(
-            GURL(*tab->pending_url).DeprecatedGetOriginAsURL().spec());
+        tab->pending_url =
+            GURL(*tab->pending_url).DeprecatedGetOriginAsURL().spec();
         break;
       case kDontScrubTab:
         break;
diff --git a/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc b/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
index 158481b..870637b 100644
--- a/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
+++ b/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
@@ -1180,8 +1180,7 @@
 
   api::system_display::DisplayProperties info;
   info.bounds_origin_x = 300;
-  info.mirroring_source_id =
-      std::make_unique<std::string>(base::NumberToString(primary.id()));
+  info.mirroring_source_id = base::NumberToString(primary.id());
 
   EXPECT_FALSE(
       CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
@@ -1639,7 +1638,7 @@
     // Mirroring destination ids not specified fails.
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
-    info.mirroring_source_id = std::make_unique<std::string>("1000000");
+    info.mirroring_source_id = "1000000";
     EXPECT_FALSE(SetMirrorMode(info));
   }
 
@@ -1647,7 +1646,7 @@
     // Mirroring source id in bad format fails.
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
-    info.mirroring_source_id = std::make_unique<std::string>("bad_format_id");
+    info.mirroring_source_id = "bad_format_id";
     info.mirroring_destination_ids =
         std::make_unique<std::vector<std::string>>();
     EXPECT_FALSE(SetMirrorMode(info));
@@ -1657,7 +1656,7 @@
     // Mirroring destination id in bad format fails.
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
-    info.mirroring_source_id = std::make_unique<std::string>("1000000");
+    info.mirroring_source_id = "1000000";
     info.mirroring_destination_ids =
         std::make_unique<std::vector<std::string>>();
     info.mirroring_destination_ids->emplace_back("bad_format_id");
@@ -1669,7 +1668,7 @@
     EXPECT_EQ(1U, display_manager()->num_connected_displays());
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
-    info.mirroring_source_id = std::make_unique<std::string>("1000000");
+    info.mirroring_source_id = "1000000";
     info.mirroring_destination_ids =
         std::make_unique<std::vector<std::string>>();
     EXPECT_FALSE(SetMirrorMode(info));
@@ -1685,7 +1684,7 @@
     // Mirroring source id not found fails.
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
-    info.mirroring_source_id = std::make_unique<std::string>("1000000");
+    info.mirroring_source_id = "1000000";
     info.mirroring_destination_ids =
         std::make_unique<std::vector<std::string>>();
     EXPECT_FALSE(SetMirrorMode(info));
@@ -1695,8 +1694,7 @@
     // Mirroring destination ids empty fails.
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
-    info.mirroring_source_id =
-        std::make_unique<std::string>(base::NumberToString(id_list[0]));
+    info.mirroring_source_id = base::NumberToString(id_list[0]);
     info.mirroring_destination_ids =
         std::make_unique<std::vector<std::string>>();
     EXPECT_FALSE(SetMirrorMode(info));
@@ -1706,8 +1704,7 @@
     // Mirroring destination ids not found fails.
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
-    info.mirroring_source_id =
-        std::make_unique<std::string>(base::NumberToString(id_list[0]));
+    info.mirroring_source_id = base::NumberToString(id_list[0]);
     info.mirroring_destination_ids =
         std::make_unique<std::vector<std::string>>();
     info.mirroring_destination_ids->emplace_back(
@@ -1719,8 +1716,7 @@
     // Duplicate display id fails.
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
-    info.mirroring_source_id =
-        std::make_unique<std::string>(base::NumberToString(id_list[0]));
+    info.mirroring_source_id = base::NumberToString(id_list[0]);
     info.mirroring_destination_ids =
         std::make_unique<std::vector<std::string>>();
     info.mirroring_destination_ids->emplace_back(
@@ -1733,8 +1729,7 @@
     // one).
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
-    info.mirroring_source_id =
-        std::make_unique<std::string>(base::NumberToString(id_list[0]));
+    info.mirroring_source_id = base::NumberToString(id_list[0]);
     info.mirroring_destination_ids =
         std::make_unique<std::vector<std::string>>();
     info.mirroring_destination_ids->emplace_back(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 1cc1f8c..b6f9ae7 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1898,6 +1898,11 @@
     "expiry_milestone": 110
   },
   {
+    "name": "enable-cros-ime-tray-hide-voice-button",
+    "owners": [ "greywang", "essential-inputs-team@google.com" ],
+    "expiry_milestone": 115
+  },
+  {
     "name": "enable-cros-language-settings-update-japanese",
     "owners": [ "keithlee", "essential-inputs-team@google.com" ],
     "expiry_milestone": 110
@@ -2437,6 +2442,11 @@
     "expiry_milestone": 140
   },
   {
+    "name": "enable-javascript-experimental-shared-memory",
+    "owners": [ "syg", "dinfuehr", "pthier" ],
+    "expiry_milestone": 120
+  },
+  {
     "name": "enable-javascript-harmony",
     "owners": [ "adamk", "hablich" ],
     // This flag is used by web developers to test upcoming javascript features.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 33e6ced..64d3a72 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1747,6 +1747,13 @@
     "conflict with the latest JavaScript features. This flag allows disabling "
     "support of those features for compatibility with such pages.";
 
+const char kJavascriptExperimentalSharedMemoryName[] =
+    "Experimental JavaScript shared memory features";
+const char kJavascriptExperimentalSharedMemoryDescription[] =
+    "Enable web pages to use non-standard, experimental JavaScript shared "
+    "memory features. Their use requires the same HTTP headers required by "
+    "cross-thread usage of SharedArrayBuffers (i.e. COOP and COEP).";
+
 const char kJourneysName[] = "History Journeys";
 const char kJourneysDescription[] = "Enables the History Journeys UI.";
 
@@ -5510,6 +5517,11 @@
 const char kImeAssistPersonalInfoDescription[] =
     "Enable auto-complete suggestions on personal infomation for native IME.";
 
+const char kImeTrayHideVoiceButtonName[] =
+    "Hides redudant voice button in IME tray";
+const char kImeTrayHideVoiceButtonDescription[] =
+    "Hides voice button in IME tray when mic icon is shown in the shelf";
+
 const char kVirtualKeyboardNewHeaderName[] =
     "Enable new header for virtual keyboard";
 const char kVirtualKeyboardNewHeaderDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 536048c..6950e52 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -978,6 +978,9 @@
 extern const char kJavascriptHarmonyShippingName[];
 extern const char kJavascriptHarmonyShippingDescription[];
 
+extern const char kJavascriptExperimentalSharedMemoryName[];
+extern const char kJavascriptExperimentalSharedMemoryDescription[];
+
 extern const char kJourneysName[];
 extern const char kJourneysDescription[];
 
@@ -3152,6 +3155,9 @@
 extern const char kImeAssistMultiWordLacrosSupportName[];
 extern const char kImeAssistMultiWordLacrosSupportDescription[];
 
+extern const char kImeTrayHideVoiceButtonName[];
+extern const char kImeTrayHideVoiceButtonDescription[];
+
 extern const char kLacrosProfileMigrationForAnyUserName[];
 extern const char kLacrosProfileMigrationForAnyUserDescription[];
 
diff --git a/chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.cc b/chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.cc
index 1008e584..7f74ee1 100644
--- a/chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.cc
+++ b/chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.cc
@@ -206,14 +206,9 @@
   offline_pages::RecentTabHelper* tab_helper =
       RecentTabHelper::FromWebContents(web_contents);
   if (!tab_helper) {
-    if (request_id != OfflinePageModel::kInvalidOfflineId) {
-      offline_pages::RequestCoordinator* request_coordinator =
-          offline_pages::RequestCoordinatorFactory::GetForBrowserContext(
-              web_contents->GetBrowserContext());
-      if (request_coordinator)
-        request_coordinator->EnableForOffliner(request_id, client_id);
-      else
-        DVLOG(1) << "SavePageIfNotNavigatedAway has no valid coordinator.";
+    if (request_id != OfflinePageModel::kInvalidOfflineId &&
+        request_coordinator) {
+      request_coordinator->EnableForOffliner(request_id, client_id);
     }
     return;
   }
diff --git a/chrome/browser/printing/print_backend_browsertest.cc b/chrome/browser/printing/print_backend_browsertest.cc
index 9c9472d..4a8a91c 100644
--- a/chrome/browser/printing/print_backend_browsertest.cc
+++ b/chrome/browser/printing/print_backend_browsertest.cc
@@ -637,8 +637,7 @@
   EXPECT_EQ(StartPrintingAndWait(print_settings), mojom::ResultCode::kSuccess);
 
   absl::optional<mojom::ResultCode> result = RenderPageAndWait();
-  ASSERT_TRUE(result.has_value());
-  EXPECT_EQ(result.value(), mojom::ResultCode::kSuccess);
+  EXPECT_EQ(result, mojom::ResultCode::kSuccess);
 }
 #endif  // BUILDFLAG(IS_WIN)
 
@@ -656,8 +655,7 @@
   EXPECT_EQ(StartPrintingAndWait(print_settings), mojom::ResultCode::kSuccess);
 
   absl::optional<mojom::ResultCode> result = RenderDocumentAndWait();
-  ASSERT_TRUE(result.has_value());
-  EXPECT_EQ(result.value(), mojom::ResultCode::kSuccess);
+  EXPECT_EQ(result, mojom::ResultCode::kSuccess);
 }
 #endif  // !BUILDFLAG(IS_WIN)
 
@@ -678,8 +676,7 @@
 #else
   absl::optional<mojom::ResultCode> result = RenderDocumentAndWait();
 #endif
-  ASSERT_TRUE(result.has_value());
-  EXPECT_EQ(result.value(), mojom::ResultCode::kSuccess);
+  EXPECT_EQ(result, mojom::ResultCode::kSuccess);
 
   EXPECT_EQ(DocumentDoneAndWait(), mojom::ResultCode::kSuccess);
 }
diff --git a/chrome/browser/printing/print_browsertest.cc b/chrome/browser/printing/print_browsertest.cc
index 796c8e3..5b1a0a1e 100644
--- a/chrome/browser/printing/print_browsertest.cc
+++ b/chrome/browser/printing/print_browsertest.cc
@@ -3618,8 +3618,7 @@
              /*has_selection=*/false);
 
   print_view_manager->WaitOnScanning();
-  ASSERT_TRUE(print_view_manager->preview_allowed().has_value());
-  ASSERT_EQ(print_view_manager->preview_allowed().value(),
+  ASSERT_EQ(print_view_manager->preview_allowed(),
             content_analysis_allows_print());
 }
 
diff --git a/chrome/browser/printing/print_view_manager_basic_unittest.cc b/chrome/browser/printing/print_view_manager_basic_unittest.cc
index 7869f34..b4aa648 100644
--- a/chrome/browser/printing/print_view_manager_basic_unittest.cc
+++ b/chrome/browser/printing/print_view_manager_basic_unittest.cc
@@ -21,7 +21,7 @@
  protected:
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
-    InitializePrinting(web_contents());
+    InitializePrintingForWebContents(web_contents());
     content::RenderFrameHostTester::For(main_rfh())
         ->InitializeRenderFrameIfNeeded();
   }
diff --git a/chrome/browser/printing/printing_init.cc b/chrome/browser/printing/printing_init.cc
index 57e5bbf..966f860 100644
--- a/chrome/browser/printing/printing_init.cc
+++ b/chrome/browser/printing/printing_init.cc
@@ -20,7 +20,7 @@
 
 namespace printing {
 
-void InitializePrinting(content::WebContents* web_contents) {
+void InitializePrintingForWebContents(content::WebContents* web_contents) {
   // Native Headless mode uses a minimalistic Print Manager implementation that
   // shortcuts most of the callbacks providing only print to PDF functionality.
   if (headless::IsChromeNativeHeadless()) {
diff --git a/chrome/browser/printing/printing_init.h b/chrome/browser/printing/printing_init.h
index 0660585..8abc8e1 100644
--- a/chrome/browser/printing/printing_init.h
+++ b/chrome/browser/printing/printing_init.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_PRINTING_PRINTING_INIT_H__
-#define CHROME_BROWSER_PRINTING_PRINTING_INIT_H__
+#ifndef CHROME_BROWSER_PRINTING_PRINTING_INIT_H_
+#define CHROME_BROWSER_PRINTING_PRINTING_INIT_H_
 
 namespace content {
 class WebContents;
@@ -11,9 +11,9 @@
 
 namespace printing {
 
-// Initialize printing related classes for web contents.
-void InitializePrinting(content::WebContents* web_contents);
+// Initialize printing related classes for a WebContents.
+void InitializePrintingForWebContents(content::WebContents* web_contents);
 
 }  // namespace printing
 
-#endif  // CHROME_BROWSER_PRINTING_PRINTING_INIT_H__
+#endif  // CHROME_BROWSER_PRINTING_PRINTING_INIT_H_
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
index 4918e282..5728b41da 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -446,14 +446,7 @@
         break;
       case 'nativeNextCharacter':
       case 'nativePreviousCharacter':
-        if (DesktopAutomationInterface.instance.textEditHandler) {
-          DesktopAutomationInterface.instance.textEditHandler
-              .injectInferredIntents([{
-                command: chrome.automation.IntentCommandType.MOVE_SELECTION,
-                textBoundary:
-                    chrome.automation.IntentTextBoundaryType.CHARACTER,
-              }]);
-        }
+        DesktopAutomationInterface.instance.onNativeNextOrPreviousCharacter();
         return true;
       case 'nextWord':
         didNavigate = true;
@@ -468,15 +461,8 @@
         break;
       case 'nativeNextWord':
       case 'nativePreviousWord':
-        if (DesktopAutomationInterface.instance.textEditHandler) {
-          DesktopAutomationInterface.instance.textEditHandler
-              .injectInferredIntents([{
-                command: chrome.automation.IntentCommandType.MOVE_SELECTION,
-                textBoundary: command === 'nativeNextWord' ?
-                    chrome.automation.IntentTextBoundaryType.WORD_END :
-                    chrome.automation.IntentTextBoundaryType.WORD_START,
-              }]);
-        }
+        DesktopAutomationInterface.instance.onNativeNextOrPreviousWord(
+            command === 'nativeNextWord');
         return true;
       case 'forward':
       case 'nextLine':
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
index a1e7423..fc845e0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
@@ -450,6 +450,28 @@
     this.shouldIgnoreDocumentSelectionFromAction_ = val;
   }
 
+  /** @override */
+  onNativeNextOrPreviousCharacter() {
+    if (this.textEditHandler) {
+      this.textEditHandler.injectInferredIntents([{
+        command: chrome.automation.IntentCommandType.MOVE_SELECTION,
+        textBoundary: chrome.automation.IntentTextBoundaryType.CHARACTER,
+      }]);
+    }
+  }
+
+  /** @override */
+  onNativeNextOrPreviousWord(isNext) {
+    if (this.textEditHandler) {
+      this.textEditHandler.injectInferredIntents([{
+        command: chrome.automation.IntentCommandType.MOVE_SELECTION,
+        textBoundary: isNext ?
+            chrome.automation.IntentTextBoundaryType.WORD_END :
+            chrome.automation.IntentTextBoundaryType.WORD_START,
+      }]);
+    }
+  }
+
   /**
    * Provides all feedback once a change event in a text field fires.
    * @param {!ChromeVoxEvent} evt
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
index a723e22f..63658f6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
@@ -17,6 +17,15 @@
    * @param {boolean} val
    */
   ignoreDocumentSelectionFromAction(val) {}
+
+  /** Handles native commands to move to the next or previous character. */
+  onNativeNextOrPreviousCharacter() {}
+
+  /**
+   * Handles native commands to move to the next or previous word.
+   * @param {boolean} isNext
+   */
+  onNativeNextOrPreviousWord(isNext) {}
 }
 
 /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
index 84e324e..62a5c76e 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
@@ -60,8 +60,7 @@
     this.addListener_(
         EventType.ACTIVE_DESCENDANT_CHANGED, this.onActiveDescendantChanged);
     this.addListener_(EventType.DETAILS_CHANGED, this.onDetailsChanged);
-    this.addListener_(
-        EventType.MENU_LIST_ITEM_SELECTED, this.onEventIfSelected);
+    this.addListener_(EventType.MENU_ITEM_SELECTED, this.onEventIfSelected);
     this.addListener_(
         EventType.SELECTED_VALUE_CHANGED, this.onSelectedValueChanged_);
   }
diff --git a/chrome/browser/resources/chromeos/login/components/BUILD.gn b/chrome/browser/resources/chromeos/login/components/BUILD.gn
index 5c8d768f..f0664b0 100644
--- a/chrome/browser/resources/chromeos/login/components/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/components/BUILD.gn
@@ -404,6 +404,7 @@
   html_file = "security_token_pin.html"
   html_type = "dom-module"
   auto_imports = oobe_auto_imports
+  migrated_imports = oobe_migrated_imports
   namespace_rewrites = oobe_namespace_rewrites
 }
 
diff --git a/chrome/browser/resources/chromeos/login/debug/debug.js b/chrome/browser/resources/chromeos/login/debug/debug.js
index fd7c51d..9d2d7f8 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug.js
@@ -13,7 +13,7 @@
 // #import './debug_util.js';
 // #import {AssistantNativeIconType} from '../../assistant_optin/utils.m.js';
 
-// #import {MessageType, ProblemType} from 'chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js';
+// #import {MessageType, ProblemType} from 'chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js';
 
 const createAssistantData = (isMinor) => {
   const data = {};
diff --git a/chrome/browser/resources/chromeos/login/oobe_auto_imports.gni b/chrome/browser/resources/chromeos/login/oobe_auto_imports.gni
index 2dc12850..e64db6fe 100644
--- a/chrome/browser/resources/chromeos/login/oobe_auto_imports.gni
+++ b/chrome/browser/resources/chromeos/login/oobe_auto_imports.gni
@@ -110,4 +110,7 @@
   "ui/webui/resources/cr_elements/cr_toggle/cr_toggle.html",
   "ui/webui/resources/cr_elements/md_select.css.html",
   "ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_enter_code_page.html",
+  "ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html",
+  "ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html",
+  "ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html",
 ]
diff --git a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
index ab7bffa..1e29cadf 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/screens/common/BUILD.gn
@@ -506,7 +506,8 @@
     "../../components/buttons:oobe_next_button.m",
     "../../components/buttons:oobe_text_button.m",
     "../../components/dialogs:oobe_adaptive_dialog.m",
-    "//ui/webui/resources/cr_components/chromeos/quick_unlock:setup_pin_keyboard.m",
+    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants",
+    "//ui/webui/resources/cr_components/chromeos/quick_unlock:setup_pin_keyboard",
     "//ui/webui/resources/js:cr.m",
   ]
   extra_deps = [ ":web_components" ]
diff --git a/chrome/browser/resources/chromeos/login/screens/common/pin_setup.js b/chrome/browser/resources/chromeos/login/screens/common/pin_setup.js
index 7528220..5083f62 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/pin_setup.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/pin_setup.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '//resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js';
+import '//resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js';
 import '//resources/cr_elements/cr_input/cr_input.js';
 import '//resources/cr_elements/cr_shared_style.css.js';
 import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
@@ -14,7 +14,7 @@
 import '../../components/buttons/oobe_next_button.m.js';
 import '../../components/buttons/oobe_text_button.m.js';
 
-import {recordLockScreenProgress} from '//resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js';
+import {recordLockScreenProgress} from '//resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js';
 import {assert, assertNotReached} from '//resources/js/assert.m.js';
 import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
 import {dom, html, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/new_tab_page/app.html b/chrome/browser/resources/new_tab_page/app.html
index f9af1485..4220fbcf 100644
--- a/chrome/browser/resources/new_tab_page/app.html
+++ b/chrome/browser/resources/new_tab_page/app.html
@@ -290,7 +290,9 @@
   <template is="dom-if" if="[[lazyRender_]]">
     <template is="dom-if" if="[[oneGoogleBarEnabled_]]">
       <ntp-iframe id="oneGoogleBar" src="[[oneGoogleBarIframePath_]]"
-          hidden$="[[!oneGoogleBarLoaded_]]">
+          hidden$="[[!oneGoogleBarLoaded_]]"
+          allow="camera [[oneGoogleBarIframeOrigin_]];
+                display-capture [[oneGoogleBarIframeOrigin_]]">
       </ntp-iframe>
     </template>
   </template>
diff --git a/chrome/browser/resources/new_tab_page/app.ts b/chrome/browser/resources/new_tab_page/app.ts
index 7e964dbf..e09a5f48 100644
--- a/chrome/browser/resources/new_tab_page/app.ts
+++ b/chrome/browser/resources/new_tab_page/app.ts
@@ -60,6 +60,7 @@
 }
 
 const CUSTOMIZE_URL_PARAM: string = 'customize';
+const OGB_IFRAME_ORIGIN = 'chrome-untrusted://new-tab-page';
 
 function recordClick(element: NtpElement) {
   chrome.metricsPrivate.recordEnumerationValue(
@@ -93,6 +94,11 @@
 
   static get properties() {
     return {
+      oneGoogleBarIframeOrigin_: {
+        type: String,
+        value: OGB_IFRAME_ORIGIN,
+      },
+
       oneGoogleBarIframePath_: {
         type: String,
         value: () => {
@@ -100,7 +106,7 @@
           params.set(
               'paramsencoded',
               btoa(window.location.search.replace(/^[?]/, '&')));
-          return `chrome-untrusted://new-tab-page/one-google-bar?${params}`;
+          return `${OGB_IFRAME_ORIGIN}/one-google-bar?${params}`;
         },
       },
 
diff --git a/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html b/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html
index 9ea168b..930aa800 100644
--- a/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html
+++ b/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html
@@ -29,21 +29,14 @@
       #virtualCardLabel {
         margin-inline-start: 8px;
       }
-
-      #cardImage {
-        margin-inline-end: 16px;
-      }
     </style>
     <div class="list-item">
       <div class="type-column">
-        <template is="dom-if" if="[[virtualCardMetadataEnabled_]]">
-          <img id="cardImage" src="[[creditCard.imageSrc]]" alt="">
-        </template>
         <div class="summary-column">
-          <div id="summaryLabel" class="ellipses">
+          <div id = "summaryLabel" class="ellipses">
             [[creditCard.metadata.summaryLabel]]
           </div>
-          <div id="summarySublabel"
+          <div id = "summarySublabel"
             hidden$="[[!shouldShowSecondarySublabel_()]]" class="ellipses">
             [[getSecondarySublabel_(creditCard.metadata)]]
           </div>
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
index 95221b0..2232888 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
@@ -236,7 +236,7 @@
     "../os_people_page:lock_screen_password_prompt_dialog",
     "../os_people_page:lock_state_behavior",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants.m",
+    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:load_time_data.m",
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
index 1604501..f534c16 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
@@ -716,13 +716,7 @@
     if (!this.showPhonePermissionSetupDialog_) {
       return false;
     }
-    if (this.pageContentData.isPhoneHubPermissionsDialogSupported) {
-      this.browserProxy_.logPhoneHubPermissionSetUpScreenAction(
-          PhoneHubPermissionsSetupFlowScreens.INTRO,
-          PhoneHubPermissionsSetupAction.SHOWN);
-      return true;
-    }
-    return false;
+    return this.pageContentData.isPhoneHubPermissionsDialogSupported;
   }
 
   /** @private */
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js
index 7687df6..b17406c4 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_permissions_setup_dialog.js
@@ -334,6 +334,9 @@
         'settings.onFeatureSetupConnectionStatusChanged',
         this.onFeatureSetupConnectionStatusChanged_.bind(this));
     this.$.dialog.showModal();
+    this.browserProxy_.logPhoneHubPermissionSetUpScreenAction(
+        PhoneHubPermissionsSetupFlowScreens.INTRO,
+        PhoneHubPermissionsSetupAction.SHOWN);
   }
 
   /**
@@ -658,7 +661,8 @@
   /** @private */
   nextPage_() {
     this.browserProxy_.logPhoneHubPermissionSetUpScreenAction(
-        this.setupScreen_, PhoneHubPermissionsSetupAction.NEXT_OR_TRY_AGAIN);
+        this.getCurrentScreen_(),
+        PhoneHubPermissionsSetupAction.NEXT_OR_TRY_AGAIN);
     switch (this.flowState_) {
       case SetupFlowStatus.INTRO:
         this.logSetupModeMetrics_();
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
index 0b0ad18..6f16839 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
@@ -414,7 +414,8 @@
     /** @private {!DevicePageBrowserProxy} */
     this.deviceBrowserProxy_ = DevicePageBrowserProxyImpl.getInstance();
 
-    if (loadTimeData.getBoolean('isAccessibilityOSSettingsVisibilityEnabled')) {
+    if (loadTimeData.getBoolean('isAccessibilityOSSettingsVisibilityEnabled') &&
+        !this.isKioskModeActive_) {
       this.redirectToNewA11ySettings();
     }
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
index 6b73755f..4258b0f 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
@@ -90,7 +90,7 @@
     "../multidevice_page:multidevice_smartlock_item",
     "//chromeos/ash/services/auth_factor_config/public/mojom:mojom_webui_js",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants.m",
+    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:load_time_data.m",
@@ -105,7 +105,7 @@
   deps = [
     ":lock_state_behavior",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants.m",
+    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants",
   ]
 }
 
@@ -135,7 +135,7 @@
     "../..:router",
     "//ash/webui/common/resources/cr_picture:png",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants.m",
+    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:icon",
@@ -190,7 +190,7 @@
     ":lock_screen_password_prompt_dialog",
     "../..:router",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_components/chromeos/quick_unlock:setup_pin_keyboard.m",
+    "//ui/webui/resources/cr_components/chromeos/quick_unlock:setup_pin_keyboard",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
 }
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
index c382ef7..359c00e 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
@@ -27,7 +27,7 @@
 import '../../settings_vars.css.js';
 import '../multidevice_page/multidevice_smartlock_item.js';
 
-import {LockScreenProgress, recordLockScreenProgress} from 'chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js';
+import {LockScreenProgress, recordLockScreenProgress} from 'chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js';
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {focusWithoutInk} from 'chrome://resources/js/cr/ui/focus_without_ink.js';
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js
index f0767c5..6bc887dd 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js
@@ -18,7 +18,7 @@
  */
 import '../../controls/password_prompt_dialog.js';
 
-import {LockScreenProgress, recordLockScreenProgress} from 'chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js';
+import {LockScreenProgress, recordLockScreenProgress} from 'chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {LockStateBehavior, LockStateBehaviorInterface} from './lock_state_behavior.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.js
index 21d15c13..f8b53ba6 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.js
@@ -10,7 +10,7 @@
  *
  */
 
-import 'chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js';
+import 'chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import 'chrome://resources/js/assert.m.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js
index ef047d7b..8ee352e 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js
@@ -11,7 +11,7 @@
  * </settings-setup-pin-dialog>
  */
 
-import 'chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js';
+import 'chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import '../../settings_shared.css.js';
diff --git a/chrome/browser/resources/webui_gallery/BUILD.gn b/chrome/browser/resources/webui_gallery/BUILD.gn
index 0b03910..8b380da 100644
--- a/chrome/browser/resources/webui_gallery/BUILD.gn
+++ b/chrome/browser/resources/webui_gallery/BUILD.gn
@@ -70,6 +70,7 @@
     "demos/cr_radio_demo.html",
     "demos/cr_slider/cr_slider_demo.html",
     "demos/cr_tab_box/cr_tab_box_demo.html",
+    "demos/cr_tabs/cr_tabs_demo.html",
     "demos/cr_tree/cr_tree_demo.html",
     "demos/cr_toggle_demo.html",
     "webui_gallery.html",
diff --git a/chrome/browser/resources/webui_gallery/app.ts b/chrome/browser/resources/webui_gallery/app.ts
index 8dc0247..e3c1dc2 100644
--- a/chrome/browser/resources/webui_gallery/app.ts
+++ b/chrome/browser/resources/webui_gallery/app.ts
@@ -80,6 +80,11 @@
               src: 'cr_tab_box/cr_tab_box_demo.html',
             },
             {
+              name: 'Tabs, Polymer',
+              path: 'tabs2',
+              src: 'cr_tabs/cr_tabs_demo.html',
+            },
+            {
               name: 'Toggles',
               path: 'toggles',
               src: 'cr_toggle_demo.html',
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_tab_box/cr_tab_box_demo.html b/chrome/browser/resources/webui_gallery/demos/cr_tab_box/cr_tab_box_demo.html
index 62f73333..5ec7c07 100644
--- a/chrome/browser/resources/webui_gallery/demos/cr_tab_box/cr_tab_box_demo.html
+++ b/chrome/browser/resources/webui_gallery/demos/cr_tab_box/cr_tab_box_demo.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="utf-8">
     <title>cr-tab-box demo</title>
-    <link rel="stylesheet" href="demo.css">
+    <link rel="stylesheet" href="../demo.css">
   </head>
   <body>
     <cr-tab-box-demo></cr-tab-box-demo>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.html b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.html
new file mode 100644
index 0000000..7d435df9
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>cr-tabs demo</title>
+    <link rel="stylesheet" href="../demo.css">
+  </head>
+  <body>
+    <cr-tabs-demo></cr-tabs-demo>
+    <script src="cr_tabs_demo_component.js" type="module"></script>
+  </body>
+</html>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo_component.html b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo_component.html
new file mode 100644
index 0000000..e2c1a83b
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo_component.html
@@ -0,0 +1,39 @@
+<link rel="stylesheet" href="../demo.css">
+<style>
+  cr-tabs,
+  iron-pages {
+    width: 100%;
+  }
+
+  .tab-contents {
+    align-items: center;
+    display: flex;
+    height: 150px;
+    justify-content: center;
+  }
+</style>
+
+<h1>cr-tabs</h1>
+<div class="demos">
+  <cr-tabs tab-names="[[tabNames_]]" selected="{{selectedTabIndex_}}"></cr-tabs>
+  <iron-pages selected="[[selectedTabIndex_]]">
+    <template is="dom-repeat" items="[[tabNames_]]" as="tabName">
+      <div class="tab-contents">
+        [[tabName]] contents
+      </div>
+    </template>
+  </iron-pages>
+</div>
+
+<div class="demos">
+  <div class="row">
+    <cr-button on-click="onAddClick_">Add</cr-button>
+    <cr-button on-click="onAddAt1Click_">Add at 1</cr-button>
+    <cr-button on-click="onSelectAt1Click_">Select at 1</cr-button>
+  </div>
+</div>
+
+<div>
+  Tab Count: [[tabNames_.length]],
+  Selected Tab: [[selectedTabIndex_]]
+</div>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo_component.ts b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo_component.ts
new file mode 100644
index 0000000..08409a2
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_tabs/cr_tabs_demo_component.ts
@@ -0,0 +1,45 @@
+// 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.
+
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
+import 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js';
+
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './cr_tabs_demo_component.html.js';
+
+class CrTabsDemoComponent extends PolymerElement {
+  static get is() {
+    return 'cr-tabs-demo';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+
+  static get properties() {
+    return {
+      selectedTabIndex_: Number,
+      tabNames_: Array,
+    };
+  }
+
+  private selectedTabIndex_ = 0;
+  private tabNames_: string[] = ['Tab 1', 'Tab 2', 'Tab 3'];
+
+  private onAddClick_() {
+    this.push('tabNames_', 'Added');
+  }
+
+  private onAddAt1Click_() {
+    this.splice('tabNames_', 1, 0, 'Added at 1');
+  }
+
+  private onSelectAt1Click_() {
+    this.selectedTabIndex_ = 1;
+  }
+}
+
+customElements.define(CrTabsDemoComponent.is, CrTabsDemoComponent);
diff --git a/chrome/browser/resources/webui_gallery/webui_gallery.gni b/chrome/browser/resources/webui_gallery/webui_gallery.gni
index 9ee7bcaf..530b187 100644
--- a/chrome/browser/resources/webui_gallery/webui_gallery.gni
+++ b/chrome/browser/resources/webui_gallery/webui_gallery.gni
@@ -10,6 +10,7 @@
   "demos/cr_dialog_demo_component.ts",
   "demos/cr_input/cr_input_demo_component.ts",
   "demos/cr_slider/cr_slider_demo_component.ts",
+  "demos/cr_tabs/cr_tabs_demo_component.ts",
 ]
 
 web_component_files_native = [
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
index 6800fb0c..099aa7fc 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
@@ -137,7 +137,7 @@
 
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        MetricsRecorder.recordDeviceClickedInShareSheet();
+        MetricsRecorder.recordSendingEvent(SendingEvent.CLICK_ITEM);
         TargetDeviceInfo targetDeviceInfo = mAdapter.getItem(position);
 
         SendTabToSelfAndroidBridge.addEntry(mProfile, mUrl, mTitle, targetDeviceInfo.cacheGuid);
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java
index 02a8f58..aab76b6 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java
@@ -12,8 +12,8 @@
  */
 @JNINamespace("send_tab_to_self")
 class MetricsRecorder {
-    public static void recordDeviceClickedInShareSheet() {
-        MetricsRecorderJni.get().recordDeviceClickedInShareSheet();
+    public static void recordSendingEvent(@SendingEvent int sendingEvent) {
+        MetricsRecorderJni.get().recordSendingEvent(sendingEvent);
     }
 
     public static void recordNotificationShown() {
@@ -34,7 +34,7 @@
 
     @NativeMethods
     interface Natives {
-        void recordDeviceClickedInShareSheet();
+        void recordSendingEvent(int sendingEvent);
         void recordNotificationShown();
         void recordNotificationOpened();
         void recordNotificationDismissed();
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java
index 9526f8b..05e5dac 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfCoordinator.java
@@ -156,16 +156,19 @@
             // This must be the old behavior where the entry point is shown even in states where
             // no promo is shown.
             assert !ChromeFeatureList.isEnabled(ChromeFeatureList.SEND_TAB_TO_SELF_SIGNIN_PROMO);
+            MetricsRecorder.recordSendingEvent(SendingEvent.SHOW_NO_TARGET_DEVICE_MESSAGE);
             mController.requestShowContent(new NoTargetDeviceBottomSheetContent(mContext), true);
             return;
         }
 
         switch (displayReason.get()) {
             case EntryPointDisplayReason.INFORM_NO_TARGET_DEVICE:
+                MetricsRecorder.recordSendingEvent(SendingEvent.SHOW_NO_TARGET_DEVICE_MESSAGE);
                 mController.requestShowContent(
                         new NoTargetDeviceBottomSheetContent(mContext), true);
                 return;
             case EntryPointDisplayReason.OFFER_FEATURE:
+                MetricsRecorder.recordSendingEvent(SendingEvent.SHOW_DEVICE_LIST);
                 // TODO(crbug.com/1219434): Merge with INFORM_NO_TARGET_DEVICE, just let the UI
                 // differentiate between the 2 by checking the device list size.
                 List<TargetDeviceInfo> targetDevices =
@@ -176,6 +179,7 @@
                         true);
                 return;
             case EntryPointDisplayReason.OFFER_SIGN_IN: {
+                MetricsRecorder.recordSendingEvent(SendingEvent.SHOW_SIGNIN_PROMO);
                 new AccountPickerBottomSheetCoordinator(mWindowAndroid, mController,
                         new SendTabToSelfAccountPickerDelegate(this::onSignInComplete, mProfile));
                 return;
diff --git a/chrome/browser/ui/android/webid/account_selection_view_android.cc b/chrome/browser/ui/android/webid/account_selection_view_android.cc
index d755e67..6366854 100644
--- a/chrome/browser/ui/android/webid/account_selection_view_android.cc
+++ b/chrome/browser/ui/android/webid/account_selection_view_android.cc
@@ -9,7 +9,6 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/strings/string_piece.h"
-#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/android/webid/jni_headers/AccountSelectionBridge_jni.h"
 #include "chrome/browser/ui/android/webid/jni_headers/Account_jni.h"
 #include "chrome/browser/ui/android/webid/jni_headers/ClientIdMetadata_jni.h"
@@ -17,12 +16,9 @@
 #include "chrome/browser/ui/webid/account_selection_view.h"
 #include "content/public/browser/identity_request_dialog_controller.h"
 #include "ui/android/color_utils_android.h"
-#include "ui/android/view_android.h"
 #include "ui/android/window_android.h"
-#include "ui/gfx/android/java_bitmap.h"
 #include "url/android/gurl_android.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ConvertJavaStringToUTF8;
@@ -116,10 +112,7 @@
 
 void AccountSelectionViewAndroid::Show(
     const std::string& rp_for_display,
-    const std::string& idp_for_display,
-    const std::vector<Account>& accounts,
-    const content::IdentityProviderMetadata& idp_metadata,
-    const content::ClientIdData& client_data,
+    const std::vector<content::IdentityProviderData>& identity_provider_data,
     Account::SignInMode sign_in_mode) {
   if (!RecreateJavaObject()) {
     // It's possible that the constructor cannot access the bottom sheet clank
@@ -129,19 +122,24 @@
     return;
   }
 
-  // Serialize the |accounts| span into a Java array and instruct the bridge
-  // to show it together with |url| to the user.
+  // Serialize the `identity_provider_data.accounts` into a Java array and
+  // instruct the bridge to show it together with |url| to the user.
   JNIEnv* env = AttachCurrentThread();
+  // Multi IDP support does not currently work on mobile. Hence, we use the
+  // first index from the `identity_provider_data` for the IDP-specific
+  // information.
   ScopedJavaLocalRef<jobjectArray> accounts_obj =
-      ConvertToJavaAccounts(env, accounts);
+      ConvertToJavaAccounts(env, identity_provider_data[0].accounts);
   ScopedJavaLocalRef<jobject> idp_metadata_obj =
-      ConvertToJavaIdentityProviderMetadata(env, idp_metadata);
+      ConvertToJavaIdentityProviderMetadata(
+          env, identity_provider_data[0].idp_metadata);
   ScopedJavaLocalRef<jobject> client_id_metadata_obj =
-      ConvertToJavaClientIdMetadata(env, client_data);
+      ConvertToJavaClientIdMetadata(env,
+                                    identity_provider_data[0].client_id_data);
   Java_AccountSelectionBridge_showAccounts(
       env, java_object_internal_, ConvertUTF8ToJavaString(env, rp_for_display),
-      ConvertUTF8ToJavaString(env, idp_for_display), accounts_obj,
-      idp_metadata_obj, client_id_metadata_obj,
+      ConvertUTF8ToJavaString(env, identity_provider_data[0].idp_for_display),
+      accounts_obj, idp_metadata_obj, client_id_metadata_obj,
       sign_in_mode == Account::SignInMode::kAuto);
 }
 
diff --git a/chrome/browser/ui/android/webid/account_selection_view_android.h b/chrome/browser/ui/android/webid/account_selection_view_android.h
index 8667700c..abe9d68 100644
--- a/chrome/browser/ui/android/webid/account_selection_view_android.h
+++ b/chrome/browser/ui/android/webid/account_selection_view_android.h
@@ -7,7 +7,6 @@
 
 #include <string>
 
-#include "base/android/scoped_java_ref.h"
 #include "base/callback.h"
 #include "chrome/browser/ui/webid/account_selection_view.h"
 
@@ -20,12 +19,10 @@
   ~AccountSelectionViewAndroid() override;
 
   // AccountSelectionView:
-  void Show(const std::string& rp_for_display,
-            const std::string& idp_for_display,
-            const std::vector<Account>& accounts,
-            const content::IdentityProviderMetadata& idp_metadata,
-            const content::ClientIdData& client_data,
-            Account::SignInMode sign_in_mode) override;
+  void Show(
+      const std::string& rp_for_display,
+      const std::vector<content::IdentityProviderData>& identity_provider_data,
+      Account::SignInMode sign_in_mode) override;
   void ShowFailureDialog(const std::string& rp_for_display,
                          const std::string& idp_for_display) override;
 
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.cc b/chrome/browser/ui/apps/chrome_app_delegate.cc
index 22b63c37..1babd63 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.cc
+++ b/chrome/browser/ui/apps/chrome_app_delegate.cc
@@ -223,7 +223,7 @@
   favicon::CreateContentFaviconDriverForWebContents(web_contents);
 
 #if BUILDFLAG(ENABLE_PRINTING)
-  printing::InitializePrinting(web_contents);
+  printing::InitializePrintingForWebContents(web_contents);
 #endif
 
   apps::AudioFocusWebContentsObserver::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc b/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc
index 3cda9cb..3672518 100644
--- a/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc
+++ b/chrome/browser/ui/ash/glanceables/glanceables_browsertest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_test_util.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/chrome_switches.h"
@@ -80,7 +81,7 @@
   Profile* profile_ = nullptr;
 };
 
-IN_PROC_BROWSER_TEST_F(GlanceablesBrowserTest, ShowsOnLogin) {
+IN_PROC_BROWSER_TEST_F(GlanceablesBrowserTest, ShowsAndHide) {
   // Not showing on the login screen.
   EXPECT_FALSE(glanceables_controller()->IsShowing());
 
@@ -96,4 +97,16 @@
 
   // Showing once the refresh token is loaded.
   EXPECT_TRUE(glanceables_controller()->IsShowing());
+
+  // Open a browser window.
+  CreateBrowser(ProfileManager::GetLastUsedProfile());
+
+  // Glanceables should close because a window opened.
+  EXPECT_FALSE(glanceables_controller()->IsShowing());
+
+  // Simulate re-showing glanceables from overview.
+  glanceables_controller()->ShowFromOverview();
+
+  // Glanceables are showing.
+  EXPECT_TRUE(glanceables_controller()->IsShowing());
 }
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl.cc b/chrome/browser/ui/ash/projector/projector_client_impl.cc
index c803843..ddc4378 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl.cc
+++ b/chrome/browser/ui/ash/projector/projector_client_impl.cc
@@ -49,7 +49,7 @@
 void ProjectorClientImpl::InitForProjectorAnnotator(views::WebView* web_view) {
   if (!ash::features::IsProjectorAnnotatorEnabled())
     return;
-  web_view->LoadInitialURL(GURL(ash::kChromeUITrustedAnnotatorAppUrl));
+  web_view->LoadInitialURL(GURL(ash::kChromeUITrustedAnnotatorUrl));
 }
 
 // Using base::Unretained for callback is safe since the ProjectorClientImpl
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc
index 9d2a8f6..b5b5430 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc
@@ -177,10 +177,10 @@
 // This test verifies that the (un)trusted Projector app and annotator WebUI
 // URLs are valid.
 IN_PROC_BROWSER_TEST_F(ProjectorClientTest, AppUrlsValid) {
-  VerifyUrlValid(kChromeUITrustedProjectorAppUrl);
-  VerifyUrlValid(kChromeUIUntrustedProjectorAppUrl);
-  VerifyUrlValid(kChromeUITrustedAnnotatorAppUrl);
-  VerifyUrlValid(kChromeUIUntrustedAnnotatorAppUrl);
+  VerifyUrlValid(kChromeUITrustedProjectorUrl);
+  VerifyUrlValid(kChromeUIUntrustedProjectorUrl);
+  VerifyUrlValid(kChromeUITrustedAnnotatorUrl);
+  VerifyUrlValid(kChromeUIUntrustedAnnotatorUrl);
 }
 
 IN_PROC_BROWSER_TEST_F(ProjectorClientTest, OpenProjectorApp) {
diff --git a/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc b/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc
index 55da384..0305d78 100644
--- a/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc
+++ b/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc
@@ -142,7 +142,7 @@
             content::PAGE_TYPE_NORMAL);
 
   // Construct the new redirected URL.
-  std::string expected_url = kChromeUITrustedProjectorAppUrl;
+  std::string expected_url = kChromeUITrustedProjectorUrl;
   expected_url += kFilePath;
   // The timestamp corresponds to 21 Jan 2022 10:00:00 GMT in microseconds since
   // Unix epoch (Jan 1 1970).
@@ -214,7 +214,7 @@
   EXPECT_EQ(tab->GetController().GetVisibleEntry()->GetPageType(),
             content::PAGE_TYPE_NORMAL);
 
-  std::string expected_url = kChromeUITrustedProjectorAppUrl;
+  std::string expected_url = kChromeUITrustedProjectorUrl;
   expected_url += "?timestamp=1642759200000000%20bogo-microseconds";
   EXPECT_EQ(tab->GetVisibleURL().spec(), expected_url);
 }
@@ -222,7 +222,7 @@
 // Verifies that navigating to chrome-untrusted://projector does not redirect.
 IN_PROC_BROWSER_TEST_F(ProjectorNavigationThrottleTest,
                        UntrustedNavigationNoRedirect) {
-  GURL untrusted_url(kChromeUIUntrustedProjectorAppUrl);
+  GURL untrusted_url(kChromeUIUntrustedProjectorUrl);
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), untrusted_url));
   FlushSystemWebAppLaunchesForTesting(profile());
@@ -246,7 +246,7 @@
 // launches the SWA.
 IN_PROC_BROWSER_TEST_F(ProjectorNavigationThrottleTest,
                        TrustedNavigationNoRedirect) {
-  GURL trusted_url(kChromeUITrustedProjectorAppUrl);
+  GURL trusted_url(kChromeUITrustedProjectorUrl);
 
   ui_test_utils::NavigateToURLWithDisposition(
       browser(), trusted_url, WindowOpenDisposition::NEW_WINDOW,
@@ -277,8 +277,6 @@
   content::WebContents* tab =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_TRUE(tab);
-  EXPECT_EQ(tab->GetController().GetVisibleEntry()->GetPageType(),
-            content::PAGE_TYPE_ERROR);
   EXPECT_EQ(tab->GetVisibleURL(), untrusted_annotator_url);
 }
 
@@ -330,7 +328,7 @@
 // disabled.
 IN_PROC_BROWSER_TEST_F(ProjectorNavigationThrottleDisabledTest,
                        UntrustedNavigationInvalidUrl) {
-  GURL untrusted_url(kChromeUIUntrustedProjectorAppUrl);
+  GURL untrusted_url(kChromeUIUntrustedProjectorUrl);
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), untrusted_url));
   content::WebContents* tab =
@@ -345,7 +343,7 @@
 // disabled.
 IN_PROC_BROWSER_TEST_F(ProjectorNavigationThrottleDisabledTest,
                        TrustedNavigationInvalidUrl) {
-  GURL trusted_url(kChromeUITrustedProjectorAppUrl);
+  GURL trusted_url(kChromeUITrustedProjectorUrl);
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), trusted_url));
   FlushSystemWebAppLaunchesForTesting(profile());
@@ -405,7 +403,7 @@
                        UntrustedNavigationLocaleDetection) {
   g_browser_process->SetApplicationLocale(locale());
 
-  GURL untrusted_url(kChromeUIUntrustedProjectorAppUrl);
+  GURL untrusted_url(kChromeUIUntrustedProjectorUrl);
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), untrusted_url));
   content::WebContents* tab =
diff --git a/chrome/browser/ui/ash/session_controller_client_impl.cc b/chrome/browser/ui/ash/session_controller_client_impl.cc
index 5c0d92d..7c2931f 100644
--- a/chrome/browser/ui/ash/session_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/session_controller_client_impl.cc
@@ -229,6 +229,10 @@
   chrome::AttemptUserExit();
 }
 
+void SessionControllerClientImpl::RequestRestartForUpdate() {
+  browser_shutdown::NotifyAndTerminate(/*fast_path=*/true);
+}
+
 void SessionControllerClientImpl::AttemptRestartChrome() {
   chrome::AttemptRestart();
 }
diff --git a/chrome/browser/ui/ash/session_controller_client_impl.h b/chrome/browser/ui/ash/session_controller_client_impl.h
index aeb5763..8b02e31 100644
--- a/chrome/browser/ui/ash/session_controller_client_impl.h
+++ b/chrome/browser/ui/ash/session_controller_client_impl.h
@@ -82,6 +82,7 @@
   void RequestLockScreen() override;
   void RequestHideLockScreen() override;
   void RequestSignOut() override;
+  void RequestRestartForUpdate() override;
   void AttemptRestartChrome() override;
   void SwitchActiveUser(const AccountId& account_id) override;
   void CycleActiveUser(ash::CycleUserDirection direction) override;
diff --git a/chrome/browser/ui/ash/system_tray_client_impl.cc b/chrome/browser/ui/ash/system_tray_client_impl.cc
index acd890a8..e66980e5 100644
--- a/chrome/browser/ui/ash/system_tray_client_impl.cc
+++ b/chrome/browser/ui/ash/system_tray_client_impl.cc
@@ -40,7 +40,6 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
-#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
@@ -696,10 +695,6 @@
   chrome::ShowFirmwareUpdatesApp(ProfileManager::GetActiveUserProfile());
 }
 
-void SystemTrayClientImpl::RequestRestartForUpdate() {
-  browser_shutdown::NotifyAndTerminate(/*fast_path=*/true);
-}
-
 void SystemTrayClientImpl::SetLocaleAndExit(
     const std::string& locale_iso_code) {
   ProfileManager::GetActiveUserProfile()->ChangeAppLocale(
diff --git a/chrome/browser/ui/ash/system_tray_client_impl.h b/chrome/browser/ui/ash/system_tray_client_impl.h
index b2e87eda..6da516d 100644
--- a/chrome/browser/ui/ash/system_tray_client_impl.h
+++ b/chrome/browser/ui/ash/system_tray_client_impl.h
@@ -97,7 +97,6 @@
   void ShowNetworkSettings(const std::string& network_id) override;
   void ShowMultiDeviceSetup() override;
   void ShowFirmwareUpdate() override;
-  void RequestRestartForUpdate() override;
   void SetLocaleAndExit(const std::string& locale_iso_code) override;
   void ShowAccessCodeCastingDialog(
       AccessCodeCastDialogOpenLocation open_location) override;
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index b7d57e4..be0355be 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -629,7 +629,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_PRINTING)
-  printing::InitializePrinting(web_contents);
+  printing::InitializePrintingForWebContents(web_contents);
 #endif
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc
index 979f1ad3..940bee7 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc
@@ -364,6 +364,8 @@
   show_in_folder_action_ = AddQuickAction(DownloadCommands::SHOW_IN_FOLDER);
   quick_action_holder_->SetVisible(false);
   layout->SetChildViewIgnoredByLayout(quick_action_holder_, true);
+  quick_action_holder_->SetBackground(
+      views::CreateThemedSolidBackground(ui::kColorDialogBackground));
 
   subpage_icon_holder_ =
       AddChildView(std::make_unique<views::FlexLayoutView>());
diff --git a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_controller.cc b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_controller.cc
index 107ef97e..17eb7a1a 100644
--- a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_controller.cc
+++ b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_controller.cc
@@ -68,16 +68,23 @@
   DCHECK(reason);
   switch (*reason) {
     case send_tab_to_self::EntryPointDisplayReason::kOfferFeature:
+      send_tab_to_self::RecordSendingEvent(ShareEntryPoint::kOmniboxIcon,
+                                           SendingEvent::kShowDeviceList);
       send_tab_to_self_bubble_view_ =
           browser->window()->ShowSendTabToSelfDevicePickerBubble(
               &GetWebContents());
       break;
     case send_tab_to_self::EntryPointDisplayReason::kOfferSignIn:
+      send_tab_to_self::RecordSendingEvent(ShareEntryPoint::kOmniboxIcon,
+                                           SendingEvent::kShowSigninPromo);
       send_tab_to_self_bubble_view_ =
           browser->window()->ShowSendTabToSelfPromoBubble(
               &GetWebContents(), /*show_signin_button=*/true);
       break;
     case send_tab_to_self::EntryPointDisplayReason::kInformNoTargetDevice:
+      send_tab_to_self::RecordSendingEvent(
+          ShareEntryPoint::kOmniboxIcon,
+          SendingEvent::kShowNoTargetDeviceMessage);
       send_tab_to_self_bubble_view_ =
           browser->window()->ShowSendTabToSelfPromoBubble(
               &GetWebContents(), /*show_signin_button=*/false);
@@ -119,7 +126,8 @@
     const std::string& target_device_guid) {
   // TODO(crbug.com/1288843): This is being recorded for entry points other
   // than the omnibox. Make the entry point a ShowBubble() argument.
-  send_tab_to_self::RecordDeviceClicked(ShareEntryPoint::kOmniboxIcon);
+  send_tab_to_self::RecordSendingEvent(ShareEntryPoint::kOmniboxIcon,
+                                       SendingEvent::kClickItem);
 
   SendTabToSelfModel* model =
       SendTabToSelfSyncServiceFactory::GetForProfile(GetProfile())
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
index 43e19a049..2b93cbf 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
@@ -7,24 +7,19 @@
 #include "base/feature_list.h"
 #include "base/i18n/case_conversion.h"
 #include "base/memory/weak_ptr.h"
-#include "base/metrics/histogram_macros.h"
 #include "chrome/browser/accessibility/accessibility_state_utils.h"
 #include "chrome/browser/image_fetcher/image_decoder_impl.h"
 #include "chrome/browser/ui/monogram_utils.h"
-#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
 #include "chrome/browser/ui/views/hover_button.h"
 #include "chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/grit/platform_locale_settings.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/vector_icons/vector_icons.h"
-#include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_features.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "skia/ext/image_operations.h"
 #include "third_party/skia/include/core/SkPath.h"
-#include "ui/accessibility/ax_role_properties.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/canvas.h"
@@ -32,7 +27,6 @@
 #include "ui/gfx/image/canvas_image_source.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/views/accessibility/view_accessibility.h"
-#include "ui/views/background.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/button/md_text_button.h"
@@ -44,10 +38,7 @@
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/flex_layout.h"
-#include "ui/views/layout/layout_provider.h"
-#include "ui/views/layout/layout_types.h"
 #include "ui/views/view_class_properties.h"
-#include "ui/views/view_utils.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view.h b/chrome/browser/ui/views/webid/account_selection_bubble_view.h
index 4f14ea22..f4eaee2 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view.h
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view.h
@@ -7,9 +7,7 @@
 
 #include "base/callback.h"
 #include "base/memory/raw_ptr.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/webid/account_selection_bubble_view_interface.h"
-#include "chrome/browser/ui/webid/account_selection_view.h"
 #include "components/image_fetcher/core/image_fetcher.h"
 #include "content/public/browser/identity_request_dialog_controller.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view_interface.h b/chrome/browser/ui/views/webid/account_selection_bubble_view_interface.h
index 6a726ad..a76c6821 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view_interface.h
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view_interface.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_WEBID_ACCOUNT_SELECTION_BUBBLE_VIEW_INTERFACE_H_
 #define CHROME_BROWSER_UI_VIEWS_WEBID_ACCOUNT_SELECTION_BUBBLE_VIEW_INTERFACE_H_
 
+#include <string>
+
+#include "base/containers/span.h"
+
 namespace content {
 struct ClientIdData;
 struct IdentityProviderMetadata;
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
index 431f6618..1452aa81 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.cc
@@ -4,13 +4,11 @@
 
 #include "chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h"
 
-#include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 
 using DismissReason = content::IdentityRequestDialogController::DismissReason;
@@ -50,10 +48,7 @@
 
 void FedCmAccountSelectionView::Show(
     const std::string& rp_etld_plus_one,
-    const std::string& idp_etld_plus_one,
-    const std::vector<Account>& accounts,
-    const content::IdentityProviderMetadata& idp_metadata,
-    const content::ClientIdData& client_data,
+    const std::vector<content::IdentityProviderData>& identity_provider_data,
     Account::SignInMode sign_in_mode) {
   Browser* browser =
       chrome::FindBrowserWithWebContents(delegate_->GetWebContents());
@@ -61,11 +56,16 @@
   if (browser)
     browser->tab_strip_model()->AddObserver(this);
 
-  idp_etld_plus_one_ = base::UTF8ToUTF16(idp_etld_plus_one);
-  idp_metadata_ = idp_metadata;
-  client_data_ = std::make_unique<content::ClientIdData>(client_data);
-  account_list_ = std::vector<content::IdentityRequestAccount>(accounts.begin(),
-                                                               accounts.end());
+  // TODO(crbug.com/1351137): Temporarily support only the first IDP, extend to
+  // support multiple IDPs.
+  idp_etld_plus_one_ =
+      base::UTF8ToUTF16(identity_provider_data[0].idp_for_display);
+  idp_metadata_ = identity_provider_data[0].idp_metadata;
+  client_data_ = std::make_unique<content::ClientIdData>(
+      identity_provider_data[0].client_id_data);
+  account_list_ = std::vector<content::IdentityRequestAccount>(
+      identity_provider_data[0].accounts.begin(),
+      identity_provider_data[0].accounts.end());
   state_ =
       (account_list_.size() == 1u) ? State::PERMISSION : State::ACCOUNT_PICKER;
 
@@ -73,8 +73,9 @@
                                 idp_etld_plus_one_)
                        ->GetWeakPtr();
   GetBubbleView()->ShowAccountPicker(idp_etld_plus_one_,
-                                     /*show_back_button=*/false, accounts,
-                                     idp_metadata, client_data);
+                                     /*show_back_button=*/false, account_list_,
+                                     idp_metadata_,
+                                     identity_provider_data[0].client_id_data);
   bubble_widget_->Show();
   bubble_widget_->AddObserver(this);
 }
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h
index c89a60d0..8ce6290 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h
@@ -32,12 +32,10 @@
   ~FedCmAccountSelectionView() override;
 
   // AccountSelectionView:
-  void Show(const std::string& rp_etld_plus_one,
-            const std::string& idp_etld_plus_one,
-            const std::vector<Account>& accounts,
-            const content::IdentityProviderMetadata& idp_metadata,
-            const content::ClientIdData& client_data,
-            Account::SignInMode sign_in_mode) override;
+  void Show(
+      const std::string& rp_etld_plus_one,
+      const std::vector<content::IdentityProviderData>& identity_provider_data,
+      Account::SignInMode sign_in_mode) override;
   void ShowFailureDialog(const std::string& rp_etld_plus_one,
                          const std::string& idp_etld_plus_one) override;
 
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_browsertest.cc b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_browsertest.cc
index 8a7bf6460..ec46b12 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_browsertest.cc
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_browsertest.cc
@@ -10,10 +10,8 @@
 #include "chrome/browser/ui/views/webid/fake_delegate.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "chrome/test/views/chrome_views_test_base.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
 
 class FedCmAccountSelectionViewBrowserTest : public DialogBrowserTest {
  public:
@@ -31,11 +29,11 @@
   void ShowUi(const std::string& name) override {
     std::vector<content::IdentityRequestAccount> accounts = {
         {"id", "email", "name", "given_name", GURL::EmptyGURL()}};
-    content::IdentityProviderMetadata idp_metadata;
-    content::ClientIdData client_data(GURL::EmptyGURL(), GURL::EmptyGURL());
-    account_selection_view()->Show("rp-example.com", "idp-example.com",
-                                   accounts, idp_metadata, client_data,
-                                   Account::SignInMode::kExplicit);
+    account_selection_view()->Show(
+        "rp-example.com",
+        {{"idp-example.com", accounts, content::IdentityProviderMetadata(),
+          content::ClientIdData(GURL::EmptyGURL(), GURL::EmptyGURL())}},
+        Account::SignInMode::kExplicit);
   }
 
   void Show() {
diff --git a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc
index 6ea45f2..52046b1 100644
--- a/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc
+++ b/chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop_unittest.cc
@@ -7,7 +7,6 @@
 #include <memory>
 #include <string>
 
-#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/views/webid/account_selection_bubble_view.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_views_test_base.h"
@@ -143,10 +142,11 @@
       accounts.emplace_back(account_info.first, "", "", "", GURL::EmptyGURL(),
                             account_info.second);
     }
-    controller->Show(kRpEtldPlusOne, kIdpEtldPlusOne, accounts,
-                     content::IdentityProviderMetadata(),
-                     content::ClientIdData(GURL(), GURL()),
-                     SignInMode::kExplicit);
+    controller->Show(
+        kRpEtldPlusOne,
+        {{kIdpEtldPlusOne, accounts, content::IdentityProviderMetadata(),
+          content::ClientIdData(GURL(), GURL())}},
+        SignInMode::kExplicit);
     return controller;
   }
 
diff --git a/chrome/browser/ui/webid/account_selection_view.h b/chrome/browser/ui/webid/account_selection_view.h
index a1e214fc..ef089bc 100644
--- a/chrome/browser/ui/webid/account_selection_view.h
+++ b/chrome/browser/ui/webid/account_selection_view.h
@@ -6,16 +6,9 @@
 #define CHROME_BROWSER_UI_WEBID_ACCOUNT_SELECTION_VIEW_H_
 
 #include <memory>
-#include "base/callback_forward.h"
-#include "base/containers/span.h"
 #include "base/memory/raw_ptr.h"
-#include "base/strings/string_piece_forward.h"
-#include "base/types/strong_alias.h"
-#include "content/public/browser/browser_context.h"
 #include "content/public/browser/identity_request_dialog_controller.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/views/view.h"
-#include "url/gurl.h"
 
 using Account = content::IdentityRequestAccount;
 
@@ -54,17 +47,16 @@
   AccountSelectionView& operator=(const AccountSelectionView&) = delete;
   virtual ~AccountSelectionView() = default;
 
-  // Instructs the view to show the provided |accounts| to the user.
-  // |rp_for_display| and |idp_for_display| are the relying party URL and
-  // identity provider URL to display in the prompt respectively. |sign_in_mode|
-  // represents whether this is an auto sign in flow. After user interaction
-  // either OnAccountSelected() or OnDismiss() gets invoked.
-  virtual void Show(const std::string& rp_for_display,
-                    const std::string& idp_for_display,
-                    const std::vector<Account>& accounts,
-                    const content::IdentityProviderMetadata& idp_metadata,
-                    const content::ClientIdData& client_data,
-                    Account::SignInMode sign_in_mode) = 0;
+  // Instructs the view to show the provided accounts to the user.
+  // `rp_for_display` is the relying party URL to display in the prompt. All
+  // IDP-specific information, including user accounts, is stored in
+  // `idps_for_display`. `sign_in_mode` represents whether this is an auto sign
+  // in flow. After user interaction either OnAccountSelected() or OnDismiss()
+  // gets invoked.
+  virtual void Show(
+      const std::string& rp_for_display,
+      const std::vector<content::IdentityProviderData>& identity_provider_data,
+      Account::SignInMode sign_in_mode) = 0;
 
   // Shows a failure UI when the accounts fetch is failed such that it is
   // observable by users. This could happen when an IDP claims that the user is
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.cc b/chrome/browser/ui/webid/identity_dialog_controller.cc
index 31782e9..4bb7df13 100644
--- a/chrome/browser/ui/webid/identity_dialog_controller.cc
+++ b/chrome/browser/ui/webid/identity_dialog_controller.cc
@@ -6,32 +6,9 @@
 
 #include <memory>
 
-#include "base/strings/strcat.h"
 #include "base/strings/string_piece.h"
-#include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/webid/account_selection_view.h"
-#include "components/infobars/core/infobar.h"
-#include "components/url_formatter/elide_url.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "net/base/url_util.h"
-#include "url/gurl.h"
-
-namespace {
-
-std::string FormatUrlForDisplay(const GURL& url) {
-  std::string formatted_url_str =
-      net::IsLocalhost(url)
-          ? url.GetWithEmptyPath().spec()
-          : net::registry_controlled_domains::GetDomainAndRegistry(
-                url,
-                net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-  return base::UTF16ToUTF8(url_formatter::FormatUrlForSecurityDisplay(
-      GURL(url.scheme() + "://" + formatted_url_str),
-      url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS));
-}
-
-}  // namespace
 
 IdentityDialogController::IdentityDialogController() = default;
 
@@ -47,41 +24,25 @@
 
 void IdentityDialogController::ShowAccountsDialog(
     content::WebContents* rp_web_contents,
+    const std::string& rp_for_display,
     const std::vector<content::IdentityProviderData>& identity_provider_data,
     content::IdentityRequestAccount::SignInMode sign_in_mode,
     AccountSelectionCallback on_selected,
     DismissCallback dismiss_callback) {
-  // TODO(crbug.com/1348262): Temporarily support only the first IDP, extend to
-  // support multiple IDPs.
-  GURL idp_url = identity_provider_data[0].idp_config_url;
-  std::vector<content::IdentityRequestAccount> accounts =
-      identity_provider_data[0].accounts;
-  content::IdentityProviderMetadata idp_metadata =
-      identity_provider_data[0].idp_metadata;
-  content::ClientIdData client_data = identity_provider_data[0].client_id_data;
-
-  // IDP scheme is expected to always be `https://`.
-  CHECK(idp_url.SchemeIs(url::kHttpsScheme));
   rp_web_contents_ = rp_web_contents;
   on_account_selection_ = std::move(on_selected);
   on_dismiss_ = std::move(dismiss_callback);
-  std::string rp_for_display =
-      FormatUrlForDisplay(rp_web_contents_->GetLastCommittedURL());
-  std::string idp_for_display = FormatUrlForDisplay(idp_url);
-
   if (!account_view_)
     account_view_ = AccountSelectionView::Create(this);
-  account_view_->Show(rp_for_display, idp_for_display, accounts, idp_metadata,
-                      client_data, sign_in_mode);
+  account_view_->Show(rp_for_display, identity_provider_data, sign_in_mode);
 }
 
 void IdentityDialogController::ShowFailureDialog(
     content::WebContents* rp_web_contents,
-    const GURL& idp_url,
+    const std::string& rp_for_display,
+    const std::string& idp_for_display,
     DismissCallback dismiss_callback) {
   const GURL rp_url = rp_web_contents->GetLastCommittedURL();
-  const std::string rp_for_display = FormatUrlForDisplay(rp_url);
-  const std::string idp_for_display = FormatUrlForDisplay(idp_url);
   rp_web_contents_ = rp_web_contents;
   on_dismiss_ = std::move(dismiss_callback);
   if (!account_view_)
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.h b/chrome/browser/ui/webid/identity_dialog_controller.h
index 0301746..ac18d98 100644
--- a/chrome/browser/ui/webid/identity_dialog_controller.h
+++ b/chrome/browser/ui/webid/identity_dialog_controller.h
@@ -10,7 +10,6 @@
 #include <vector>
 #include "base/callback.h"
 #include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/webid/account_selection_view.h"
 #include "content/public/browser/identity_request_dialog_controller.h"
 #include "content/public/browser/web_contents.h"
@@ -41,12 +40,14 @@
   // content::IdentityRequestDialogController
   void ShowAccountsDialog(
       content::WebContents* rp_web_contents,
+      const std::string& rp_for_display,
       const std::vector<content::IdentityProviderData>& identity_provider_data,
       content::IdentityRequestAccount::SignInMode sign_in_mode,
       AccountSelectionCallback on_selected,
       DismissCallback dismiss_callback) override;
   void ShowFailureDialog(content::WebContents* rp_web_contents,
-                         const GURL& idp_url,
+                         const std::string& rp_for_display,
+                         const std::string& idp_for_display,
                          DismissCallback dismiss_callback) override;
 
   // AccountSelectionView::Delegate:
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 5fa8c1a..3415820 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -59,6 +59,8 @@
     "isolated_web_apps/install_isolated_app_from_command_line.h",
     "isolated_web_apps/signed_web_bundle_reader.cc",
     "isolated_web_apps/signed_web_bundle_reader.h",
+    "isolation_data.cc",
+    "isolation_data.h",
     "isolation_prefs_utils.cc",
     "isolation_prefs_utils.h",
     "locks/app_lock.cc",
diff --git a/chrome/browser/web_applications/isolation_data.cc b/chrome/browser/web_applications/isolation_data.cc
new file mode 100644
index 0000000..fcf7dd6
--- /dev/null
+++ b/chrome/browser/web_applications/isolation_data.cc
@@ -0,0 +1,73 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/isolation_data.h"
+
+#include "base/functional/overloaded.h"
+
+namespace web_app {
+
+IsolationData::IsolationData(
+    absl::variant<InstalledBundle, DevModeBundle, DevModeProxy> content)
+    : content(content) {}
+IsolationData::~IsolationData() = default;
+IsolationData::IsolationData(const IsolationData&) = default;
+IsolationData& IsolationData::operator=(const IsolationData&) = default;
+IsolationData::IsolationData(IsolationData&&) = default;
+IsolationData& IsolationData::operator=(IsolationData&&) = default;
+
+bool IsolationData::operator==(const IsolationData& other) const {
+  return content == other.content;
+}
+bool IsolationData::operator!=(const IsolationData& other) const {
+  return !(*this == other);
+}
+
+bool IsolationData::InstalledBundle::operator==(
+    const IsolationData::InstalledBundle& other) const {
+  return path == other.path;
+}
+bool IsolationData::InstalledBundle::operator!=(
+    const IsolationData::InstalledBundle& other) const {
+  return !(*this == other);
+}
+
+bool IsolationData::DevModeBundle::operator==(
+    const IsolationData::DevModeBundle& other) const {
+  return path == other.path;
+}
+bool IsolationData::DevModeBundle::operator!=(
+    const IsolationData::DevModeBundle& other) const {
+  return !(*this == other);
+}
+
+bool IsolationData::DevModeProxy::operator==(
+    const IsolationData::DevModeProxy& other) const {
+  return proxy_url == other.proxy_url;
+}
+bool IsolationData::DevModeProxy::operator!=(
+    const IsolationData::DevModeProxy& other) const {
+  return !(*this == other);
+}
+
+base::Value IsolationData::AsDebugValue() const {
+  base::Value::Dict value;
+  absl::visit(
+      base::Overloaded{
+          [&value](const IsolationData::InstalledBundle& bundle) {
+            value.SetByDottedPath("content.installed_bundle.path", bundle.path);
+          },
+          [&value](const IsolationData::DevModeBundle& bundle) {
+            value.SetByDottedPath("content.dev_mode_bundle.path", bundle.path);
+          },
+          [&value](const IsolationData::DevModeProxy& proxy) {
+            value.SetByDottedPath("content.dev_mode_proxy.proxy_url",
+                                  proxy.proxy_url);
+          },
+      },
+      content);
+  return base::Value(std::move(value));
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/isolation_data.h b/chrome/browser/web_applications/isolation_data.h
new file mode 100644
index 0000000..8da6369
--- /dev/null
+++ b/chrome/browser/web_applications/isolation_data.h
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_ISOLATION_DATA_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATION_DATA_H_
+
+#include <string>
+
+#include "base/values.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+
+namespace web_app {
+
+// Contains IWA-specific information like bundle location. All IWAs will have
+// an instance of this struct in their WebApp object.
+struct IsolationData {
+  struct InstalledBundle {
+    bool operator==(const InstalledBundle& other) const;
+    bool operator!=(const InstalledBundle& other) const;
+
+    std::string path;
+  };
+
+  struct DevModeBundle {
+    bool operator==(const DevModeBundle& other) const;
+    bool operator!=(const DevModeBundle& other) const;
+
+    std::string path;
+  };
+
+  struct DevModeProxy {
+    bool operator==(const DevModeProxy& other) const;
+    bool operator!=(const DevModeProxy& other) const;
+
+    std::string proxy_url;
+  };
+
+  explicit IsolationData(
+      absl::variant<InstalledBundle, DevModeBundle, DevModeProxy> content);
+  ~IsolationData();
+  IsolationData(const IsolationData&);
+  IsolationData& operator=(const IsolationData&);
+  IsolationData(IsolationData&&);
+  IsolationData& operator=(IsolationData&&);
+
+  bool operator==(const IsolationData&) const;
+  bool operator!=(const IsolationData&) const;
+  base::Value AsDebugValue() const;
+
+  absl::variant<InstalledBundle, DevModeBundle, DevModeProxy> content;
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATION_DATA_H_
diff --git a/chrome/browser/web_applications/proto/web_app.proto b/chrome/browser/web_applications/proto/web_app.proto
index 1705b2f..f979a05 100644
--- a/chrome/browser/web_applications/proto/web_app.proto
+++ b/chrome/browser/web_applications/proto/web_app.proto
@@ -127,7 +127,7 @@
 }
 
 // Contains information specific to Isolated Web Apps.
-message IsolationData {
+message IsolationDataProto {
   // Information needed to load data from an installed web bundle.
   message InstalledBundle {
     optional string path = 1;
@@ -385,5 +385,5 @@
 
   // If present, signals that this app is an Isolated Web App, and contains
   // IWA-specific information like bundle location.
-  optional IsolationData isolation_data = 60;
+  optional IsolationDataProto isolation_data = 60;
 }
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.cc b/chrome/browser/web_applications/test/web_app_test_utils.cc
index 4888576..136eb3b 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_test_utils.cc
@@ -612,17 +612,17 @@
   }
 
   if (random.next_bool()) {
-    using IsolationDataContent = decltype(WebApp::IsolationData::content);
+    using IsolationDataContent = decltype(IsolationData::content);
     constexpr size_t kNumContentTypes =
         absl::variant_size<IsolationDataContent>::value;
     IsolationDataContent content_types[] = {
-        WebApp::IsolationData::InstalledBundle{.path = seed_str},
-        WebApp::IsolationData::DevModeBundle{.path = seed_str},
-        WebApp::IsolationData::DevModeProxy{.proxy_url = seed_str},
+        IsolationData::InstalledBundle{.path = seed_str},
+        IsolationData::DevModeBundle{.path = seed_str},
+        IsolationData::DevModeProxy{.proxy_url = seed_str},
     };
     static_assert(std::size(content_types) == kNumContentTypes);
 
-    WebApp::IsolationData isolation_data(
+    IsolationData isolation_data(
         content_types[random.next_uint(kNumContentTypes)]);
     app->SetIsolationData(isolation_data);
   }
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index da2d969..ec88611a 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -10,7 +10,6 @@
 
 #include "base/check_op.h"
 #include "base/containers/contains.h"
-#include "base/functional/overloaded.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_split.h"
@@ -513,76 +512,6 @@
   return root;
 }
 
-WebApp::IsolationData::IsolationData(
-    absl::variant<InstalledBundle, DevModeBundle, DevModeProxy> content)
-    : content(content) {}
-WebApp::IsolationData::~IsolationData() = default;
-WebApp::IsolationData::IsolationData(const IsolationData&) = default;
-WebApp::IsolationData& WebApp::IsolationData::operator=(
-    const WebApp::IsolationData&) = default;
-WebApp::IsolationData::IsolationData(IsolationData&&) = default;
-WebApp::IsolationData& WebApp::IsolationData::operator=(
-    WebApp::IsolationData&&) = default;
-bool WebApp::IsolationData::operator==(
-    const WebApp::IsolationData& other) const {
-  return content == other.content;
-}
-bool WebApp::IsolationData::operator!=(
-    const WebApp::IsolationData& other) const {
-  return !(*this == other);
-}
-bool WebApp::IsolationData::InstalledBundle::operator==(
-    const WebApp::IsolationData::InstalledBundle& other) const {
-  return path == other.path;
-}
-bool WebApp::IsolationData::InstalledBundle::operator!=(
-    const WebApp::IsolationData::InstalledBundle& other) const {
-  return !(*this == other);
-}
-bool WebApp::IsolationData::DevModeBundle::operator==(
-    const WebApp::IsolationData::DevModeBundle& other) const {
-  return path == other.path;
-}
-bool WebApp::IsolationData::DevModeBundle::operator!=(
-    const WebApp::IsolationData::DevModeBundle& other) const {
-  return !(*this == other);
-}
-bool WebApp::IsolationData::DevModeProxy::operator==(
-    const WebApp::IsolationData::DevModeProxy& other) const {
-  return proxy_url == other.proxy_url;
-}
-bool WebApp::IsolationData::DevModeProxy::operator!=(
-    const WebApp::IsolationData::DevModeProxy& other) const {
-  return !(*this == other);
-}
-base::Value WebApp::IsolationData::AsDebugValue() const {
-  base::Value::Dict value;
-  value.Set(
-      "content",
-      absl::visit(base::Overloaded{
-                      [](const WebApp::IsolationData::InstalledBundle& bundle) {
-                        base::Value::Dict content_dict;
-                        content_dict.SetByDottedPath("installed_bundle.path",
-                                                     bundle.path);
-                        return content_dict;
-                      },
-                      [](const WebApp::IsolationData::DevModeBundle& bundle) {
-                        base::Value::Dict content_dict;
-                        content_dict.SetByDottedPath("dev_mode_bundle.path",
-                                                     bundle.path);
-                        return content_dict;
-                      },
-                      [](const WebApp::IsolationData::DevModeProxy& proxy) {
-                        base::Value::Dict content_dict;
-                        content_dict.SetByDottedPath("dev_mode_proxy.proxy_url",
-                                                     proxy.proxy_url);
-                        return content_dict;
-                      },
-                  },
-                  content));
-  return base::Value(std::move(value));
-}
-
 bool WebApp::operator==(const WebApp& other) const {
   auto AsTuple = [](const WebApp& app) {
     // Keep in order declared in web_app.h.
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index f6a3b20..0a456af1 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -12,6 +12,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_data.h"
+#include "chrome/browser/web_applications/isolation_data.h"
 #include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "chrome/browser/web_applications/web_app_chromeos_data.h"
@@ -26,7 +27,6 @@
 #include "components/sync/model/string_ordinal.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "url/gurl.h"
@@ -308,41 +308,6 @@
     return current_os_integration_states_;
   }
 
-  struct IsolationData {
-    struct InstalledBundle {
-      bool operator==(const InstalledBundle& other) const;
-      bool operator!=(const InstalledBundle& other) const;
-
-      std::string path;
-    };
-    struct DevModeBundle {
-      bool operator==(const DevModeBundle& other) const;
-      bool operator!=(const DevModeBundle& other) const;
-
-      std::string path;
-    };
-    struct DevModeProxy {
-      bool operator==(const DevModeProxy& other) const;
-      bool operator!=(const DevModeProxy& other) const;
-
-      std::string proxy_url;
-    };
-
-    explicit IsolationData(
-        absl::variant<InstalledBundle, DevModeBundle, DevModeProxy> content);
-    ~IsolationData();
-    IsolationData(const IsolationData&);
-    IsolationData& operator=(const IsolationData&);
-    IsolationData(IsolationData&&);
-    IsolationData& operator=(IsolationData&&);
-
-    bool operator==(const IsolationData&) const;
-    bool operator!=(const IsolationData&) const;
-    base::Value AsDebugValue() const;
-
-    absl::variant<InstalledBundle, DevModeBundle, DevModeProxy> content;
-  };
-
   // If present, signals that this app is an Isolated Web App, and contains
   // IWA-specific information like bundle location.
   const absl::optional<IsolationData>& isolation_data() const {
diff --git a/chrome/browser/web_applications/web_app_database.cc b/chrome/browser/web_applications/web_app_database.cc
index ae4ca0f..fe4982a7 100644
--- a/chrome/browser/web_applications/web_app_database.cc
+++ b/chrome/browser/web_applications/web_app_database.cc
@@ -735,7 +735,6 @@
       web_app.always_show_toolbar_in_fullscreen());
 
   if (web_app.isolation_data().has_value()) {
-    using IsolationData = WebApp::IsolationData;
     auto* mutable_data = local_data->mutable_isolation_data();
     absl::visit(
         base::Overloaded{
@@ -1369,27 +1368,23 @@
 
   if (local_data.has_isolation_data()) {
     switch (local_data.isolation_data().content_case()) {
-      case IsolationData::ContentCase::kInstalledBundle:
-        web_app->SetIsolationData(
-            WebApp::IsolationData(WebApp::IsolationData::InstalledBundle{
-                .path =
-                    local_data.isolation_data().installed_bundle().path()}));
+      case IsolationDataProto::ContentCase::kInstalledBundle:
+        web_app->SetIsolationData(IsolationData(IsolationData::InstalledBundle{
+            .path = local_data.isolation_data().installed_bundle().path()}));
         break;
 
-      case IsolationData::ContentCase::kDevModeBundle:
-        web_app->SetIsolationData(
-            WebApp::IsolationData(WebApp::IsolationData::DevModeBundle{
-                .path = local_data.isolation_data().dev_mode_bundle().path()}));
+      case IsolationDataProto::ContentCase::kDevModeBundle:
+        web_app->SetIsolationData(IsolationData(IsolationData::DevModeBundle{
+            .path = local_data.isolation_data().dev_mode_bundle().path()}));
         break;
 
-      case IsolationData::ContentCase::kDevModeProxy:
-        web_app->SetIsolationData(
-            WebApp::IsolationData(WebApp::IsolationData::DevModeProxy{
-                .proxy_url =
-                    local_data.isolation_data().dev_mode_proxy().proxy_url()}));
+      case IsolationDataProto::ContentCase::kDevModeProxy:
+        web_app->SetIsolationData(IsolationData(IsolationData::DevModeProxy{
+            .proxy_url =
+                local_data.isolation_data().dev_mode_proxy().proxy_url()}));
         break;
 
-      case IsolationData::ContentCase::CONTENT_NOT_SET:
+      case IsolationDataProto::ContentCase::CONTENT_NOT_SET:
         DLOG(ERROR) << "WebApp proto isolation_data parse error: "
                     << "content not set";
         return nullptr;
diff --git a/chrome/browser/web_applications/web_app_database_unittest.cc b/chrome/browser/web_applications/web_app_database_unittest.cc
index a9428fb..7823cbb 100644
--- a/chrome/browser/web_applications/web_app_database_unittest.cc
+++ b/chrome/browser/web_applications/web_app_database_unittest.cc
@@ -543,8 +543,7 @@
     return web_app;
   }
 
-  std::unique_ptr<WebApp> CreateIsolatedWebApp(
-      WebApp::IsolationData isolation_data) {
+  std::unique_ptr<WebApp> CreateIsolatedWebApp(IsolationData isolation_data) {
     std::unique_ptr<WebApp> web_app = CreateMinimalWebApp();
     web_app->SetIsolationData(isolation_data);
     return web_app;
@@ -556,7 +555,7 @@
   }
 };
 
-TEST_F(WebAppDatabaseIsolationDataTest, NotIsolated) {
+TEST_F(WebAppDatabaseIsolationDataTest, DoesNotSetIsolationDataIfNotIsolated) {
   std::unique_ptr<WebApp> web_app = CreateMinimalWebApp();
 
   std::unique_ptr<WebApp> protoed_web_app = ToAndFromProto(*web_app);
@@ -566,40 +565,40 @@
                              absl::nullopt)));
 }
 
-TEST_F(WebAppDatabaseIsolationDataTest, InstalledBundle) {
-  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(WebApp::IsolationData(
-      WebApp::IsolationData::InstalledBundle{.path = "bundle_path"}));
+TEST_F(WebAppDatabaseIsolationDataTest, SavesInstalledBundleIsolationData) {
+  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(
+      IsolationData(IsolationData::InstalledBundle{.path = "bundle_path"}));
 
   std::unique_ptr<WebApp> protoed_web_app = ToAndFromProto(*web_app);
   EXPECT_THAT(*web_app, Eq(*protoed_web_app));
-  EXPECT_THAT(web_app->isolation_data()->content,
-              VariantWith<WebApp::IsolationData::InstalledBundle>(
-                  Field("path", &WebApp::IsolationData::InstalledBundle::path,
-                        Eq("bundle_path"))));
+  EXPECT_THAT(
+      web_app->isolation_data()->content,
+      VariantWith<IsolationData::InstalledBundle>(Field(
+          "path", &IsolationData::InstalledBundle::path, Eq("bundle_path"))));
 }
 
-TEST_F(WebAppDatabaseIsolationDataTest, DevModeBundle) {
-  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(WebApp::IsolationData(
-      WebApp::IsolationData::DevModeBundle{.path = "dev_bundle_path"}));
+TEST_F(WebAppDatabaseIsolationDataTest, SavesDevModeBundleIsolationData) {
+  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(
+      IsolationData(IsolationData::DevModeBundle{.path = "dev_bundle_path"}));
 
   std::unique_ptr<WebApp> protoed_web_app = ToAndFromProto(*web_app);
   EXPECT_THAT(*web_app, Eq(*protoed_web_app));
-  EXPECT_THAT(web_app->isolation_data()->content,
-              VariantWith<WebApp::IsolationData::DevModeBundle>(
-                  Field("path", &WebApp::IsolationData::DevModeBundle::path,
-                        Eq("dev_bundle_path"))));
+  EXPECT_THAT(
+      web_app->isolation_data()->content,
+      VariantWith<IsolationData::DevModeBundle>(Field(
+          "path", &IsolationData::DevModeBundle::path, Eq("dev_bundle_path"))));
 }
 
-TEST_F(WebAppDatabaseIsolationDataTest, DevModeProxy) {
-  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(WebApp::IsolationData(
-      WebApp::IsolationData::DevModeProxy{.proxy_url = "proxy"}));
+TEST_F(WebAppDatabaseIsolationDataTest, SavesDevModeProxyIsolationData) {
+  std::unique_ptr<WebApp> web_app = CreateIsolatedWebApp(
+      IsolationData(IsolationData::DevModeProxy{.proxy_url = "proxy"}));
 
   std::unique_ptr<WebApp> protoed_web_app = ToAndFromProto(*web_app);
   EXPECT_THAT(*web_app, Eq(*protoed_web_app));
-  EXPECT_THAT(web_app->isolation_data()->content,
-              VariantWith<WebApp::IsolationData::DevModeProxy>(Field(
-                  "proxy_url", &WebApp::IsolationData::DevModeProxy::proxy_url,
-                  Eq("proxy"))));
+  EXPECT_THAT(
+      web_app->isolation_data()->content,
+      VariantWith<IsolationData::DevModeProxy>(Field(
+          "proxy_url", &IsolationData::DevModeProxy::proxy_url, Eq("proxy"))));
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_unittest.cc b/chrome/browser/web_applications/web_app_unittest.cc
index b9c9caa..f3fa48fa2 100644
--- a/chrome/browser/web_applications/web_app_unittest.cc
+++ b/chrome/browser/web_applications/web_app_unittest.cc
@@ -312,8 +312,8 @@
 TEST(WebAppTest, IsolationDataDebugValue) {
   WebApp app{GenerateAppId(/*manifest_id=*/absl::nullopt,
                            GURL("https://example.com"))};
-  app.SetIsolationData(WebApp::IsolationData(
-      WebApp::IsolationData::InstalledBundle{.path = "random_path"}));
+  app.SetIsolationData(
+      IsolationData(IsolationData::InstalledBundle{.path = "random_path"}));
 
   EXPECT_TRUE(app.isolation_data().has_value());
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index ce40bc2..96eeba28 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1662054737-a2304e602c58f4dbfea78cbd32c47932b71309e6.profdata
+chrome-linux-main-1662076685-ce2d2cfe0cb810c5ee80a3a6f3b28b62baf6898e.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 14972e7..26bdc21 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1662054737-e7c5ffe7ab5274e96a9ed29563dba90729571cd0.profdata
+chrome-mac-arm-main-1662076685-60ec8ec62c940e118197dcccec23fed90825a7f2.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index f8ef0ce..3f21a57 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1662054737-3a7c0953e87fb6b04bbf815822887c78507b7fd3.profdata
+chrome-mac-main-1662076685-725e0a52c658bddfed9124e8672497abb8fd8ad8.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 3405b81..ba5aff1 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1662044321-d46a350ac727c4599f2d8be865b618622cce8e01.profdata
+chrome-win32-main-1662065959-26a19e0d361bee2390ff567d87db1bf564db8326.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 9286977d..631c9b5a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1662054737-0e309a4d1bb3ad78c455bcf60adf194a500d0c50.profdata
+chrome-win64-main-1662076685-fb2d4d68b7e1b9b66ba6ebb109b24a6affc4903f.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 411f774..8d1c8cb 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -100,6 +100,7 @@
       "$root_gen_dir/components/autofill/core/browser/autofill_address_rewriter_resources.pak",
       "$root_gen_dir/components/components_resources.pak",
       "$root_gen_dir/content/content_resources.pak",
+      "$root_gen_dir/content/indexed_db_resources.pak",
       "$root_gen_dir/content/quota_internals_resources.pak",
       "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
       "$root_gen_dir/net/net_resources.pak",
@@ -119,6 +120,7 @@
       "//components/optimization_guide/optimization_guide_internals/resources",
       "//components/resources",
       "//content:content_resources",
+      "//content/browser/resources/indexed_db:resources",
       "//content/browser/resources/quota:resources",
       "//mojo/public/js:resources",
       "//net:net_resources",
@@ -235,8 +237,11 @@
         "$root_gen_dir/ash/webui/ash_os_feedback_untrusted_resources.pak",
         "$root_gen_dir/ash/webui/ash_personalization_app_resources.pak",
         "$root_gen_dir/ash/webui/ash_print_management_resources.pak",
+        "$root_gen_dir/ash/webui/ash_projector_annotator_trusted_resources.pak",
+        "$root_gen_dir/ash/webui/ash_projector_annotator_untrusted_resources.pak",
         "$root_gen_dir/ash/webui/ash_projector_app_trusted_resources.pak",
         "$root_gen_dir/ash/webui/ash_projector_app_untrusted_resources.pak",
+        "$root_gen_dir/ash/webui/ash_projector_common_resources.pak",
         "$root_gen_dir/ash/webui/ash_scanning_app_resources.pak",
         "$root_gen_dir/ash/webui/ash_shimless_rma_resources.pak",
         "$root_gen_dir/ash/webui/ash_shortcut_customization_app_resources.pak",
@@ -302,9 +307,12 @@
         "//ash/webui/resources:os_feedback_untrusted_resources",
         "//ash/webui/resources:personalization_app_resources",
         "//ash/webui/resources:print_management_resources",
+        "//ash/webui/resources:projector_annotator_trusted_resources",
+        "//ash/webui/resources:projector_annotator_untrusted_resources",
         "//ash/webui/resources:projector_app_bundle_resources",
         "//ash/webui/resources:projector_app_trusted_resources",
         "//ash/webui/resources:projector_app_untrusted_resources",
+        "//ash/webui/resources:projector_common_resources",
         "//ash/webui/resources:scanning_app_resources",
         "//ash/webui/resources:shimless_rma_resources",
         "//ash/webui/resources:shortcut_customization_app_resources",
diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl
index c82173d..cec695a82 100644
--- a/chrome/common/extensions/api/autofill_private.idl
+++ b/chrome/common/extensions/api/autofill_private.idl
@@ -179,9 +179,6 @@
     // Credit card's network.
     DOMString? network;
 
-    // Credit card's image source.
-    DOMString? imageSrc;
-
     AutofillMetadata? metadata;
   };
 
diff --git a/chrome/common/extensions/chrome_manifest_url_handlers.cc b/chrome/common/extensions/chrome_manifest_url_handlers.cc
index 42bdbd1..0b6650f 100644
--- a/chrome/common/extensions/chrome_manifest_url_handlers.cc
+++ b/chrome/common/extensions/chrome_manifest_url_handlers.cc
@@ -96,25 +96,27 @@
 
   using UrlOverrideInfo = api::chrome_url_overrides::UrlOverrideInfo;
   auto url_overrides = std::make_unique<URLOverrides>();
-  auto property_map = std::map<const char*, const std::string*>{
-      {UrlOverrideInfo::kNewtab,
-       manifest_keys.chrome_url_overrides.newtab.get()},
-      {UrlOverrideInfo::kBookmarks,
-       manifest_keys.chrome_url_overrides.bookmarks.get()},
-      {UrlOverrideInfo::kHistory,
-       manifest_keys.chrome_url_overrides.history.get()},
-      {UrlOverrideInfo::kActivationmessage,
-       manifest_keys.chrome_url_overrides.activationmessage.get()},
-      {UrlOverrideInfo::kKeyboard,
-       manifest_keys.chrome_url_overrides.keyboard.get()}};
+  auto property_map =
+      std::map<const char*,
+               std::reference_wrapper<const absl::optional<std::string>>>{
+          {UrlOverrideInfo::kNewtab,
+           std::ref(manifest_keys.chrome_url_overrides.newtab)},
+          {UrlOverrideInfo::kBookmarks,
+           std::ref(manifest_keys.chrome_url_overrides.bookmarks)},
+          {UrlOverrideInfo::kHistory,
+           std::ref(manifest_keys.chrome_url_overrides.history)},
+          {UrlOverrideInfo::kActivationmessage,
+           std::ref(manifest_keys.chrome_url_overrides.activationmessage)},
+          {UrlOverrideInfo::kKeyboard,
+           std::ref(manifest_keys.chrome_url_overrides.keyboard)}};
 
-  for (auto& property : property_map) {
-    if (!property.second)
+  for (const auto& property : property_map) {
+    if (!property.second.get())
       continue;
 
     // Replace the entry with a fully qualified chrome-extension:// URL.
     url_overrides->chrome_url_overrides_[property.first] =
-        extension->GetResourceURL(*property.second);
+        extension->GetResourceURL(*property.second.get());
 
     // For component extensions, add override URL to extent patterns.
     if (extension->is_legacy_packaged_app() &&
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index bc8cb3dc..ccdc0330 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -234,7 +234,6 @@
         "chromeos/diagnostics/diagnostics_browsertest.js",
         "chromeos/firmware_update/firmware_update_browsertest.js",
         "chromeos/os_feedback_ui/os_feedback_browsertest.js",
-        "chromeos/print_management/print_management_browsertest.js",
         "chromeos/scanning/scanning_app_browsertest.js",
         "chromeos/shimless_rma/shimless_rma_browsertest.js",
         "nearby_share/nearby_browsertest.js",
@@ -266,6 +265,7 @@
         "chromeos/parent_access/parent_access_browsertest.js",
         "chromeos/personalization_app/personalization_app_component_browsertest.js",
         "chromeos/personalization_app/personalization_app_controller_browsertest.js",
+        "chromeos/print_management/print_management_browsertest.js",
         "chromeos/shortcut_customization/shortcut_customization_browsertest.js",
       ]
     }
@@ -580,6 +580,7 @@
       "chromeos/cloud_upload:build_grdp",
       "chromeos/manage_mirrorsync:build_grdp",
       "chromeos/personalization_app:build_grdp",
+      "chromeos/print_management:build_grdp",
       "chromeos/shortcut_customization:build_grdp",
       "//ui/file_manager:build_tests_gen_grdp",
       "//ui/file_manager:build_tests_grdp",
@@ -588,6 +589,7 @@
       "$target_gen_dir/chromeos/cloud_upload/resources.grdp",
       "$target_gen_dir/chromeos/personalization_app/resources.grdp",
       "$target_gen_dir/chromeos/shortcut_customization/resources.grdp",
+      "$target_gen_dir/chromeos/print_management/resources.grdp",
       "$target_gen_dir/chromeos/manage_mirrorsync/resources.grdp",
       "$root_gen_dir/ui/file_manager/tests_resources.grdp",
       "$root_gen_dir/ui/file_manager/tests_gen_resources.grdp",
diff --git a/chrome/test/data/webui/chromeos/print_management/.gitignore b/chrome/test/data/webui/chromeos/print_management/.gitignore
new file mode 100644
index 0000000..79f93fdd
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/print_management/.gitignore
@@ -0,0 +1,2 @@
+# Generated from ash/webui/personalization_app/tools/gen_tsconfig.py
+tsconfig.json
\ No newline at end of file
diff --git a/chrome/test/data/webui/chromeos/print_management/BUILD.gn b/chrome/test/data/webui/chromeos/print_management/BUILD.gn
new file mode 100644
index 0000000..11c2f939
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/print_management/BUILD.gn
@@ -0,0 +1,36 @@
+# 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.
+
+import("//tools/typescript/ts_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+ts_library("build_ts") {
+  root_dir = "."
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  in_files = [ "print_management_test.ts" ]
+  path_mappings = [
+    "chrome://print-management/*|" +
+        rebase_path("$root_gen_dir/ash/webui/print_management/resources/tsc/*",
+                    target_gen_dir),
+    "chrome://webui-test/*|" +
+        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
+                    target_gen_dir),
+  ]
+  deps = [
+    "//ash/webui/print_management/resources:build_ts",
+    "//ui/webui/resources:library",
+  ]
+  extra_deps = [ "../..:generate_definitions" ]
+}
+
+generate_grd("build_grdp") {
+  grd_prefix = "webui_chromeos_print_management_app"
+  out_grd = "$target_gen_dir/resources.grdp"
+
+  deps = [ ":build_ts" ]
+  manifest_files =
+      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
+  resource_path_prefix = "chromeos/print_management"
+}
diff --git a/chrome/test/data/webui/chromeos/print_management/print_management_browsertest.js b/chrome/test/data/webui/chromeos/print_management/print_management_browsertest.js
index f284865..bd1b58a0 100644
--- a/chrome/test/data/webui/chromeos/print_management/print_management_browsertest.js
+++ b/chrome/test/data/webui/chromeos/print_management/print_management_browsertest.js
@@ -10,17 +10,12 @@
 
 GEN('#include "content/public/test/browser_test.h"');
 
-/**
- * @constructor
- * @extends {PolymerTest}
- */
-function PrintManagementBrowserTest() {}
-
-PrintManagementBrowserTest.prototype = {
-  __proto__: PolymerTest.prototype,
-
-  browsePreload: 'chrome://print-management/test_loader.html?module=chromeos/' +
-      'print_management/print_management_test.js&host=test',
+var PrintManagementBrowserTest = class extends PolymerTest {
+  get browsePreload() {
+    return 'chrome://print-management/test_loader.html' +
+        '?module=chromeos/print_management/' +
+        'print_management_test.js';
+  }
 };
 
 TEST_F('PrintManagementBrowserTest', 'All', function() {
diff --git a/chrome/test/data/webui/chromeos/print_management/print_management_test.js b/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
similarity index 66%
rename from chrome/test/data/webui/chromeos/print_management/print_management_test.js
rename to chrome/test/data/webui/chromeos/print_management/print_management_test.ts
index c84828f..27ba0d3 100644
--- a/chrome/test/data/webui/chromeos/print_management/print_management_test.js
+++ b/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
@@ -3,20 +3,40 @@
 // found in the LICENSE file.
 
 import 'chrome://print-management/print_management.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
+import {IronIconElement} from '//resources/polymer/v3_0/iron-icon/iron-icon.js';
 import {setMetadataProviderForTesting} from 'chrome://print-management/mojo_interface_provider.js';
-import {ActivePrintJobState, CompletedPrintJobInfo, PrinterErrorCode, PrintingMetadataProviderRemote, PrintJobCompletionStatus, PrintJobInfo, PrintJobsObserverRemote} from 'chrome://print-management/printing_manager.mojom-webui.js';
+import {PrintJobEntryElement} from 'chrome://print-management/print_job_entry.js';
+import {PrintManagementElement} from 'chrome://print-management/print_management.js';
+import {ActivePrintJobInfo, ActivePrintJobState, CompletedPrintJobInfo, PrinterErrorCode, PrintingMetadataProviderInterface, PrintJobCompletionStatus, PrintJobInfo, PrintJobsObserverRemote} from 'chrome://print-management/printing_manager.mojom-webui.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {flushTasks} from 'chrome://test/test_util.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/test_util.js';
+
+export function initPrintJobEntryElement(): PrintJobEntryElement {
+  const element = document.createElement('print-job-entry');
+  document.body.appendChild(element);
+  flush();
+  return element;
+}
 
 /**
- * Returns the match for |selector| in |element|'s shadow DOM.
- * @param {element} Element
- * @param {selector} string
- * @return {!Element | null}
+ * Tear down an element. Remove from dom and call |flushTasks| to finish any
+ * async cleanup in polymer and execute pending promises.
  */
-function querySelector(element, selector) {
+export async function teardownElement(element: HTMLElement|null) {
+  if (!element) {
+    return;
+  }
+  element.remove();
+  await flushTasks();
+}
+
+// Returns the match for |selector| in |element|'s shadow DOM.
+function querySelector<E extends Element>(
+    element: Element, selector: string): E|null {
   if (!element) {
     return null;
   }
@@ -24,15 +44,11 @@
     return null;
   }
 
-  return element.shadowRoot.querySelector(selector);
+  return element.shadowRoot.querySelector<E>(selector);
 }
 
-/**
- * Converts a JS string to mojo_base::mojom::String16 object.
- * @param {string} str
- * @return {!object}
- */
-function strToMojoString16(str) {
+// Converts a JS string to mojo_base::mojom::String16 object.
+function strToMojoString16(str: string): {data: number[]} {
   const arr = [];
   for (var i = 0; i < str.length; i++) {
     arr[i] = str.charCodeAt(i);
@@ -43,49 +59,37 @@
 /**
  * Converts a JS time (milliseconds since UNIX epoch) to mojom::time
  * (microseconds since WINDOWS epoch).
- * @param {Date} jsDate
- * @return {number}
  */
-function convertToMojoTime(jsDate) {
+function convertToMojoTime(jsDate: Date): number {
   const windowsEpoch = new Date(Date.UTC(1601, 0, 1, 0, 0, 0));
   const jsEpoch = new Date(Date.UTC(1970, 0, 1, 0, 0, 0));
-  return ((jsEpoch - windowsEpoch) * 1000) + (jsDate.getTime() * 1000);
+  return ((jsEpoch.getTime() - windowsEpoch.getTime()) * 1000) +
+      (jsDate.getTime() * 1000);
 }
 
-/**
- * Converts utf16 to a readable string.
- * @param {!object} arr
- * @return {string}
- */
-function decodeString16(arr) {
+// Converts utf16 to a readable string.
+function decodeString16(arr: {data: number[]}): string {
   return arr.data.map(ch => String.fromCodePoint(ch)).join('');
 }
 
-/**
- * @param {string} id
- * @param {string} title
- * @param {number} date
- * @param {number} printerErrorCode
- * @param {?CompletedPrintJobInfo}
- *     completedInfo
- * @param {?ActivePrintJobInfo}
- *     activeInfo
- * @return {!Object}
- */
 function createJobEntry(
-    id, title, date, printerErrorCode, completedInfo, activeInfo) {
+    id: string, title: string, date: number, printerErrorCode: number,
+    completedInfo?: CompletedPrintJobInfo,
+    activeInfo?: ActivePrintJobInfo): PrintJobInfo {
   // Assert that only one of either |completedInfo| or |activeInfo| is non-null.
   assertTrue(completedInfo ? !activeInfo : !!activeInfo);
 
-  const jobEntry = {
+  const jobEntry: PrintJobInfo = {
     'id': id,
     'title': strToMojoString16(title),
-    'creationTime': {internalValue: date},
+    'creationTime': {internalValue: BigInt(date)},
     'printerId': 'printerId',
     'printerName': strToMojoString16('printerName'),
     'printerUri': {url: '192.168.1.1'},
     'numberOfPages': 4,
     'printerErrorCode': printerErrorCode,
+    'completedInfo': undefined,
+    'activePrintJobInfo': undefined,
   };
 
   if (completedInfo) {
@@ -96,23 +100,15 @@
   return jobEntry;
 }
 
-/**
- * @param {number} completionStatus
- * @return {!CompletedPrintJobInfo}
- */
-function createCompletedPrintJobInfo(completionStatus) {
+function createCompletedPrintJobInfo(completionStatus: number):
+    CompletedPrintJobInfo {
   const completedInfo = {'completionStatus': completionStatus};
   return completedInfo;
 }
 
-/**
- *
- * @param {number} printedPages
- * @param {!ActivePrintJobState}
- *     activeState
- * @return {!ActivePrintJobInfo}
- */
-function createOngoingPrintJobInfo(printedPages, activeState) {
+function createOngoingPrintJobInfo(
+    printedPages: number,
+    activeState: ActivePrintJobState): ActivePrintJobInfo {
   const activeInfo = {
     'printedPages': printedPages,
     'activeState': activeState,
@@ -120,86 +116,66 @@
   return activeInfo;
 }
 
-/**
- * @param{!Array<!PrintJobInfo>}
- *     expected
- * @param{!Array<!HTMLElement>} actual
- */
-function verifyPrintJobs(expected, actual) {
+function verifyPrintJobs(
+    expected: PrintJobInfo[], actual: PrintJobEntryElement[]) {
   assertEquals(expected.length, actual.length);
   for (let i = 0; i < expected.length; i++) {
-    const actualJobInfo = actual[i].jobEntry;
-    assertEquals(expected[i].id, actualJobInfo.id);
+    const actualJobInfo = actual[i]!.jobEntry;
+    const expectedJob = expected[i]!;
+    assertEquals(expectedJob.id, actualJobInfo.id);
     assertEquals(
-        decodeString16(expected[i].title), decodeString16(actualJobInfo.title));
+        decodeString16(expectedJob.title), decodeString16(actualJobInfo.title));
     assertEquals(
-        Number(expected[i].creationTime.internalValue),
+        Number(expectedJob.creationTime.internalValue),
         Number(actualJobInfo.creationTime.internalValue));
-    assertEquals(expected[i].printerId, actualJobInfo.printerId);
+    assertEquals(expectedJob.printerId, actualJobInfo.printerId);
     assertEquals(
-        decodeString16(expected[i].printerName),
+        decodeString16(expectedJob.printerName),
         decodeString16(actualJobInfo.printerName));
-    assertEquals(expected[i].printerErrorCode, actualJobInfo.printerErrorCode);
+    assertEquals(expectedJob.printerErrorCode, actualJobInfo.printerErrorCode);
 
     if (actualJobInfo.completedInfo) {
       assertEquals(
-          expected[i].completedInfo.completionStatus,
+          expectedJob.completedInfo?.completionStatus,
           actualJobInfo.completedInfo.completionStatus);
     } else {
       assertEquals(
-          expected[i].activePrintJobInfo.printedPages,
-          actualJobInfo.activePrintJobInfo.printedPages);
+          expectedJob.activePrintJobInfo?.printedPages,
+          actualJobInfo.activePrintJobInfo?.printedPages);
       assertEquals(
-          expected[i].activePrintJobInfo.activeState,
-          actualJobInfo.activePrintJobInfo.activeState);
+          expectedJob.activePrintJobInfo?.activeState,
+          actualJobInfo.activePrintJobInfo?.activeState);
     }
   }
 }
 
-/**
- * @param {!HTMLElement} page
- * @return {!Array<!HTMLElement>}
- */
-function getHistoryPrintJobEntries(page) {
-  const entryList = querySelector(page, '#entryList');
+function getHistoryPrintJobEntries(page: HTMLElement): PrintJobEntryElement[] {
+  const entryList = querySelector(page, '#entryList')!;
   return Array.from(
       entryList.querySelectorAll('print-job-entry:not([hidden])'));
 }
 
-/**
- * @param {!HTMLElement} page
- * @return {!Array<!HTMLElement>}
- */
-function getOngoingPrintJobEntries(page) {
-  assertTrue(querySelector(page, '#ongoingEmptyState').hidden);
-  const entryList = querySelector(page, '#ongoingList');
+function getOngoingPrintJobEntries(page: HTMLElement): PrintJobEntryElement[] {
+  assertTrue(!!querySelector<HTMLElement>(page, '#ongoingEmptyState')?.hidden);
+  const entryList = querySelector(page, '#ongoingList')!;
   return Array.from(
       entryList.querySelectorAll('print-job-entry:not([hidden])'));
 }
 
-class FakePrintingMetadataProvider {
+class FakePrintingMetadataProvider implements
+    PrintingMetadataProviderInterface {
+  private resolverMap_ = new Map();
+
+  private printJobs_: PrintJobInfo[] = [];
+
+  private shouldAttemptCancel_ = true;
+  private isAllowedByPolicy_ = true;
+
+  private printJobsObserverRemote_: PrintJobsObserverRemote|null = null;
+
+  private expirationPeriod_ = 90;
+
   constructor() {
-    /** @private {!Map<string, !PromiseResolver>} */
-    this.resolverMap_ = new Map();
-
-    /**
-     * @private {!Array<PrintJobInfo>}
-     */
-    this.printJobs_ = [];
-
-    /** @private boolean */
-    this.shouldAttemptCancel_ = true;
-    this.isAllowedByPolicy_ = true;
-
-    /**
-     * @private
-     *     {?PrintJobsObserverRemote}
-     */
-    this.printJobsObserverRemote_;
-
-    /** @private {number} */
-    this.expirationPeriod_ = 90;
-
     this.resetForTest();
   }
 
@@ -221,119 +197,80 @@
         'getPrintJobHistoryExpirationPeriod', new PromiseResolver());
   }
 
-  /**
-   * @param {string} methodName
-   * @return {!PromiseResolver}
-   * @private
-   */
-  getResolver_(methodName) {
+  private getResolver_(methodName: string): PromiseResolver<void> {
     const method = this.resolverMap_.get(methodName);
     assertTrue(!!method, `Method '${methodName}' not found.`);
     return method;
   }
 
-  /**
-   * @param {string} methodName
-   * @protected
-   */
-  methodCalled(methodName) {
+  protected methodCalled(methodName: string) {
     this.getResolver_(methodName).resolve();
   }
 
-  /**
-   * @param {string} methodName
-   * @return {!Promise}
-   */
-  whenCalled(methodName) {
+  whenCalled(methodName: string): Promise<any> {
     return this.getResolver_(methodName).promise.then(() => {
       // Support sequential calls to whenCalled by replacing the promise.
       this.resolverMap_.set(methodName, new PromiseResolver());
     });
   }
 
-  /**
-   * @return
-   *      {PrintJobsObserverRemote}
-   */
-  getObserverRemote() {
-    return this.printJobsObserverRemote_;
+  getObserverRemote(): PrintJobsObserverRemote {
+    return this.printJobsObserverRemote_!;
   }
 
-  /**
-   * @param {?Array<!PrintJobInfo>}
-   *     printJobs
-   */
-  setPrintJobs(printJobs) {
+  setPrintJobs(printJobs: PrintJobInfo[]) {
     this.printJobs_ = printJobs;
   }
 
-  /** @param {number} expirationPeriod */
-  setExpirationPeriod(expirationPeriod) {
+  setExpirationPeriod(expirationPeriod: number) {
     this.expirationPeriod_ = expirationPeriod;
   }
 
-  /**
-   * @param {boolean} shouldAttemptCancel
-   */
-  setShouldAttemptCancel(shouldAttemptCancel) {
+  setShouldAttemptCancel(shouldAttemptCancel: boolean) {
     this.shouldAttemptCancel_ = shouldAttemptCancel;
   }
 
-  /**
-   * @param {boolean} isAllowedByPolicy
-   */
-  setDeletePrintJobPolicy(isAllowedByPolicy) {
+  setDeletePrintJobPolicy(isAllowedByPolicy: boolean) {
     this.isAllowedByPolicy_ = isAllowedByPolicy;
   }
 
-  /**
-   * @param {PrintJobInfo} job
-   */
-  addPrintJob(job) {
+  addPrintJob(job: PrintJobInfo) {
     this.printJobs_ = this.printJobs_.concat(job);
   }
 
   simulatePrintJobsDeletedfromDatabase() {
     this.printJobs_ = [];
-    this.printJobsObserverRemote_.onAllPrintJobsDeleted();
+    this.printJobsObserverRemote_!.onAllPrintJobsDeleted();
   }
 
-  /**
-   * @param {PrintJobInfo} job
-   */
-  simulateUpdatePrintJob(job) {
-    if (job.activePrintJobInfo.activeState ===
+  simulateUpdatePrintJob(job: PrintJobInfo) {
+    if (job.activePrintJobInfo?.activeState ===
         ActivePrintJobState.kDocumentDone) {
       // Create copy of |job| to modify.
       const updatedJob = Object.assign({}, job);
-      updatedJob.activePrintJobInfo = null;
+      updatedJob.activePrintJobInfo = undefined;
       updatedJob.completedInfo =
           createCompletedPrintJobInfo(PrintJobCompletionStatus.kPrinted);
       // Replace with updated print job.
       const idx =
-          this.printJobs_.findIndex(arr_job => arr_job.id === updatedJob.id);
+          this.printJobs_.findIndex(arrJob => arrJob.id === updatedJob.id);
       if (idx !== -1) {
         this.printJobs_.splice(idx, 1, updatedJob);
       }
     }
-    this.printJobsObserverRemote_.onPrintJobUpdate(job);
+    this.printJobsObserverRemote_!.onPrintJobUpdate(job);
   }
 
   // printingMetadataProvider methods
 
-  /**
-   * @return {!Promise<{printJobs:
-   *     !Array<PrintJobInfo>}>}
-   */
-  getPrintJobs() {
+  getPrintJobs(): Promise<{printJobs: PrintJobInfo[]}> {
     return new Promise(resolve => {
       this.methodCalled('getPrintJobs');
       resolve({printJobs: this.printJobs_ || []});
     });
   }
 
-  /** @return {!Promise<{success: boolean}>} */
-  deleteAllPrintJobs() {
+  deleteAllPrintJobs(): Promise<{success: boolean}> {
     return new Promise(resolve => {
       this.printJobs_ = [];
       this.methodCalled('deleteAllPrintJobs');
@@ -341,16 +278,16 @@
     });
   }
 
-  /** @return {!Promise<{isAllowedByPolicy: boolean}>} */
-  getDeletePrintJobHistoryAllowedByPolicy() {
+  getDeletePrintJobHistoryAllowedByPolicy():
+      Promise<{isAllowedByPolicy: boolean}> {
     return new Promise(resolve => {
       this.methodCalled('getDeletePrintJobHistoryAllowedByPolicy');
       resolve({isAllowedByPolicy: this.isAllowedByPolicy_});
     });
   }
 
-  /** @return {!Promise<{expirationPeriod: number}>} */
-  getPrintJobHistoryExpirationPeriod() {
+  getPrintJobHistoryExpirationPeriod():
+      Promise<{expirationPeriodInDays: number, isFromPolicy: boolean}> {
     return new Promise(resolve => {
       this.methodCalled('getPrintJobHistoryExpirationPeriod');
       resolve({
@@ -360,23 +297,14 @@
     });
   }
 
-  /**
-   * @param {!string} id
-   * @return {!Promise<{attemptedCancel}>}
-   */
-  cancelPrintJob(id) {
+  cancelPrintJob(): Promise<{attemptedCancel: boolean}> {
     return new Promise(resolve => {
       this.methodCalled('cancelPrintJob');
-      resolve({attempedCancel: this.shouldAttemptCancel_});
+      resolve({attemptedCancel: this.shouldAttemptCancel_});
     });
   }
 
-  /**
-   * @param
-   * {!PrintJobsObserverRemote} remote
-   * @return {!Promise}
-   */
-  observePrintJobs(remote) {
+  observePrintJobs(remote: PrintJobsObserverRemote): Promise<void> {
     return new Promise(resolve => {
       this.printJobsObserverRemote_ = remote;
       this.methodCalled('observePrintJobs');
@@ -386,64 +314,45 @@
 }
 
 suite('PrintManagementTest', () => {
-  /** @type {?PrintManagementElement} */
-  let page = null;
+  let page: PrintManagementElement|null = null;
 
-  /**
-   * @type {?PrintingMetadataProviderRemote}
-   */
-  let mojoApi_;
+  let mojoApi_: FakePrintingMetadataProvider;
 
   suiteSetup(() => {
     mojoApi_ = new FakePrintingMetadataProvider();
     setMetadataProviderForTesting(mojoApi_);
   });
 
-  setup(function() {
-    PolymerTest.clearBody();
-  });
-
   teardown(function() {
     mojoApi_.resetForTest();
-    page.remove();
+    page?.remove();
     page = null;
   });
 
-  /**
-   * @param {?Array<!PrintJobInfo>}
-   *     printJobs
-   * @return {!Promise}
-   */
-  function initializePrintManagementApp(printJobs) {
+  function initializePrintManagementApp(printJobs: PrintJobInfo[]):
+      Promise<any> {
     mojoApi_.setPrintJobs(printJobs);
-    page = document.createElement('print-management');
+    page = document.createElement('print-management') as PrintManagementElement;
     document.body.appendChild(page);
     assertTrue(!!page);
     flush();
     return mojoApi_.whenCalled('observePrintJobs');
   }
 
-  /**
-   * @param {!HtmlElement} jobEntryElement
-   * @param {FakePrintingMetadataProvider} mojoApi
-   * @param {boolean} shouldAttemptCancel
-   * @param {?Array<!PrintJobInfo>}
-   *    expectedHistoryList
-   * @return {!Promise}
-   */
   function simulateCancelPrintJob(
-      jobEntryElement, mojoApi, shouldAttemptCancel, expectedHistoryList) {
+      jobEntryElement: PrintJobEntryElement,
+      mojoApi: FakePrintingMetadataProvider, shouldAttemptCancel: boolean,
+      expectedHistoryList: PrintJobInfo[]): Promise<any> {
     mojoApi.setShouldAttemptCancel(shouldAttemptCancel);
 
-    const cancelButton =
-        querySelector(jobEntryElement, '#cancelPrintJobButton');
+    const cancelButton = querySelector<HTMLButtonElement>(
+        jobEntryElement, '#cancelPrintJobButton')!;
     cancelButton.click();
     return mojoApi.whenCalled('cancelPrintJob').then(() => {
       // Create copy of |jobEntryElement.jobEntry| to modify.
       const updatedJob = Object.assign({}, jobEntryElement.jobEntry);
       updatedJob.activePrintJobInfo = createOngoingPrintJobInfo(
-          /*printedPages=*/ 0, ActivePrintJobState.kDocumentDone,
-          PrinterErrorCode.kNoError);
+          /*printedPages=*/ 0, ActivePrintJobState.kDocumentDone);
       // Simulate print jobs cancelled notification update sent.
       mojoApi.getObserverRemote().onPrintJobUpdate(updatedJob);
 
@@ -460,7 +369,7 @@
       createJobEntry(
           'newest', 'titleA',
           convertToMojoTime(new Date(Date.UTC(2020, 3, 1, 1, 1, 1))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
     ];
     // Print job metadata will be stored for 1 day.
     mojoApi_.setExpirationPeriod(1);
@@ -473,8 +382,8 @@
           return mojoApi_.whenCalled('getPrintJobHistoryExpirationPeriod');
         })
         .then(() => {
-          const historyInfoTooltip = querySelector(page, 'paper-tooltip');
-          assertEquals(expectedText, historyInfoTooltip.textContent.trim());
+          const historyInfoTooltip = querySelector(page!, 'paper-tooltip');
+          assertEquals(expectedText, historyInfoTooltip?.textContent?.trim());
         });
   });
 
@@ -486,7 +395,7 @@
       createJobEntry(
           'newest', 'titleA',
           convertToMojoTime(new Date(Date.UTC(2020, 3, 1, 1, 1, 1))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
     ];
 
     // Print job metadata will be stored for 90 days which is the default
@@ -501,8 +410,8 @@
           return mojoApi_.whenCalled('getPrintJobHistoryExpirationPeriod');
         })
         .then(() => {
-          const historyInfoTooltip = querySelector(page, 'paper-tooltip');
-          assertEquals(expectedText, historyInfoTooltip.textContent.trim());
+          const historyInfoTooltip = querySelector(page!, 'paper-tooltip');
+          assertEquals(expectedText, historyInfoTooltip?.textContent?.trim());
         });
   });
 
@@ -515,7 +424,7 @@
       createJobEntry(
           'newest', 'titleA',
           convertToMojoTime(new Date(Date.UTC(2020, 3, 1, 1, 1, 1))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
     ];
 
     // When this policy is set to a value of -1, the print jobs metadata is
@@ -530,8 +439,8 @@
           return mojoApi_.whenCalled('getPrintJobHistoryExpirationPeriod');
         })
         .then(() => {
-          const historyInfoTooltip = querySelector(page, 'paper-tooltip');
-          assertEquals(expectedText, historyInfoTooltip.textContent.trim());
+          const historyInfoTooltip = querySelector(page!, 'paper-tooltip');
+          assertEquals(expectedText, historyInfoTooltip?.textContent?.trim());
         });
   });
 
@@ -543,7 +452,7 @@
       createJobEntry(
           'newest', 'titleA',
           convertToMojoTime(new Date(Date.UTC(2020, 3, 1, 1, 1, 1))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
     ];
 
     // Print job metadata will be stored for 4 days.
@@ -557,8 +466,8 @@
           return mojoApi_.whenCalled('getPrintJobHistoryExpirationPeriod');
         })
         .then(() => {
-          const historyInfoTooltip = querySelector(page, 'paper-tooltip');
-          assertEquals(expectedText, historyInfoTooltip.textContent.trim());
+          const historyInfoTooltip = querySelector(page!, 'paper-tooltip');
+          assertEquals(expectedText, historyInfoTooltip?.textContent?.trim());
         });
   });
 
@@ -569,15 +478,15 @@
       createJobEntry(
           'newest', 'titleA',
           convertToMojoTime(new Date(Date.UTC(2020, 3, 1, 1, 1, 1))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
       createJobEntry(
           'middle', 'titleB',
           convertToMojoTime(new Date(Date.UTC(2020, 2, 1, 1, 1, 1))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
       createJobEntry(
           'oldest', 'titleC',
           convertToMojoTime(new Date(Date.UTC(2020, 1, 1, 1, 1, 1))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
     ];
 
     // Initialize with a reversed array of |expectedArr|, since we expect the
@@ -589,7 +498,7 @@
         })
         .then(() => {
           flush();
-          verifyPrintJobs(expectedArr, getHistoryPrintJobEntries(page));
+          verifyPrintJobs(expectedArr, getHistoryPrintJobEntries(page!));
         });
   });
 
@@ -602,8 +511,10 @@
         })
         .then(() => {
           flush();
-          assertTrue(querySelector(page, '#clearAllButton').disabled);
-          assertTrue(!querySelector(page, '#policyIcon'));
+          assertTrue(
+              !!querySelector<HTMLButtonElement>(page!, '#clearAllButton')
+                    ?.disabled);
+          assertTrue(!querySelector(page!, '#policyIcon'));
         });
   });
 
@@ -613,7 +524,7 @@
         convertToMojoTime(new Date(Date.UTC(2020, 3, 1, 1, 1, 1))),
         PrinterErrorCode.kNoError,
         createCompletedPrintJobInfo(PrintJobCompletionStatus.kPrinted),
-        /*activeInfo=*/ null)];
+        /*activeInfo=*/ undefined)];
     // Set policy to prevent user from deleting history.
     mojoApi_.setDeletePrintJobPolicy(/*isAllowedByPolicy=*/ false);
     return initializePrintManagementApp(expectedArr)
@@ -622,8 +533,10 @@
         })
         .then(() => {
           flush();
-          assertTrue(querySelector(page, '#clearAllButton').disabled);
-          assertTrue(!!querySelector(page, '#policyIcon'));
+          assertTrue(
+              !!querySelector<HTMLButtonElement>(page!, '#clearAllButton')
+                    ?.disabled);
+          assertTrue(!!querySelector(page!, '#policyIcon'));
         });
   });
 
@@ -633,16 +546,16 @@
     const expectedArr = [
       createJobEntry(
           'fileA', 'titleA',
-          convertToMojoTime(new Date(Date('February 5, 2020 03:24:00'))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          convertToMojoTime(new Date(Date.parse('February 5, 2020 03:24:00'))),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
       createJobEntry(
           'fileB', 'titleB',
-          convertToMojoTime(new Date(Date('February 5, 2020 03:24:00'))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          convertToMojoTime(new Date(Date.parse('February 5, 2020 03:24:00'))),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
       createJobEntry(
           'fileC', 'titleC',
-          convertToMojoTime(new Date(Date('February 5, 2020 03:24:00'))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          convertToMojoTime(new Date(Date.parse('February 5, 2020 03:24:00'))),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
     ];
 
     return initializePrintManagementApp(expectedArr)
@@ -654,17 +567,19 @@
         })
         .then(() => {
           flush();
-          verifyPrintJobs(expectedArr, getHistoryPrintJobEntries(page));
+          verifyPrintJobs(expectedArr, getHistoryPrintJobEntries(page!));
 
           // Click the clear all button.
-          const button = querySelector(page, '#clearAllButton');
+          const button =
+              querySelector<HTMLButtonElement>(page!, '#clearAllButton')!;
           button.click();
           flush();
           // Verify that the confirmation dialog shows up and click on the
           // confirmation button.
-          const dialog = querySelector(page, '#clearHistoryDialog');
+          const dialog = querySelector(page!, '#clearHistoryDialog');
           assertTrue(!!dialog);
-          const dialogActionButton = querySelector(dialog, '.action-button');
+          const dialogActionButton =
+              querySelector<HTMLButtonElement>(dialog, '.action-button')!;
           assertTrue(!dialogActionButton.disabled);
           dialogActionButton.click();
           assertTrue(dialogActionButton.disabled);
@@ -674,9 +589,11 @@
           flush();
           // After clearing the history list, expect that the history list and
           // header are no longer
-          assertTrue(!querySelector(page, '#entryList'));
-          assertTrue(!querySelector(page, '#historyHeaderContainer'));
-          assertTrue(querySelector(page, '#clearAllButton').disabled);
+          assertTrue(!querySelector(page!, '#entryList'));
+          assertTrue(!querySelector(page!, '#historyHeaderContainer'));
+          assertTrue(
+              !!querySelector<HTMLButtonElement>(page!, '#clearAllButton')
+                    ?.disabled);
         });
   });
 
@@ -685,17 +602,17 @@
         createCompletedPrintJobInfo(PrintJobCompletionStatus.kPrinted);
     const expectedArr = [
       createJobEntry(
-          'fileA', 'titleA',
-          convertToMojoTime(new Date(Date('February 5, 2020 03:24:00'))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          'fileC', 'titleC',
+          convertToMojoTime(new Date(Date.parse('February 7, 2020 03:24:00'))),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
       createJobEntry(
           'fileB', 'titleB',
-          convertToMojoTime(new Date(Date('February 6, 2020 03:24:00'))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          convertToMojoTime(new Date(Date.parse('February 6, 2020 03:24:00'))),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
       createJobEntry(
-          'fileC', 'titleC',
-          convertToMojoTime(new Date(Date('February 7, 2020 03:24:00'))),
-          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ null),
+          'fileA', 'titleA',
+          convertToMojoTime(new Date(Date.parse('February 5, 2020 03:24:00'))),
+          PrinterErrorCode.kNoError, completedInfo, /*activeInfo=*/ undefined),
     ];
 
     return initializePrintManagementApp(expectedArr)
@@ -704,7 +621,7 @@
         })
         .then(() => {
           flush();
-          verifyPrintJobs(expectedArr, getHistoryPrintJobEntries(page));
+          verifyPrintJobs(expectedArr, getHistoryPrintJobEntries(page!));
 
           // Simulate observer call that signals all print jobs have been
           // deleted. Expect the UI to retrieve an empty list of print jobs.
@@ -715,9 +632,11 @@
           flush();
           // After clearing the history list, expect that the history list and
           // header are no longer
-          assertTrue(!querySelector(page, '#entryList'));
-          assertTrue(!querySelector(page, '#historyHeaderContainer'));
-          assertTrue(querySelector(page, '#clearAllButton').disabled);
+          assertTrue(!querySelector(page!, '#entryList'));
+          assertTrue(!querySelector(page!, '#historyHeaderContainer'));
+          assertTrue(
+              !!querySelector<HTMLButtonElement>(page!, '#clearAllButton')
+                    ?.disabled);
         });
   });
 
@@ -730,7 +649,7 @@
           flush();
           // Header should be not be rendered since no there are no completed
           // print jobs in the history.
-          assertTrue(!querySelector(page, '#historyHeaderContainer'));
+          assertTrue(!querySelector(page!, '#historyHeaderContainer'));
         });
   });
 
@@ -742,12 +661,12 @@
     const expectedArr = [
       createJobEntry(
           'fileA', 'titleA',
-          convertToMojoTime(new Date(Date('February 5, 2020 03:23:00'))),
-          PrinterErrorCode.kNoError, /*completedInfo=*/ null, activeInfo1),
+          convertToMojoTime(new Date(Date.parse('February 5, 2020 03:23:00'))),
+          PrinterErrorCode.kNoError, /*completedInfo=*/ undefined, activeInfo1),
       createJobEntry(
           'fileB', 'titleB',
-          convertToMojoTime(new Date(Date('February 5, 2020 03:24:00'))),
-          PrinterErrorCode.kNoError, /*completedInfo=*/ null, activeInfo2),
+          convertToMojoTime(new Date(Date.parse('February 5, 2020 03:24:00'))),
+          PrinterErrorCode.kNoError, /*completedInfo=*/ undefined, activeInfo2),
     ];
 
     return initializePrintManagementApp(expectedArr)
@@ -756,7 +675,7 @@
         })
         .then(() => {
           flush();
-          verifyPrintJobs(expectedArr, getOngoingPrintJobEntries(page));
+          verifyPrintJobs(expectedArr, getOngoingPrintJobEntries(page!));
         });
   });
 
@@ -765,7 +684,7 @@
       createJobEntry(
           'fileA', 'titleA',
           convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-          PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+          PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
           createOngoingPrintJobInfo(
               /*printedPages=*/ 0, ActivePrintJobState.kStarted)),
     ];
@@ -775,8 +694,8 @@
     const expectedUpdatedArr = [
       createJobEntry(
           'fileA', 'titleA',
-          convertToMojoTime(new Date(Date('February 5, 2020 03:24:00'))),
-          PrinterErrorCode.kNoError, /*completedInfo=*/ null, activeInfo2),
+          convertToMojoTime(new Date(Date.parse('February 5, 2020 03:24:00'))),
+          PrinterErrorCode.kNoError, /*completedInfo=*/ undefined, activeInfo2),
     ];
 
     return initializePrintManagementApp(expectedArr)
@@ -785,13 +704,13 @@
         })
         .then(() => {
           flush();
-          verifyPrintJobs(expectedArr, getOngoingPrintJobEntries(page));
-          mojoApi_.simulateUpdatePrintJob(expectedUpdatedArr[0]);
+          verifyPrintJobs(expectedArr, getOngoingPrintJobEntries(page!));
+          mojoApi_.simulateUpdatePrintJob(expectedUpdatedArr[0]!);
           return flushTasks();
         })
         .then(() => {
           flush();
-          verifyPrintJobs(expectedUpdatedArr, getOngoingPrintJobEntries(page));
+          verifyPrintJobs(expectedUpdatedArr, getOngoingPrintJobEntries(page!));
         });
   });
 
@@ -800,7 +719,7 @@
       createJobEntry(
           'fileA', 'titleA',
           convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-          PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+          PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
           createOngoingPrintJobInfo(
               /*printedPages=*/ 0, ActivePrintJobState.kStarted)),
     ];
@@ -811,7 +730,8 @@
       createJobEntry(
           'fileA', 'titleA',
           convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-          PrinterErrorCode.kOutOfPaper, /*completedInfo=*/ null, activeInfo2),
+          PrinterErrorCode.kOutOfPaper, /*completedInfo=*/ undefined,
+          activeInfo2),
     ];
 
     return initializePrintManagementApp(expectedArr)
@@ -820,13 +740,13 @@
         })
         .then(() => {
           flush();
-          verifyPrintJobs(expectedArr, getOngoingPrintJobEntries(page));
-          mojoApi_.simulateUpdatePrintJob(expectedUpdatedArr[0]);
+          verifyPrintJobs(expectedArr, getOngoingPrintJobEntries(page!));
+          mojoApi_.simulateUpdatePrintJob(expectedUpdatedArr[0]!);
           return flushTasks();
         })
         .then(() => {
           flush();
-          verifyPrintJobs(expectedUpdatedArr, getOngoingPrintJobEntries(page));
+          verifyPrintJobs(expectedUpdatedArr, getOngoingPrintJobEntries(page!));
         });
   });
 
@@ -834,14 +754,14 @@
     const initialJob = [createJobEntry(
         'fileA', 'titleA',
         convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 0, ActivePrintJobState.kStarted))];
 
     const newOngoingJob = createJobEntry(
         'fileB', 'titleB',
-        convertToMojoTime(new Date(Date('February 5, 2020 03:25:00'))),
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        convertToMojoTime(new Date(Date.parse('February 5, 2020 03:25:00'))),
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 1, ActivePrintJobState.kStarted));
 
@@ -851,31 +771,34 @@
         })
         .then(() => {
           flush();
-          verifyPrintJobs(initialJob, getOngoingPrintJobEntries(page));
+          verifyPrintJobs(initialJob, getOngoingPrintJobEntries(page!));
           mojoApi_.simulateUpdatePrintJob(newOngoingJob);
           return flushTasks();
         })
         .then(() => {
           flush();
           verifyPrintJobs(
-              [initialJob[0], newOngoingJob], getOngoingPrintJobEntries(page));
+              [initialJob[0]!, newOngoingJob],
+              getOngoingPrintJobEntries(page!));
         });
   });
 
   test('OngoingPrintJobCompletesAndUpdatesHistoryList', () => {
     const id = 'fileA';
     const title = 'titleA';
-    const date = convertToMojoTime(new Date(Date('February 5, 2020 03:24:00')));
+    const date =
+        convertToMojoTime(new Date(Date.parse('February 5, 2020 03:24:00')));
 
     const activeJob = createJobEntry(
-        id, title, date, PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        id, title, date, PrinterErrorCode.kNoError,
+        /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 0, ActivePrintJobState.kStarted));
 
     const expectedPrintJobArr = [createJobEntry(
         id, title, date, PrinterErrorCode.kNoError,
         createCompletedPrintJobInfo(PrintJobCompletionStatus.kPrinted),
-        /*activeInfo=*/ '')];
+        /*activeInfo=*/ undefined)];
 
     return initializePrintManagementApp([activeJob])
         .then(() => {
@@ -883,9 +806,9 @@
         })
         .then(() => {
           flush();
-          verifyPrintJobs([activeJob], getOngoingPrintJobEntries(page));
+          verifyPrintJobs([activeJob], getOngoingPrintJobEntries(page!));
           // Simulate ongoing print job has completed.
-          activeJob.activePrintJobInfo.activeState =
+          activeJob.activePrintJobInfo!.activeState =
               ActivePrintJobState.kDocumentDone;
           mojoApi_.simulateUpdatePrintJob(activeJob);
           // Simulate print job has been added to history.
@@ -893,7 +816,8 @@
         })
         .then(() => {
           flush();
-          verifyPrintJobs(expectedPrintJobArr, getHistoryPrintJobEntries(page));
+          verifyPrintJobs(
+              expectedPrintJobArr, getHistoryPrintJobEntries(page!));
         });
   });
 
@@ -906,8 +830,9 @@
           flush();
           // Assert that ongoing list is empty and the empty state message is
           // not hidden.
-          assertTrue(!querySelector(page, '#ongoingList'));
-          assertTrue(!querySelector(page, '#ongoingEmptyState').hidden);
+          assertTrue(!querySelector(page!, '#ongoingList'));
+          assertTrue(
+              !querySelector<HTMLElement>(page!, '#ongoingEmptyState')?.hidden);
         });
   });
 
@@ -915,13 +840,13 @@
     const kId = 'fileA';
     const kTitle = 'titleA';
     const kTime =
-        convertToMojoTime(new Date(Date('February 5, 2020 03:23:00')));
+        convertToMojoTime(new Date(Date.parse('February 5, 2020 03:23:00')));
     const expectedArr = [
       createJobEntry(
           kId, kTitle, kTime, PrinterErrorCode.kNoError,
-          /*completedInfo=*/ null,
+          /*completedInfo=*/ undefined,
           createOngoingPrintJobInfo(
-              /*printedPages=*/ 0, ActivePrintJobState.STARTED)),
+              /*printedPages=*/ 0, ActivePrintJobState.kStarted)),
     ];
 
     const expectedHistoryList = [createJobEntry(
@@ -934,19 +859,20 @@
         })
         .then(() => {
           flush();
-          const jobEntries = getOngoingPrintJobEntries(page);
+          const jobEntries = getOngoingPrintJobEntries(page!);
           verifyPrintJobs(expectedArr, jobEntries);
 
           return simulateCancelPrintJob(
-              jobEntries[0], mojoApi_,
+              jobEntries[0]!, mojoApi_,
               /*shouldAttemptCancel*/ true, expectedHistoryList);
         })
         .then(() => {
           flush();
           // Verify that there are no ongoing print jobs and history list is
           // populated.
-          assertTrue(!querySelector(page, '#ongoingList'));
-          verifyPrintJobs(expectedHistoryList, getHistoryPrintJobEntries(page));
+          assertTrue(!querySelector(page!, '#ongoingList'));
+          verifyPrintJobs(
+              expectedHistoryList, getHistoryPrintJobEntries(page!));
         });
   });
 
@@ -954,14 +880,14 @@
     const kId = 'fileA';
     const kTitle = 'titleA';
     const kTime =
-        convertToMojoTime(new Date(Date('February 5, 2020 03:23:00')));
+        convertToMojoTime(new Date(Date.parse('February 5, 2020 03:23:00')));
 
     const expectedArr = [
       createJobEntry(
           kId, kTitle, kTime, PrinterErrorCode.kNoError,
-          /*completedInfo=*/ null,
+          /*completedInfo=*/ undefined,
           createOngoingPrintJobInfo(
-              /*printedPages=*/ 0, ActivePrintJobState.STARTED)),
+              /*printedPages=*/ 0, ActivePrintJobState.kStarted)),
     ];
 
     const expectedHistoryList = [createJobEntry(
@@ -974,11 +900,11 @@
         })
         .then(() => {
           flush();
-          const jobEntries = getOngoingPrintJobEntries(page);
+          const jobEntries = getOngoingPrintJobEntries(page!);
           verifyPrintJobs(expectedArr, jobEntries);
 
           return simulateCancelPrintJob(
-              jobEntries[0], mojoApi_,
+              jobEntries[0]!, mojoApi_,
               /*shouldAttemptCancel=*/ false, expectedHistoryList);
         })
         .then(() => {
@@ -986,50 +912,29 @@
           // Verify that there are no ongoing print jobs and history list is
           // populated.
           // TODO(crbug/1093527): Show error message to user after UX guidance.
-          assertTrue(!querySelector(page, '#ongoingList'));
-          verifyPrintJobs(expectedHistoryList, getHistoryPrintJobEntries(page));
+          assertTrue(!querySelector(page!, '#ongoingList'));
+          verifyPrintJobs(
+              expectedHistoryList, getHistoryPrintJobEntries(page!));
         });
   });
 });
 
 suite('PrintJobEntryTest', () => {
-  /** @type {?HTMLElement} */
-  let jobEntryTestElement = null;
+  let jobEntryTestElement: PrintJobEntryElement|null = null;
 
-  /**
-   * @type {?PrintingMetadataProviderRemote}
-   */
-  let mojoApi_;
+  let mojoApi_: PrintingMetadataProviderInterface;
 
   suiteSetup(() => {
     mojoApi_ = new FakePrintingMetadataProvider();
     setMetadataProviderForTesting(mojoApi_);
   });
 
-  setup(() => {
-    jobEntryTestElement = document.createElement('print-job-entry');
-    assertTrue(!!jobEntryTestElement);
-    document.body.appendChild(jobEntryTestElement);
-  });
-
   teardown(() => {
-    jobEntryTestElement.remove();
-    jobEntryTestElement = null;
+    teardownElement(jobEntryTestElement);
   });
 
-  /**
-   * @param {!HTMLElement} element
-   * @param {number} newStatus
-   * @param {string} expectedStatus
-   */
-  function updateAndVerifyCompletionStatus(element, newStatus, expectedStatus) {
-    element.set('jobEntry.completedInfo.completionStatus', newStatus);
-    assertEquals(
-        expectedStatus,
-        querySelector(element, '#completionStatus').textContent.trim());
-  }
-
   test('initializeJobEntry', () => {
+    jobEntryTestElement = initPrintJobEntryElement();
     const expectedTitle = 'title.pdf';
     const expectedStatus = PrintJobCompletionStatus.kPrinted;
     const expectedPrinterError = PrinterErrorCode.kNoError;
@@ -1038,22 +943,22 @@
     const completedInfo = createCompletedPrintJobInfo(expectedStatus);
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', expectedTitle, expectedCreationTime, expectedPrinterError,
-        completedInfo, /*activeInfo=*/ null);
+        completedInfo, /*activeInfo=*/ undefined);
 
     flush();
 
     // Assert the title, creation time, and status are displayed correctly.
     assertEquals(
         expectedTitle,
-        querySelector(jobEntryTestElement, '#jobTitle').textContent.trim());
+        querySelector(jobEntryTestElement, '#jobTitle')?.textContent?.trim());
     assertEquals(
         'Printed',
-        querySelector(jobEntryTestElement, '#completionStatus')
-            .textContent.trim());
+        querySelector(
+            jobEntryTestElement, '#completionStatus')!.textContent?.trim());
     // Verify correct icon is shown.
     assertEquals(
         'print-management:file-pdf',
-        querySelector(jobEntryTestElement, '#fileIcon').icon);
+        querySelector<IronIconElement>(jobEntryTestElement, '#fileIcon')?.icon);
 
     // Change date and assert it shows the correct date (Feb 5, 2020);
     jobEntryTestElement.set('jobEntry.creationTime', {
@@ -1061,10 +966,12 @@
     });
     assertEquals(
         'Feb 5, 2020',
-        querySelector(jobEntryTestElement, '#creationTime').textContent.trim());
+        querySelector(jobEntryTestElement, '#creationTime')
+            ?.textContent?.trim());
   });
 
   test('initializeFailedJobEntry', () => {
+    jobEntryTestElement = initPrintJobEntryElement();
     const expectedTitle = 'titleA.doc';
     // Create a print job with a failed status with error: out of paper.
     jobEntryTestElement.jobEntry = createJobEntry(
@@ -1072,28 +979,30 @@
         convertToMojoTime(new Date('February 5, 2020 03:24:00')),
         PrinterErrorCode.kOutOfPaper,
         createCompletedPrintJobInfo(PrintJobCompletionStatus.kFailed),
-        /*activeInfo=*/ null);
+        /*activeInfo=*/ undefined);
 
     flush();
 
     // Assert the title, creation time, and status are displayed correctly.
     assertEquals(
         expectedTitle,
-        querySelector(jobEntryTestElement, '#jobTitle').textContent.trim());
+        querySelector(jobEntryTestElement, '#jobTitle')?.textContent?.trim());
     assertEquals(
         'Failed - Out of paper',
         querySelector(jobEntryTestElement, '#completionStatus')
-            .textContent.trim());
+            ?.textContent?.trim());
     // Verify correct icon is shown.
     assertEquals(
         'print-management:file-word',
-        querySelector(jobEntryTestElement, '#fileIcon').icon);
+        querySelector<IronIconElement>(jobEntryTestElement, '#fileIcon')?.icon);
     assertEquals(
         'Feb 5, 2020',
-        querySelector(jobEntryTestElement, '#creationTime').textContent.trim());
+        querySelector(jobEntryTestElement, '#creationTime')
+            ?.textContent?.trim());
   });
 
   test('initializeOngoingJobEntry', () => {
+    jobEntryTestElement = initPrintJobEntryElement();
     const expectedTitle = 'title';
     const expectedCreationTime =
         convertToMojoTime(new Date('February 5, 2020 03:24:00'));
@@ -1101,7 +1010,7 @@
 
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', expectedTitle, expectedCreationTime,
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(/*printedPages=*/ 1, expectedPrinterError));
 
     flush();
@@ -1109,20 +1018,22 @@
     // Assert the title, creation time, and status are displayed correctly.
     assertEquals(
         expectedTitle,
-        querySelector(jobEntryTestElement, '#jobTitle').textContent.trim());
+        querySelector(jobEntryTestElement, '#jobTitle')?.textContent?.trim());
     assertEquals(
         'Feb 5, 2020',
-        querySelector(jobEntryTestElement, '#creationTime').textContent.trim());
+        querySelector(jobEntryTestElement, '#creationTime')
+            ?.textContent?.trim());
     assertEquals(
         '1/4',
         querySelector(jobEntryTestElement, '#numericalProgress')
-            .textContent.trim());
+            ?.textContent?.trim());
     assertEquals(
         'print-management:file-generic',
-        querySelector(jobEntryTestElement, '#fileIcon').icon);
+        querySelector<IronIconElement>(jobEntryTestElement, '#fileIcon')?.icon);
   });
 
   test('initializeStoppedOngoingJobEntry', () => {
+    jobEntryTestElement = initPrintJobEntryElement();
     const expectedTitle = 'title';
     const expectedCreationTime =
         convertToMojoTime(new Date('February 5, 2020 03:24:00'));
@@ -1131,7 +1042,7 @@
 
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', expectedTitle, expectedCreationTime, expectedOngoingError,
-        /*completedInfo=*/ null,
+        /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(/*printedPages=*/ 1, expectedPrinterError));
 
     flush();
@@ -1139,23 +1050,26 @@
     // Assert the title, creation time, and status are displayed correctly.
     assertEquals(
         expectedTitle,
-        querySelector(jobEntryTestElement, '#jobTitle').textContent.trim());
+        querySelector(jobEntryTestElement, '#jobTitle')?.textContent?.trim());
     assertEquals(
         'Feb 5, 2020',
-        querySelector(jobEntryTestElement, '#creationTime').textContent.trim());
+        querySelector(jobEntryTestElement, '#creationTime')
+            ?.textContent?.trim());
     assertEquals(
         'Stopped - Out of paper',
-        querySelector(jobEntryTestElement, '#ongoingError').textContent.trim());
+        querySelector(jobEntryTestElement, '#ongoingError')
+            ?.textContent?.trim());
     assertEquals(
         'print-management:file-generic',
-        querySelector(jobEntryTestElement, '#fileIcon').icon);
+        querySelector<IronIconElement>(jobEntryTestElement, '#fileIcon')?.icon);
   });
 
   test('ensureGoogleFileIconIsShown', () => {
+    jobEntryTestElement = initPrintJobEntryElement();
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', /*fileName=*/ '.test - Google Docs',
         /*date=*/ convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 1,
             /*printerError=*/ ActivePrintJobState.kStarted));
@@ -1164,14 +1078,15 @@
 
     assertEquals(
         'print-management:file-gdoc',
-        querySelector(jobEntryTestElement, '#fileIcon').icon);
+        querySelector<IronIconElement>(jobEntryTestElement, '#fileIcon')?.icon);
   });
 
   test('ensureGenericFileIconIsShown', () => {
+    jobEntryTestElement = initPrintJobEntryElement();
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', /*fileName=*/ '.test',
         /*date=*/ convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 1,
             /*printerError=*/ ActivePrintJobState.kStarted));
@@ -1180,63 +1095,64 @@
 
     assertEquals(
         'print-management:file-generic',
-        querySelector(jobEntryTestElement, '#fileIcon').icon);
+        querySelector<IronIconElement>(jobEntryTestElement, '#fileIcon')?.icon);
   });
 
   test('ensureFileIconClassMatchesFileIcon', () => {
+    jobEntryTestElement = initPrintJobEntryElement();
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', /*fileName=*/ '.test',
         /*date=*/ convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 1,
             /*printerError=*/ ActivePrintJobState.kStarted));
     flush();
     assertEquals(
-        jobEntryTestElement.fileIconClass_, 'flex-center file-icon-gray');
+        jobEntryTestElement.getFileIconClass(), 'flex-center file-icon-gray');
 
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', /*fileName=*/ '.doc',
         /*date=*/ convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 1,
             /*printerError=*/ ActivePrintJobState.kStarted));
     flush();
     assertEquals(
-        jobEntryTestElement.fileIconClass_, 'flex-center file-icon-blue');
+        jobEntryTestElement.getFileIconClass(), 'flex-center file-icon-blue');
 
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', /*fileName=*/ ' - Google Drawings',
         /*date=*/ convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 1,
             /*printerError=*/ ActivePrintJobState.kStarted));
     flush();
     assertEquals(
-        jobEntryTestElement.fileIconClass_, 'flex-center file-icon-red');
+        jobEntryTestElement.getFileIconClass(), 'flex-center file-icon-red');
 
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', /*fileName=*/ '.xlsx',
         /*date=*/ convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 1,
             /*printerError=*/ ActivePrintJobState.kStarted));
     flush();
     assertEquals(
-        jobEntryTestElement.fileIconClass_, 'flex-center file-icon-green');
+        jobEntryTestElement.getFileIconClass(), 'flex-center file-icon-green');
 
     jobEntryTestElement.jobEntry = createJobEntry(
         /*id=*/ '1', /*fileName=*/ ' - Google Slides',
         /*date=*/ convertToMojoTime(new Date('February 5, 2020 03:24:00')),
-        PrinterErrorCode.kNoError, /*completedInfo=*/ null,
+        PrinterErrorCode.kNoError, /*completedInfo=*/ undefined,
         createOngoingPrintJobInfo(
             /*printedPages=*/ 1,
             /*printerError=*/ ActivePrintJobState.kStarted));
     flush();
     assertEquals(
-        jobEntryTestElement.fileIconClass_, 'flex-center file-icon-yellow');
+        jobEntryTestElement.getFileIconClass(), 'flex-center file-icon-yellow');
   });
 });
diff --git a/chrome/test/data/webui/chromeos/print_management/tsconfig_base.json b/chrome/test/data/webui/chromeos/print_management/tsconfig_base.json
new file mode 100644
index 0000000..edffdeca
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/print_management/tsconfig_base.json
@@ -0,0 +1,10 @@
+{
+  "extends": "../../../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "allowJs": true,
+    "typeRoots": [
+       "../../../../../../third_party/node/node_modules/@types"
+    ],
+    "types": ["mocha"]
+  }
+}
\ No newline at end of file
diff --git a/chrome/test/data/webui/settings/chromeos/fake_quick_unlock_uma.js b/chrome/test/data/webui/settings/chromeos/fake_quick_unlock_uma.js
index e8026de..4ca5fd8 100644
--- a/chrome/test/data/webui/settings/chromeos/fake_quick_unlock_uma.js
+++ b/chrome/test/data/webui/settings/chromeos/fake_quick_unlock_uma.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {LockScreenProgress} from 'chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js';
+import {LockScreenProgress} from 'chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js';
 
 /**
  * @fileoverview Fake implementation of chrome histogram recording for testing.
diff --git a/chrome/test/data/webui/settings/chromeos/quick_unlock_authenticate_browsertest_chromeos.js b/chrome/test/data/webui/settings/chromeos/quick_unlock_authenticate_browsertest_chromeos.js
index d3c945b8..f6d7c1a 100644
--- a/chrome/test/data/webui/settings/chromeos/quick_unlock_authenticate_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/chromeos/quick_unlock_authenticate_browsertest_chromeos.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {CrSettingsPrefs, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
-import {LockScreenProgress} from 'chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js';
+import {LockScreenProgress} from 'chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
index 824b5bc..474093b 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
@@ -159,7 +159,6 @@
     expirationMonth: Math.ceil(Math.random() * 11).toString(),
     expirationYear: (2016 + Math.floor(Math.random() * 5)).toString(),
     network: `${card}_network`,
-    imageSrc: 'chrome://theme/IDR_AUTOFILL_CC_GENERIC',
     metadata: {
       isLocal: true,
       summaryLabel: card + ' ' +
diff --git a/chrome/test/data/webui/settings/payments_section_test.ts b/chrome/test/data/webui/settings/payments_section_test.ts
index 0666ee24..b73ddce5 100644
--- a/chrome/test/data/webui/settings/payments_section_test.ts
+++ b/chrome/test/data/webui/settings/payments_section_test.ts
@@ -8,7 +8,7 @@
 import {PaymentsManagerImpl, SettingsCreditCardEditDialogElement, SettingsPaymentsSectionElement, SettingsVirtualCardUnenrollDialogElement} from 'chrome://settings/lazy_load.js';
 import {MetricsBrowserProxyImpl, PrivacyElementInteractions, SettingsToggleButtonElement} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, whenAttributeIs, isVisible} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, whenAttributeIs} from 'chrome://webui-test/test_util.js';
 
 import {createCreditCardEntry, createEmptyCreditCardEntry, PaymentsManagerExpectations,TestPaymentsManager} from './passwords_and_autofill_fake_data.js';
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
@@ -417,21 +417,6 @@
                     .querySelector<HTMLElement>('#paymentsIndicator')!.hidden);
   });
 
-  test('verifyCardImage', function() {
-    loadTimeData.overrideValues({
-      virtualCardMetadataEnabled: true,
-    });
-    const creditCard = createCreditCardEntry();
-    const section =
-        createPaymentsSection([creditCard], /*upiIds=*/[], /*prefValues=*/ {});
-
-    const creditCardList = section.$.paymentsList;
-    assertTrue(!!creditCardList);
-    assertEquals(1, getLocalAndServerCreditCardListItems().length);
-    assertTrue(isVisible(getCardRowShadowRoot(section.$.paymentsList)
-                             .querySelector('#cardImage')));
-  });
-
   test('verifyAddVsEditCreditCardTitle', function() {
     const newCreditCard = createEmptyCreditCardEntry();
     const newCreditCardDialog = createCreditCardDialog(newCreditCard);
diff --git a/chrome/updater/run_all_unittests.cc b/chrome/updater/run_all_unittests.cc
index 938bf10..766c4f0d 100644
--- a/chrome/updater/run_all_unittests.cc
+++ b/chrome/updater/run_all_unittests.cc
@@ -56,16 +56,6 @@
   ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_NORMAL);
 }
 
-void MaybeIncreaseTestTimeouts(int argc, char** argv) {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (!command_line->HasSwitch(switches::kTestLauncherTimeout)) {
-    command_line->AppendSwitchASCII(switches::kTestLauncherTimeout, "60000");
-  }
-  if (!command_line->HasSwitch(switches::kUiTestActionTimeout)) {
-    command_line->AppendSwitchASCII(switches::kUiTestActionTimeout, "30000");
-  }
-}
-
 // Sets the _NT_ALT_SYMBOL_PATH for the system or the user, if it is not set
 // already. Resets it on destruction. The environment variable is set in the
 // corresponding registry hive. _NT_ALT_SYMBOL_PATH is used to avoid symbol
@@ -139,11 +129,29 @@
 
 #endif  // BUILDFLAG(IS_WIN)
 
+namespace {
+
+void MaybeIncreaseTestTimeouts(int argc, char** argv) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(switches::kTestLauncherTimeout)) {
+    command_line->AppendSwitchASCII(switches::kTestLauncherTimeout, "60000");
+  }
+  if (!command_line->HasSwitch(switches::kUiTestActionTimeout)) {
+    command_line->AppendSwitchASCII(switches::kUiTestActionTimeout, "30000");
+  }
+}
+
+}  // namespace
+
 int main(int argc, char** argv) {
   base::CommandLine::Init(argc, argv);
   base::ScopedClosureRunner reset_command_line(
       base::BindOnce(&base::CommandLine::Reset));
 
+  // Change the test timeout defaults if the command line arguments to override
+  // them are not present.
+  MaybeIncreaseTestTimeouts(argc, argv);
+
 #if BUILDFLAG(IS_WIN)
   std::cerr << "Process priority: " << base::Process::Current().GetPriority()
             << std::endl;
@@ -155,10 +163,6 @@
   // swarming task runs with a priority below normal.
   FixExecutionPriorities();
 
-  // Change the test timeout defaults if the command line arguments to override
-  // them are not present.
-  MaybeIncreaseTestTimeouts(argc, argv);
-
   auto scoped_com_initializer =
       std::make_unique<base::win::ScopedCOMInitializer>(
           base::win::ScopedCOMInitializer::kMTA);
diff --git a/chrome/updater/win/installer/exit_code.h b/chrome/updater/win/installer/exit_code.h
index 3f3378e..1afe8f84 100644
--- a/chrome/updater/win/installer/exit_code.h
+++ b/chrome/updater/win/installer/exit_code.h
@@ -22,6 +22,7 @@
   RUN_SETUP_FAILED_FILE_NOT_FOUND = 122,            // ERROR_FILE_NOT_FOUND.
   RUN_SETUP_FAILED_PATH_NOT_FOUND = 123,            // ERROR_PATH_NOT_FOUND.
   RUN_SETUP_FAILED_COULD_NOT_CREATE_PROCESS = 124,  // All other errors.
+  UNSUPPORTED_WINDOWS_VERSION = 125,
 };
 
 }  // namespace updater
diff --git a/chrome/updater/win/installer/installer.cc b/chrome/updater/win/installer/installer.cc
index 6054c03..3a43589 100644
--- a/chrome/updater/win/installer/installer.cc
+++ b/chrome/updater/win/installer/installer.cc
@@ -29,6 +29,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
+#include "base/win/windows_version.h"
 #include "chrome/installer/util/lzma_util.h"
 #include "chrome/installer/util/util_constants.h"
 #include "chrome/updater/constants.h"
@@ -288,6 +289,10 @@
   CHECK(EnableSecureDllLoading());
   EnableProcessHeapMetadataProtection();
 
+  if (base::win::GetVersion() < base::win::Version::WIN7) {
+    return ProcessExitResult(UNSUPPORTED_WINDOWS_VERSION);
+  }
+
   CommandString cmd_line_args;
   ProcessExitResult args_result = BuildCommandLineArguments(
       ::GetCommandLineW(), cmd_line_args.get(), cmd_line_args.capacity());
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 82a6d4a..21561e37 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-15064.0.0
\ No newline at end of file
+15081.0.0
\ No newline at end of file
diff --git a/chromeos/dbus/power/BUILD.gn b/chromeos/dbus/power/BUILD.gn
index 69459e0..1b84f59 100644
--- a/chromeos/dbus/power/BUILD.gn
+++ b/chromeos/dbus/power/BUILD.gn
@@ -60,6 +60,7 @@
     "//third_party/cros_system_api/dbus/power_manager/suspend.proto",
     "//third_party/cros_system_api/dbus/power_manager/switch_states.proto",
     "//third_party/cros_system_api/dbus/power_manager/thermal.proto",
+    "//third_party/cros_system_api/dbus/power_manager/user_charging_event.proto",
   ]
 
   proto_out_dir = "chromeos/dbus/power_manager"
diff --git a/components/desks_storage/core/desk_model_wrapper.cc b/components/desks_storage/core/desk_model_wrapper.cc
index 87aa5ae..86563ba 100644
--- a/components/desks_storage/core/desk_model_wrapper.cc
+++ b/components/desks_storage/core/desk_model_wrapper.cc
@@ -90,7 +90,7 @@
 void DeskModelWrapper::DeleteAllEntries(
     DeskModel::DeleteEntryCallback callback) {
   DeskModel::DeleteEntryStatus desk_template_delete_status =
-      GetDeskTemplateModel()->DeleteAllEntries();
+      GetDeskTemplateModel()->DeleteAllEntriesSync();
   if (desk_template_delete_status != DeskModel::DeleteEntryStatus::kOk) {
     std::move(callback).Run(desk_template_delete_status);
     return;
diff --git a/components/desks_storage/core/desk_sync_bridge.cc b/components/desks_storage/core/desk_sync_bridge.cc
index dc3700e6..e4801b4 100644
--- a/components/desks_storage/core/desk_sync_bridge.cc
+++ b/components/desks_storage/core/desk_sync_bridge.cc
@@ -1180,11 +1180,11 @@
 }
 
 void DeskSyncBridge::DeleteAllEntries(DeleteEntryCallback callback) {
-  DeleteEntryStatus status = DeleteAllEntries();
+  DeleteEntryStatus status = DeleteAllEntriesSync();
   std::move(callback).Run(status);
 }
 
-DeskModel::DeleteEntryStatus DeskSyncBridge::DeleteAllEntries() {
+DeskModel::DeleteEntryStatus DeskSyncBridge::DeleteAllEntriesSync() {
   if (!IsReady()) {
     // This sync bridge has not finished initializing.
     // Cannot delete anything.
diff --git a/components/desks_storage/core/desk_sync_bridge.h b/components/desks_storage/core/desk_sync_bridge.h
index e83f2ee..78fd8f5 100644
--- a/components/desks_storage/core/desk_sync_bridge.h
+++ b/components/desks_storage/core/desk_sync_bridge.h
@@ -102,12 +102,14 @@
 
   const ash::DeskTemplate* GetUserEntryByUUID(const base::GUID& uuid) const;
 
-  DeskModel::DeleteEntryStatus DeleteAllEntries();
-
  private:
+  friend class DeskModelWrapper;
+
   using DeskEntries =
       base::flat_map<base::GUID, std::unique_ptr<ash::DeskTemplate>>;
 
+  DeskModel::DeleteEntryStatus DeleteAllEntriesSync();
+
   // Notify all observers that the model is loaded;
   void NotifyDeskModelLoaded();
 
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc
index 4cde5cc..85b64a9 100644
--- a/components/guest_view/browser/guest_view_base.cc
+++ b/components/guest_view/browser/guest_view_base.cc
@@ -158,8 +158,7 @@
 GuestViewBase::GuestViewBase(WebContents* owner_web_contents)
     : owner_web_contents_(owner_web_contents),
       browser_context_(owner_web_contents->GetBrowserContext()),
-      guest_instance_id_(GetGuestViewManager()->GetNextInstanceID()),
-      guest_host_(nullptr) {
+      guest_instance_id_(GetGuestViewManager()->GetNextInstanceID()) {
   SetOwnerHost();
 }
 
@@ -413,10 +412,6 @@
   // may wish to access their openers.
   weak_ptr_factory_.InvalidateWeakPtrs();
 
-  // Give the content module an opportunity to perform some cleanup.
-  guest_host_->WillDestroy();
-  guest_host_ = nullptr;
-
   g_webcontents_guestview_map.Get().erase(web_contents());
   GetGuestViewManager()->RemoveGuest(guest_instance_id_);
   pending_events_.clear();
@@ -439,10 +434,6 @@
   }
 }
 
-void GuestViewBase::SetGuestHost(content::GuestHost* guest_host) {
-  guest_host_ = guest_host;
-}
-
 void GuestViewBase::WillAttach(
     WebContents* embedder_web_contents,
     content::RenderFrameHost* outer_contents_frame,
diff --git a/components/guest_view/browser/guest_view_base.h b/components/guest_view/browser/guest_view_base.h
index d38037c4..461cf4a 100644
--- a/components/guest_view/browser/guest_view_base.h
+++ b/components/guest_view/browser/guest_view_base.h
@@ -15,7 +15,6 @@
 #include "components/guest_view/common/guest_view_constants.h"
 #include "components/zoom/zoom_observer.h"
 #include "content/public/browser/browser_plugin_guest_delegate.h"
-#include "content/public/browser/guest_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -333,7 +332,6 @@
   std::unique_ptr<content::WebContents> CreateNewGuestWindow(
       const content::WebContents::CreateParams& create_params) final;
   content::WebContents* GetOwnerWebContents() final;
-  void SetGuestHost(content::GuestHost* guest_host) final;
 
   // WebContentsDelegate implementation.
   void ActivateContents(content::WebContents* contents) final;
@@ -453,9 +451,6 @@
   // element may not match the size of the guest.
   gfx::Size guest_size_;
 
-  // A pointer to the guest_host.
-  raw_ptr<content::GuestHost> guest_host_;
-
   // Indicates whether autosize mode is enabled or not.
   bool auto_size_enabled_ = false;
 
diff --git a/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc b/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc
index 8e508b7..18b49e4 100644
--- a/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc
+++ b/components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.cc
@@ -152,6 +152,13 @@
     return FORWARD_OBSERVING;
   }
 
+  ObservePolicy OnPrerenderStart(content::NavigationHandle* navigation_handle,
+                                 const GURL& currently_committed_url) override {
+    const bool should_ignore = navigation_handle->GetURL().spec().find(
+                                   "ignore-on-start") != std::string::npos;
+    return should_ignore ? STOP_OBSERVING : CONTINUE_OBSERVING;
+  }
+
   ObservePolicy OnCommit(content::NavigationHandle* handle) override {
     const bool should_ignore =
         handle->GetURL().spec().find("ignore-on-commit") != std::string::npos;
diff --git a/components/send_tab_to_self/BUILD.gn b/components/send_tab_to_self/BUILD.gn
index 7dbf9d3..baa285b 100644
--- a/components/send_tab_to_self/BUILD.gn
+++ b/components/send_tab_to_self/BUILD.gn
@@ -61,7 +61,10 @@
 if (is_android) {
   java_cpp_enum("java_enum_srcjar") {
     visibility = [ ":*" ]
-    sources = [ "entry_point_display_reason.h" ]
+    sources = [
+      "entry_point_display_reason.h",
+      "metrics_util.h",
+    ]
   }
 
   android_library("send_tab_to_self_java") {
diff --git a/components/send_tab_to_self/metrics_util.cc b/components/send_tab_to_self/metrics_util.cc
index a6d81b4..f08cd34 100644
--- a/components/send_tab_to_self/metrics_util.cc
+++ b/components/send_tab_to_self/metrics_util.cc
@@ -11,16 +11,6 @@
 
 namespace {
 
-// State of the send tab to self option in the UI.
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class ClickResult {
-  // kShowItem = 0,
-  kClickItem = 1,
-  // kShowDeviceList = 2,
-  kMaxValue = kClickItem,
-};
-
 // Status of received STTS notifications.
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
@@ -30,7 +20,7 @@
   kDismissed = 1,
   kOpened = 2,
   kTimedOut = 3,
-  kSent = 4,
+  // kSent = 4,
   kDismissReasonUnknown = 5,
   kThrottled = 6,
   kMaxValue = kThrottled,
@@ -57,14 +47,11 @@
 
 }  // namespace
 
-void RecordDeviceClicked(ShareEntryPoint entry_point) {
-  // TODO(crbug.com/956722): Only kClickItem is used today, so we should replace
-  // this with a histogram which doesn't use the ClickResult enum.
+void RecordSendingEvent(ShareEntryPoint entry_point, SendingEvent event) {
   base::UmaHistogramEnumeration(
       base::StrCat({"SendTabToSelf.", GetEntryPointHistogramString(entry_point),
                     ".ClickResult"}),
-      ClickResult::kClickItem);
-  RecordNotificationSent();
+      event);
 }
 
 void RecordNotificationShown() {
@@ -87,11 +74,6 @@
                                 NotificationStatus::kTimedOut);
 }
 
-void RecordNotificationSent() {
-  base::UmaHistogramEnumeration("Sharing.SendTabToSelf.NotificationStatus",
-                                NotificationStatus::kSent);
-}
-
 void RecordNotificationDismissReasonUnknown() {
   base::UmaHistogramEnumeration("Sharing.SendTabToSelf.NotificationStatus",
                                 NotificationStatus::kDismissReasonUnknown);
diff --git a/components/send_tab_to_self/metrics_util.h b/components/send_tab_to_self/metrics_util.h
index 3043a31..911db208 100644
--- a/components/send_tab_to_self/metrics_util.h
+++ b/components/send_tab_to_self/metrics_util.h
@@ -17,11 +17,23 @@
   kTabMenu,
 };
 
-// Records whether the user clicked to send a tab to a device.
-void RecordDeviceClicked(ShareEntryPoint entry_point);
+// State of the send tab to self option in the UI.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// For historical reasons, this maps to SendTabToSelfClickResult in enums.xml.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: (
+//   org.chromium.chrome.browser.share.send_tab_to_self)
+enum class SendingEvent {
+  // kShowItem = 0,
+  kClickItem = 1,
+  kShowDeviceList = 2,
+  kShowNoTargetDeviceMessage = 3,
+  kShowSigninPromo = 4,
+  kMaxValue = kShowSigninPromo,
+};
 
-// Records when a tab is sent to a device.
-void RecordNotificationSent();
+void RecordSendingEvent(ShareEntryPoint entry_point, SendingEvent event);
 
 // Records when a received STTS notification is shown.
 void RecordNotificationShown();
diff --git a/components/services/app_service/public/cpp/BUILD.gn b/components/services/app_service/public/cpp/BUILD.gn
index 8e7f68a..1ca40333 100644
--- a/components/services/app_service/public/cpp/BUILD.gn
+++ b/components/services/app_service/public/cpp/BUILD.gn
@@ -51,6 +51,8 @@
     "app_launch_util.h",
     "app_types.cc",
     "app_types.h",
+    "capability_access.cc",
+    "capability_access.h",
     "features.cc",
     "features.h",
     "intent_filter.cc",
diff --git a/components/services/app_service/public/cpp/capability_access.cc b/components/services/app_service/public/cpp/capability_access.cc
new file mode 100644
index 0000000..0e67718e
--- /dev/null
+++ b/components/services/app_service/public/cpp/capability_access.cc
@@ -0,0 +1,22 @@
+// 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/services/app_service/public/cpp/capability_access.h"
+
+namespace apps {
+
+CapabilityAccess::CapabilityAccess(const std::string& app_id)
+    : app_id(app_id) {}
+
+CapabilityAccess::~CapabilityAccess() = default;
+
+CapabilityAccessPtr CapabilityAccess::Clone() const {
+  auto capability_access = std::make_unique<CapabilityAccess>(app_id);
+
+  capability_access->camera = camera;
+  capability_access->microphone = microphone;
+  return capability_access;
+}
+
+}  // namespace apps
diff --git a/components/services/app_service/public/cpp/capability_access.h b/components/services/app_service/public/cpp/capability_access.h
new file mode 100644
index 0000000..66f6ddbc
--- /dev/null
+++ b/components/services/app_service/public/cpp/capability_access.h
@@ -0,0 +1,44 @@
+// 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_SERVICES_APP_SERVICE_PUBLIC_CPP_CAPABILITY_ACCESS_H_
+#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_CAPABILITY_ACCESS_H_
+
+#include <string>
+#include <utility>
+
+#include "base/component_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace apps {
+
+// Information about whether an app is accessing some capability, e.g. camera,
+// microphone.
+struct COMPONENT_EXPORT(APP_TYPES) CapabilityAccess {
+  explicit CapabilityAccess(const std::string& app_id);
+
+  CapabilityAccess(const CapabilityAccess&) = delete;
+  CapabilityAccess& operator=(const CapabilityAccess&) = delete;
+
+  ~CapabilityAccess();
+
+  std::unique_ptr<CapabilityAccess> Clone() const;
+
+  std::string app_id;
+
+  // Whether the app is accessing camera.
+  absl::optional<bool> camera;
+
+  // Whether the app is accessing microphone.
+  absl::optional<bool> microphone;
+
+  // When adding new fields, also update the Merge method and other helpers in
+  // components/services/app_service/public/cpp/CapabilityAccessUpdate.*
+};
+
+using CapabilityAccessPtr = std::unique_ptr<CapabilityAccess>;
+
+}  // namespace apps
+
+#endif  // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_CAPABILITY_ACCESS_H_
diff --git a/components/services/app_service/public/cpp/capability_access_update.cc b/components/services/app_service/public/cpp/capability_access_update.cc
index 3f9aa3a..7e9dd81 100644
--- a/components/services/app_service/public/cpp/capability_access_update.cc
+++ b/components/services/app_service/public/cpp/capability_access_update.cc
@@ -33,10 +33,45 @@
   // should also be updated.
 }
 
+// static
+void CapabilityAccessUpdate::Merge(CapabilityAccess* state,
+                                   const CapabilityAccess* delta) {
+  DCHECK(state);
+  if (!delta) {
+    return;
+  }
+
+  if (delta->app_id != state->app_id) {
+    LOG(ERROR) << "inconsistent (app_id): (" << delta->app_id << ") vs ("
+               << state->app_id << ") ";
+    DCHECK(false);
+    return;
+  }
+
+  if (delta->camera.has_value()) {
+    state->camera = delta->camera;
+  }
+  if (delta->microphone.has_value()) {
+    state->microphone = delta->microphone;
+  }
+  // When adding new fields to the CapabilityAccess Mojo type, this function
+  // should also be updated.
+}
+
 CapabilityAccessUpdate::CapabilityAccessUpdate(
     const apps::mojom::CapabilityAccess* state,
     const apps::mojom::CapabilityAccess* delta,
     const ::AccountId& account_id)
+    : mojom_state_(state), mojom_delta_(delta), account_id_(account_id) {
+  DCHECK(mojom_state_ || mojom_delta_);
+  if (mojom_state_ && mojom_delta_) {
+    DCHECK(mojom_state_->app_id == delta->app_id);
+  }
+}
+
+CapabilityAccessUpdate::CapabilityAccessUpdate(const CapabilityAccess* state,
+                                               const CapabilityAccess* delta,
+                                               const ::AccountId& account_id)
     : state_(state), delta_(delta), account_id_(account_id) {
   DCHECK(state_ || delta_);
   if (state_ && delta_) {
@@ -45,42 +80,46 @@
 }
 
 bool CapabilityAccessUpdate::StateIsNull() const {
-  return state_ == nullptr;
+  return mojom_state_ == nullptr;
 }
 
 const std::string& CapabilityAccessUpdate::AppId() const {
-  return delta_ ? delta_->app_id : state_->app_id;
+  return mojom_delta_ ? mojom_delta_->app_id : mojom_state_->app_id;
 }
 
 apps::mojom::OptionalBool CapabilityAccessUpdate::Camera() const {
-  if (delta_ && (delta_->camera != apps::mojom::OptionalBool::kUnknown)) {
-    return delta_->camera;
+  if (mojom_delta_ &&
+      (mojom_delta_->camera != apps::mojom::OptionalBool::kUnknown)) {
+    return mojom_delta_->camera;
   }
-  if (state_) {
-    return state_->camera;
+  if (mojom_state_) {
+    return mojom_state_->camera;
   }
   return apps::mojom::OptionalBool::kUnknown;
 }
 
 bool CapabilityAccessUpdate::CameraChanged() const {
-  return delta_ && (delta_->camera != apps::mojom::OptionalBool::kUnknown) &&
-         (!state_ || (delta_->camera != state_->camera));
+  return mojom_delta_ &&
+         (mojom_delta_->camera != apps::mojom::OptionalBool::kUnknown) &&
+         (!mojom_state_ || (mojom_delta_->camera != mojom_state_->camera));
 }
 
 apps::mojom::OptionalBool CapabilityAccessUpdate::Microphone() const {
-  if (delta_ && (delta_->microphone != apps::mojom::OptionalBool::kUnknown)) {
-    return delta_->microphone;
+  if (mojom_delta_ &&
+      (mojom_delta_->microphone != apps::mojom::OptionalBool::kUnknown)) {
+    return mojom_delta_->microphone;
   }
-  if (state_) {
-    return state_->microphone;
+  if (mojom_state_) {
+    return mojom_state_->microphone;
   }
   return apps::mojom::OptionalBool::kUnknown;
 }
 
 bool CapabilityAccessUpdate::MicrophoneChanged() const {
-  return delta_ &&
-         (delta_->microphone != apps::mojom::OptionalBool::kUnknown) &&
-         (!state_ || (delta_->microphone != state_->microphone));
+  return mojom_delta_ &&
+         (mojom_delta_->microphone != apps::mojom::OptionalBool::kUnknown) &&
+         (!mojom_state_ ||
+          (mojom_delta_->microphone != mojom_state_->microphone));
 }
 
 const ::AccountId& CapabilityAccessUpdate::AccountId() const {
diff --git a/components/services/app_service/public/cpp/capability_access_update.h b/components/services/app_service/public/cpp/capability_access_update.h
index 6675b1e1..8ef067bf 100644
--- a/components/services/app_service/public/cpp/capability_access_update.h
+++ b/components/services/app_service/public/cpp/capability_access_update.h
@@ -10,6 +10,7 @@
 #include "base/component_export.h"
 #include "base/memory/raw_ptr.h"
 #include "components/account_id/account_id.h"
+#include "components/services/app_service/public/cpp/capability_access.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 
 namespace apps {
@@ -35,17 +36,25 @@
 // CapabilityAccessUpdate.
 //
 // See components/services/app_service/README.md for more details.
+//
+// TODO(crbug.com/1253250): Remove all mojom related code.
+// 1. Modify comments.
+// 2. Replace mojom related functions with non-mojom functions.
 class COMPONENT_EXPORT(APP_UPDATE) CapabilityAccessUpdate {
  public:
   // Modifies |state| by copying over all of |delta|'s known fields: those
   // fields whose values aren't "unknown". The |state| may not be nullptr.
   static void Merge(apps::mojom::CapabilityAccess* state,
                     const apps::mojom::CapabilityAccess* delta);
+  static void Merge(CapabilityAccess* state, const CapabilityAccess* delta);
 
   // At most one of |state| or |delta| may be nullptr.
   CapabilityAccessUpdate(const apps::mojom::CapabilityAccess* state,
                          const apps::mojom::CapabilityAccess* delta,
                          const AccountId& account_id);
+  CapabilityAccessUpdate(const CapabilityAccess* state,
+                         const CapabilityAccess* delta,
+                         const AccountId& account_id);
 
   CapabilityAccessUpdate(const CapabilityAccessUpdate&) = delete;
   CapabilityAccessUpdate& operator=(const CapabilityAccessUpdate&) = delete;
@@ -65,8 +74,11 @@
   const ::AccountId& AccountId() const;
 
  private:
-  raw_ptr<const apps::mojom::CapabilityAccess> state_;
-  raw_ptr<const apps::mojom::CapabilityAccess> delta_;
+  raw_ptr<const apps::mojom::CapabilityAccess> mojom_state_;
+  raw_ptr<const apps::mojom::CapabilityAccess> mojom_delta_;
+
+  raw_ptr<const CapabilityAccess> state_;
+  raw_ptr<const CapabilityAccess> delta_;
 
   const ::AccountId& account_id_;
 };
diff --git a/components/translate/content/android/translate_message.cc b/components/translate/content/android/translate_message.cc
index 9b29f368..9d951d5 100644
--- a/components/translate/content/android/translate_message.cc
+++ b/components/translate/content/android/translate_message.cc
@@ -82,7 +82,9 @@
   }
 
   void ClearNativePointer(JNIEnv* env) override {
-    Java_TranslateMessage_clearNativePointer(env, java_translate_message_);
+    if (java_translate_message_)
+      Java_TranslateMessage_clearNativePointer(env, java_translate_message_);
+    java_translate_message_ = nullptr;
   }
 
   void Dismiss(JNIEnv* env) override {
@@ -155,7 +157,6 @@
   JNIEnv* env = base::android::AttachCurrentThread();
   if (state_ != State::kDismissed)
     bridge_->Dismiss(env);
-  bridge_->ClearNativePointer(env);
 }
 
 void TranslateMessage::ShowTranslateStep(TranslateStep step,
@@ -165,6 +166,11 @@
   JNIEnv* env = base::android::AttachCurrentThread();
 
   if (!ui_delegate_) {
+    ui_delegate_ = std::make_unique<TranslateUIDelegate>(
+        translate_manager_, source_language, target_language);
+  }
+
+  if (state_ == State::kDismissed) {
     if (!bridge_->CreateTranslateMessage(env, web_contents_, this,
                                          GetDismissalDurationSeconds())) {
       // The |bridge_| failed to create the Java TranslateMessage, such as when
@@ -172,12 +178,8 @@
       return;
     }
 
-    ui_delegate_ = std::make_unique<TranslateUIDelegate>(
-        translate_manager_, source_language, target_language);
-  }
-
-  if (state_ == State::kDismissed)
     RecordCompactInfobarEvent(InfobarEvent::INFOBAR_IMPRESSION);
+  }
 
   if (ui_delegate_->GetSourceLanguageCode() != source_language)
     ui_delegate_->UpdateSourceLanguage(source_language);
@@ -410,6 +412,7 @@
     }
   }
 
+  bridge_->ClearNativePointer(env);
   state_ = State::kDismissed;
 
   // The only time |on_dismiss_callback_| will be null is during the destruction
diff --git a/components/translate/content/android/translate_message_unittest.cc b/components/translate/content/android/translate_message_unittest.cc
index 4b4f15e56..b3f0ae35 100644
--- a/components/translate/content/android/translate_message_unittest.cc
+++ b/components/translate/content/android/translate_message_unittest.cc
@@ -257,6 +257,7 @@
     translate_message_->HandlePrimaryAction(env);
 
     // Simulate a dismissal triggered from the Java side.
+    EXPECT_CALL(*bridge_, ClearNativePointer(env));
     int prev_on_dismiss_callback_called_count =
         on_dismiss_callback_called_count_;
     translate_message_->HandleDismiss(
@@ -403,6 +404,7 @@
   }
 
   // Simulate a dismissal triggered by the Java side.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::TIMER));
   EXPECT_EQ(1, on_dismiss_callback_called_count_);
@@ -413,8 +415,11 @@
   EXPECT_CALL(*bridge_, CreateTranslateMessage(
                             env, _, _, kDefaultDismissalDurationSeconds))
       .WillOnce(Return(true));
-
   TranslateThenRevertThenDismiss(env, "fr", "en");
+
+  EXPECT_CALL(*bridge_, CreateTranslateMessage(
+                            env, _, _, kDefaultDismissalDurationSeconds))
+      .WillOnce(Return(true));
   TranslateThenRevertThenDismiss(env, "de", "es");
 }
 
@@ -476,7 +481,6 @@
             env,
             static_cast<jint>(messages::DismissReason::DISMISSED_BY_FEATURE));
       }));
-
   EXPECT_CALL(*bridge_, ClearNativePointer(env));
 
   translate_message_.reset();
@@ -1118,6 +1122,26 @@
   TranslateThenRevertThenDismiss(env, "fr", "en");
 }
 
+TEST_F(TranslateMessageTest, CreateTranslateMessageSucceedsThenFails) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  // The first call to CreateTranslateMessage will succeed.
+  EXPECT_CALL(*bridge_, CreateTranslateMessage(
+                            env, _, _, kDefaultDismissalDurationSeconds))
+      .WillOnce(Return(true));
+  TranslateThenRevertThenDismiss(env, "fr", "en");
+
+  // The second call to CreateTranslateMessage will fail.
+  EXPECT_CALL(*bridge_, CreateTranslateMessage(
+                            env, _, _, kDefaultDismissalDurationSeconds))
+      .WillOnce(Return(false));
+
+  // ShowMessage should not be called after CreateTranslateMessage fails.
+  EXPECT_CALL(*bridge_, ShowMessage(_, _, _, _, _)).Times(0);
+  translate_message_->ShowTranslateStep(TRANSLATE_STEP_BEFORE_TRANSLATE, "fr",
+                                        "en");
+}
+
 TEST_F(TranslateMessageTest, TranslationDismissedInProgressByTimer) {
   JNIEnv* env = base::android::AttachCurrentThread();
 
@@ -1143,6 +1167,7 @@
   EXPECT_EQ(0, translate_prefs_->GetTranslationIgnoredCount("fr"));
 
   // Dismiss the translate message while translation is still in-progress.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::TIMER));
   EXPECT_EQ(1, on_dismiss_callback_called_count_);
@@ -1177,6 +1202,7 @@
   EXPECT_EQ(0, translate_prefs_->GetTranslationIgnoredCount("fr"));
 
   // Dismiss the translate message while translation is still in-progress.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::GESTURE));
   EXPECT_EQ(1, on_dismiss_callback_called_count_);
@@ -1204,6 +1230,8 @@
   ShowBeforeTranslationMessage(env, "fr", "en");
 
   base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::TIMER));
 
@@ -1220,17 +1248,18 @@
 TEST_F(TranslateMessageTest, TranslationNotIgnoredBecauseOverflowMenuOpened) {
   JNIEnv* env = base::android::AttachCurrentThread();
 
+  // Show the translate message and simulate the overflow menu being opened.
   EXPECT_CALL(*bridge_, CreateTranslateMessage(
                             env, _, _, kDefaultDismissalDurationSeconds))
       .WillOnce(Return(true));
-
-  // Show the translate message and simulate the overflow menu being opened.
   ShowBeforeTranslationMessage(env, "fr", "en");
+
   EXPECT_CALL(*bridge_, ConstructMenuItemArray(env, _, _, _, _, _))
       .WillOnce(Return(nullptr));
   translate_message_->BuildOverflowMenu(env);
 
   // Dismiss the translate message.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::TIMER));
   EXPECT_EQ(1, on_dismiss_callback_called_count_);
@@ -1240,9 +1269,13 @@
   EXPECT_EQ(0, translate_prefs_->GetTranslationIgnoredCount("fr"));
 
   // Show the translate message again, this time without interacting with it.
+  EXPECT_CALL(*bridge_, CreateTranslateMessage(
+                            env, _, _, kDefaultDismissalDurationSeconds))
+      .WillOnce(Return(true));
   ShowBeforeTranslationMessage(env, "fr", "en");
 
   // Dismiss the translate message.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::TIMER));
   EXPECT_EQ(2, on_dismiss_callback_called_count_);
@@ -1274,6 +1307,7 @@
                                         "en");
 
   // Dismiss the message.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::TIMER));
   EXPECT_EQ(1, on_dismiss_callback_called_count_);
@@ -1299,6 +1333,8 @@
   ShowBeforeTranslationMessage(env, "fr", "en");
 
   base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::GESTURE));
 
@@ -1314,17 +1350,18 @@
 TEST_F(TranslateMessageTest, TranslationNotDeniedBecauseOverflowMenuOpened) {
   JNIEnv* env = base::android::AttachCurrentThread();
 
+  // Show the translate message and simulate the overflow menu being opened.
   EXPECT_CALL(*bridge_, CreateTranslateMessage(
                             env, _, _, kDefaultDismissalDurationSeconds))
       .WillOnce(Return(true));
-
-  // Show the translate message and simulate the overflow menu being opened.
   ShowBeforeTranslationMessage(env, "fr", "en");
+
   EXPECT_CALL(*bridge_, ConstructMenuItemArray(env, _, _, _, _, _))
       .WillOnce(Return(nullptr));
   translate_message_->BuildOverflowMenu(env);
 
   // Dismiss the translate message.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::GESTURE));
   EXPECT_EQ(1, on_dismiss_callback_called_count_);
@@ -1334,9 +1371,13 @@
   EXPECT_EQ(0, translate_prefs_->GetTranslationDeniedCount("fr"));
 
   // Show the translate message again, this time without interacting with it.
+  EXPECT_CALL(*bridge_, CreateTranslateMessage(
+                            env, _, _, kDefaultDismissalDurationSeconds))
+      .WillOnce(Return(true));
   ShowBeforeTranslationMessage(env, "fr", "en");
 
   // Dismiss the translate message.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::GESTURE));
   EXPECT_EQ(2, on_dismiss_callback_called_count_);
@@ -1368,6 +1409,7 @@
                                         "en");
 
   // Dismiss the message.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::GESTURE));
   EXPECT_EQ(1, on_dismiss_callback_called_count_);
@@ -1487,6 +1529,7 @@
   translate_message_->HandlePrimaryAction(env);
 
   // Simulate the message being dismissed from Java.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::GESTURE));
   EXPECT_EQ(1, on_dismiss_callback_called_count_);
@@ -1494,6 +1537,9 @@
       translate_prefs_->IsLanguagePairOnAlwaysTranslateList("fr", "en"));
 
   // Finish the translation, causing the Message to pop up again.
+  EXPECT_CALL(*bridge_, CreateTranslateMessage(
+                            env, _, _, kDefaultDismissalDurationSeconds))
+      .WillOnce(Return(true));
   FinishTranslation(env, "fr", "en");
 
   EXPECT_TRUE(
@@ -1672,6 +1718,7 @@
               env,
               static_cast<jint>(messages::DismissReason::DISMISSED_BY_FEATURE));
         }));
+    EXPECT_CALL(*bridge_, ClearNativePointer(env));
     translate_message_->HandlePrimaryAction(env);
 
     histogram_tester.ExpectUniqueSample(
@@ -1703,6 +1750,7 @@
   ShowBeforeTranslationMessage(env, "fr", "en");
 
   // Dismiss the message.
+  EXPECT_CALL(*bridge_, ClearNativePointer(env));
   translate_message_->HandleDismiss(
       env, static_cast<jint>(messages::DismissReason::GESTURE));
   EXPECT_EQ(1, on_dismiss_callback_called_count_);
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 5dda4d5..263dfc9 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -174,6 +174,11 @@
                                               base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
+// TODO(crbug.com/1357744): Solve the vulkan flakiness issue before enabling
+// this on Linux.
+const base::Feature kAllowUndamagedNonrootRenderPassToSkip{
+    "AllowUndamagedNonrootRenderPassToSkip", base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool IsAdpfEnabled() {
   // TODO(crbug.com/1157620): Limit this to correct android version.
   return base::FeatureList::IsEnabled(kAdpf);
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index 7f2476e..c0e45fb 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -59,6 +59,9 @@
 #endif
 
 VIZ_COMMON_EXPORT extern const base::Feature kDrawPredictedInkPoint;
+VIZ_COMMON_EXPORT extern const base::Feature
+    kAllowUndamagedNonrootRenderPassToSkip;
+
 VIZ_COMMON_EXPORT extern const char kDraw1Point12Ms[];
 VIZ_COMMON_EXPORT extern const char kDraw2Points6Ms[];
 VIZ_COMMON_EXPORT extern const char kDraw1Point6Ms[];
diff --git a/components/viz/common/quads/render_pass_internal.h b/components/viz/common/quads/render_pass_internal.h
index 4a69c2d..b8cadac 100644
--- a/components/viz/common/quads/render_pass_internal.h
+++ b/components/viz/common/quads/render_pass_internal.h
@@ -62,9 +62,13 @@
 
   // If true we might reuse the texture if there is no damage.
   bool cache_render_pass = false;
+
   // Indicates whether there is accumulated damage from contributing render
   // surface or layer or surface quad. Not including property changes on itself.
-  bool has_damage_from_contributing_content = false;
+  // TODO(crbug.com/1358700): By default we assume the pass is damaged. Remove
+  // this field in favour of using |damage_rect| for feature
+  // kAllowUndamagedNonrootRenderPassToSkip.
+  bool has_damage_from_contributing_content = true;
 
   // Generate mipmap for trilinear filtering, applied to render pass' texture.
   bool generate_mipmap = false;
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 589fd71..f8ba1a3 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -21,6 +21,7 @@
 #include "cc/base/math_util.h"
 #include "cc/paint/filter_operations.h"
 #include "components/viz/common/display/renderer_settings.h"
+#include "components/viz/common/features.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_util.h"
 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
@@ -86,7 +87,9 @@
       debug_settings_(debug_settings),
       output_surface_(output_surface),
       resource_provider_(resource_provider),
-      overlay_processor_(overlay_processor) {
+      overlay_processor_(overlay_processor),
+      allow_undamaged_nonroot_render_pass_to_skip_(base::FeatureList::IsEnabled(
+          features::kAllowUndamagedNonrootRenderPassToSkip)) {
   DCHECK(output_surface_);
 }
 
@@ -347,6 +350,7 @@
   // Only reshape when we know we are going to draw. Otherwise, the reshape
   // can leave the window at the wrong size if we never draw and the proper
   // viewport size is never set.
+  skipped_render_pass_ids_.clear();
   bool needs_full_frame_redraw = false;
   auto display_transform = output_surface_->GetDisplayTransform();
   OutputSurface::ReshapeParams reshape_params;
@@ -414,7 +418,6 @@
   current_frame()->render_passes_in_draw_order = nullptr;
   current_frame()->root_render_pass = nullptr;
 
-  skipped_render_pass_ids_.clear();
   render_passes_in_draw_order->clear();
   render_pass_filters_.clear();
   render_pass_backdrop_filters_.clear();
@@ -721,25 +724,18 @@
   if (render_pass == current_frame()->root_render_pass)
     return false;
 
-  // TODO(crbug.com/783275): It's possible to skip a child RenderPass if damage
-  // does not overlap it, since that means nothing has changed:
-  //   ComputeScissorRectForRenderPass(render_pass).IsEmpty()
-  // However that caused crashes where the RenderPass' texture was not present
-  // (never seen the RenderPass before, or the texture was deleted when not used
-  // for a frame). It could avoid skipping if there is no texture present, which
-  // is what was done for a while, but this seems to papering over a missing
-  // damage problem, or we're failing to understand the system wholey.
-  // If attempted again this should probably CHECK() that the texture exists,
-  // and attempt to figure out where the new RenderPass texture without damage
-  // is coming from.
-
   // If the RenderPass wants to be cached, then we only draw it if we need to.
   // When damage is present, then we can't skip the RenderPass. Or if the
   // texture does not exist (first frame, or was deleted) then we can't skip
   // the RenderPass.
-  if (render_pass->cache_render_pass) {
-    if (render_pass->has_damage_from_contributing_content)
+  if (render_pass->cache_render_pass ||
+      allow_undamaged_nonroot_render_pass_to_skip_) {
+    // TODO(crbug.com/1346502): Fix CopyOutputRequest and allow the render pass
+    // with copy request to skip.
+    if (render_pass->has_damage_from_contributing_content ||
+        !render_pass->copy_requests.empty()) {
       return false;
+    }
     return IsRenderPassResourceAllocated(render_pass->id);
   }
 
diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h
index d8df093..7d71fdb5 100644
--- a/components/viz/service/display/direct_renderer.h
+++ b/components/viz/service/display/direct_renderer.h
@@ -152,6 +152,11 @@
     return last_root_render_pass_scissor_rect_;
   }
 
+  base::flat_set<AggregatedRenderPassId>*
+  GetLastSkippedRenderPassIdsForTesting() {
+    return &skipped_render_pass_ids_;
+  }
+
   virtual DelegatedInkPointRendererBase* GetDelegatedInkPointRenderer(
       bool create_if_necessary);
   virtual void SetDelegatedInkMetadata(
@@ -319,6 +324,10 @@
   // use OverlayProcessorStub so this pointer is never null.
   raw_ptr<OverlayProcessorInterface> overlay_processor_;
 
+  // If the non-root render pass and its embedded child render passes are not
+  // damaged, skip the rendering.
+  const bool allow_undamaged_nonroot_render_pass_to_skip_;
+
   // Whether it's valid to SwapBuffers with an empty rect. Trivially true when
   // using partial swap.
   bool allow_empty_swap_ = false;
diff --git a/components/viz/service/display/display_compositor_memory_and_task_controller.cc b/components/viz/service/display/display_compositor_memory_and_task_controller.cc
index 04df4ef..10d0924 100644
--- a/components/viz/service/display/display_compositor_memory_and_task_controller.cc
+++ b/components/viz/service/display/display_compositor_memory_and_task_controller.cc
@@ -23,32 +23,15 @@
   DCHECK(gpu_task_scheduler_);
   base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
                             base::WaitableEvent::InitialState::NOT_SIGNALED);
-  auto callback = base::BindOnce(
-      &DisplayCompositorMemoryAndTaskController::InitializeOnGpuSkia,
-      base::Unretained(this), skia_dependency_.get(), &event);
+  auto callback =
+      base::BindOnce(&DisplayCompositorMemoryAndTaskController::InitializeOnGpu,
+                     base::Unretained(this), skia_dependency_.get(), &event);
   gpu_task_scheduler_->ScheduleGpuTask(std::move(callback), {});
   event.Wait();
 
   shared_image_interface_ =
       std::make_unique<gpu::SharedImageInterfaceInProcess>(
-          gpu_task_scheduler_->GetTaskSequence(), controller_on_gpu_.get(),
-          nullptr /* command_buffer_helper*/);
-}
-
-DisplayCompositorMemoryAndTaskController::
-    DisplayCompositorMemoryAndTaskController(
-        gpu::CommandBufferTaskExecutor* task_executor,
-        gpu::ImageFactory* image_factory)
-    : gpu_task_scheduler_(
-          std::make_unique<gpu::GpuTaskSchedulerHelper>(task_executor)) {
-  DCHECK(gpu_task_scheduler_);
-  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
-                            base::WaitableEvent::InitialState::NOT_SIGNALED);
-  auto callback = base::BindOnce(
-      &DisplayCompositorMemoryAndTaskController::InitializeOnGpuGL,
-      base::Unretained(this), task_executor, image_factory, &event);
-  gpu_task_scheduler_->GetTaskSequence()->ScheduleTask(std::move(callback), {});
-  event.Wait();
+          gpu_task_scheduler_->GetTaskSequence(), controller_on_gpu_.get());
 }
 
 DisplayCompositorMemoryAndTaskController::
@@ -69,7 +52,7 @@
   event.Wait();
 }
 
-void DisplayCompositorMemoryAndTaskController::InitializeOnGpuSkia(
+void DisplayCompositorMemoryAndTaskController::InitializeOnGpu(
     SkiaOutputSurfaceDependency* skia_dependency,
     base::WaitableEvent* event) {
   DCHECK(event);
@@ -86,17 +69,6 @@
   event->Signal();
 }
 
-void DisplayCompositorMemoryAndTaskController::InitializeOnGpuGL(
-    gpu::CommandBufferTaskExecutor* task_executor,
-    gpu::ImageFactory* image_factory,
-    base::WaitableEvent* event) {
-  DCHECK(event);
-  controller_on_gpu_ =
-      std::make_unique<gpu::DisplayCompositorMemoryAndTaskControllerOnGpu>(
-          task_executor, image_factory);
-  event->Signal();
-}
-
 void DisplayCompositorMemoryAndTaskController::DestroyOnGpu(
     base::WaitableEvent* event) {
   DCHECK(event);
diff --git a/components/viz/service/display/display_compositor_memory_and_task_controller.h b/components/viz/service/display/display_compositor_memory_and_task_controller.h
index 75767d9..42ad19d 100644
--- a/components/viz/service/display/display_compositor_memory_and_task_controller.h
+++ b/components/viz/service/display/display_compositor_memory_and_task_controller.h
@@ -16,10 +16,9 @@
 }
 
 namespace gpu {
-class ImageFactory;
 class SharedImageInterface;
 class SharedImageInterfaceInProcess;
-}
+}  // namespace gpu
 
 namespace viz {
 class SkiaOutputSurfaceDependency;
@@ -27,17 +26,11 @@
 // This class holds onwership of task posting sequence to the gpu thread and
 // memory tracking for the display compositor. This class has a 1:1 relationship
 // to the display compositor class. This class is only used for gpu compositing.
-// TODO(weiliangc): After GLRenderer is removed, this should merge with
-// SkiaOutputSurfaceDependency.
+// TODO(weiliangc): This should merge with SkiaOutputSurfaceDependency.
 class VIZ_SERVICE_EXPORT DisplayCompositorMemoryAndTaskController {
  public:
-  // For SkiaRenderer.
   explicit DisplayCompositorMemoryAndTaskController(
       std::unique_ptr<SkiaOutputSurfaceDependency> skia_dependency);
-  // For InProcessCommandBuffer.
-  DisplayCompositorMemoryAndTaskController(
-      gpu::CommandBufferTaskExecutor* task_executor,
-      gpu::ImageFactory* image_factory);
   DisplayCompositorMemoryAndTaskController(
       const DisplayCompositorMemoryAndTaskController&) = delete;
   DisplayCompositorMemoryAndTaskController& operator=(
@@ -58,11 +51,8 @@
   gpu::SharedImageInterface* shared_image_interface();
 
  private:
-  void InitializeOnGpuSkia(SkiaOutputSurfaceDependency* skia_dependency,
-                           base::WaitableEvent* event);
-  void InitializeOnGpuGL(gpu::CommandBufferTaskExecutor* task_executor,
-                         gpu::ImageFactory* image_factory,
-                         base::WaitableEvent* event);
+  void InitializeOnGpu(SkiaOutputSurfaceDependency* skia_dependency,
+                       base::WaitableEvent* event);
   void DestroyOnGpu(base::WaitableEvent* event);
 
   // Accessed on viz compositor thread.
@@ -75,8 +65,6 @@
       controller_on_gpu_;
 
   // Accessed on the compositor thread.
-  // TODO(weiliangc): Move the GLRenderer's SharedImageInterface ownership here
-  // as well.
   std::unique_ptr<gpu::SharedImageInterfaceInProcess> shared_image_interface_;
 };
 
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index 896a3636..28fc8a0 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -4448,6 +4448,94 @@
   }
 }
 
+TEST_F(DisplayTest, CanSkipRenderPass) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      features::kAllowUndamagedNonrootRenderPassToSkip);
+
+  id_allocator_.GenerateId();
+  const LocalSurfaceId local_surface_id(
+      id_allocator_.GetCurrentLocalSurfaceId());
+
+  // Set up first display.
+  SetUpSoftwareDisplay(RendererSettings());
+  StubDisplayClient client;
+  display_->Initialize(&client, manager_.surface_manager());
+  display_->SetLocalSurfaceId(local_surface_id, 1.f);
+
+  // Create frame sink for a sub surface.
+  TestSurfaceIdAllocator sub_surface_id1(kAnotherFrameSinkId);
+  auto sub_support1 = std::make_unique<CompositorFrameSinkSupport>(
+      nullptr, &manager_, kAnotherFrameSinkId, /*is_root=*/false);
+
+  // generate render pass id for the nonroot render pass.
+  CompositorRenderPassId::Generator render_pass_id_generator;
+  auto id_1 = render_pass_id_generator.GenerateNextId();
+
+  const gfx::Size display_size(100, 100);
+  const gfx::Rect root_damage_rect(20, 20, 40, 40);
+  display_->Resize(display_size);
+  const gfx::Rect sub_surface_rect(5, 5, 60, 60);
+  const gfx::Rect sub_surface_damage_rect(10, 10, 30, 30);
+
+  for (size_t frame_num = 1; frame_num <= 3; ++frame_num) {
+    ResetDamageForTest();
+
+    // Nonroot render pass with id_1. No update for frame #3.
+    if (frame_num != 3) {
+      CompositorRenderPassList pass_list;
+      auto bd_pass = CompositorRenderPass::Create();
+      bd_pass->output_rect = sub_surface_rect;
+      bd_pass->damage_rect = sub_surface_damage_rect;
+      bd_pass->has_damage_from_contributing_content = true;
+      bd_pass->id = id_1;
+      pass_list.push_back(std::move(bd_pass));
+
+      CompositorFrame frame = CompositorFrameBuilder()
+                                  .SetRenderPassList(std::move(pass_list))
+                                  .Build();
+
+      sub_support1->SubmitCompositorFrame(sub_surface_id1.local_surface_id(),
+                                          std::move(frame));
+    }
+
+    // Root render pass
+    {
+      auto frame =
+          CompositorFrameBuilder()
+              .AddRenderPass(RenderPassBuilder(display_size)
+                                 .AddSurfaceQuad(sub_surface_rect,
+                                                 SurfaceRange(absl::nullopt,
+                                                              sub_surface_id1),
+                                                 {.allow_merge = false})
+                                 .SetDamageRect(root_damage_rect))
+              .Build();
+      support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
+
+      scheduler_->reset_swapped_for_test();
+      display_->DrawAndSwap({base::TimeTicks::Now(), base::TimeTicks::Now()});
+      EXPECT_TRUE(scheduler_->swapped());
+
+      // Number of skipped non-root render passes.
+      auto* skipped = display_->renderer_for_testing()
+                          ->GetLastSkippedRenderPassIdsForTesting();
+
+      if (frame_num != 3) {
+        // Whether the render pass can be skpped or not depends on the flag
+        // pass->has_damage_from_contributing_content and the render pass
+        // damage rect.
+        EXPECT_EQ(0u, skipped->size());
+      } else {
+        // No frame update for the sub surface. The nonroot render pass damage
+        // rect will be zero. pass->has_damage_from_contributing_content becomes
+        // false when there is no frame update. The associated non-render pass
+        // can be skipped.
+        EXPECT_EQ(1u, skipped->size());
+      }
+    }
+  }
+}
+
 class SkiaDelegatedInkRendererTest : public DisplayTest {
  public:
   void SetUp() override { EnablePrediction(); }
diff --git a/components/viz/service/display/skia_output_surface.h b/components/viz/service/display/skia_output_surface.h
index 8fb646fb..981ca6a 100644
--- a/components/viz/service/display/skia_output_surface.h
+++ b/components/viz/service/display/skia_output_surface.h
@@ -34,10 +34,6 @@
 class ColorSpace;
 }  // namespace gfx
 
-namespace gpu {
-class SharedImageInterface;
-}
-
 namespace viz {
 
 class OverlayCandidate;
@@ -202,9 +198,6 @@
   // the GPU main thread.
   virtual gpu::SyncToken Flush() = 0;
 
-  // Only used for creating and destroying shared images for render passes
-  virtual gpu::SharedImageInterface* GetSharedImageInterface() = 0;
-
   // Set the number of frame buffers to use when
   // `supports_dynamic_frame_buffer_allocation` is true. `n` must satisfy
   // 0 < n <= capabilities_.number_of_buffers.
diff --git a/components/viz/service/display_embedder/compositor_gpu_thread.cc b/components/viz/service/display_embedder/compositor_gpu_thread.cc
index 17a87cfa..247e67d 100644
--- a/components/viz/service/display_embedder/compositor_gpu_thread.cc
+++ b/components/viz/service/display_embedder/compositor_gpu_thread.cc
@@ -23,7 +23,6 @@
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_surface_egl.h"
-#include "ui/gl/gl_utils.h"
 #include "ui/gl/init/gl_factory.h"
 
 #if BUILDFLAG(ENABLE_VULKAN)
@@ -39,6 +38,7 @@
     gpu::GpuChannelManager* gpu_channel_manager,
     gpu::VulkanImplementation* vulkan_implementation,
     gpu::VulkanDeviceQueue* device_queue,
+    gl::GLDisplay* display,
     bool enable_watchdog) {
   DCHECK(gpu_channel_manager);
 
@@ -52,9 +52,10 @@
   // context virtualization group extension should be enabled. Also since angle
   // currently always enables this extension, we are adding DCHECK() to ensure
   // that instead of enabling/disabling DrDc based on the extension.
-  if (gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE)
-    DCHECK(gl::GLSurfaceEGL::GetGLDisplayEGL()
-               ->ext->b_EGL_ANGLE_context_virtualization);
+  if (gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE) {
+    gl::GLDisplayEGL* display_egl = display->GetAs<gl::GLDisplayEGL>();
+    DCHECK(display_egl->ext->b_EGL_ANGLE_context_virtualization);
+  }
 #endif
 
   scoped_refptr<VulkanContextProvider> vulkan_context_provider;
@@ -78,7 +79,7 @@
 #endif
 
   auto compositor_gpu_thread = base::WrapUnique(new CompositorGpuThread(
-      gpu_channel_manager, std::move(vulkan_context_provider),
+      gpu_channel_manager, std::move(vulkan_context_provider), display,
       enable_watchdog));
 
   if (!compositor_gpu_thread->Initialize())
@@ -89,11 +90,13 @@
 CompositorGpuThread::CompositorGpuThread(
     gpu::GpuChannelManager* gpu_channel_manager,
     scoped_refptr<VulkanContextProvider> vulkan_context_provider,
+    gl::GLDisplay* display,
     bool enable_watchdog)
     : base::Thread("CompositorGpuThread"),
       gpu_channel_manager_(gpu_channel_manager),
       enable_watchdog_(enable_watchdog),
       vulkan_context_provider_(std::move(vulkan_context_provider)),
+      display_(display),
       weak_ptr_factory_(this) {}
 
 CompositorGpuThread::~CompositorGpuThread() {
@@ -113,8 +116,7 @@
   // Create a new share group. Note that this share group is different from the
   // share group which gpu main thread uses.
   auto share_group = base::MakeRefCounted<gl::GLShareGroup>();
-  auto surface =
-      gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(), gfx::Size());
+  auto surface = gl::init::CreateOffscreenGLSurface(display_, gfx::Size());
 
   const auto& gpu_preferences = gpu_channel_manager_->gpu_preferences();
 
diff --git a/components/viz/service/display_embedder/compositor_gpu_thread.h b/components/viz/service/display_embedder/compositor_gpu_thread.h
index 158fc6c..d651716 100644
--- a/components/viz/service/display_embedder/compositor_gpu_thread.h
+++ b/components/viz/service/display_embedder/compositor_gpu_thread.h
@@ -15,6 +15,10 @@
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
 
+namespace gl {
+class GLDisplay;
+}  // namespace gl
+
 namespace gpu {
 class GpuChannelManager;
 class VulkanImplementation;
@@ -32,6 +36,7 @@
       gpu::GpuChannelManager* gpu_channel_manager,
       gpu::VulkanImplementation* vulkan_implementation,
       gpu::VulkanDeviceQueue* device_queue,
+      gl::GLDisplay* display,
       bool enable_watchdog);
 
   // Disallow copy and assign.
@@ -71,6 +76,7 @@
   CompositorGpuThread(
       gpu::GpuChannelManager* gpu_channel_manager,
       scoped_refptr<VulkanContextProvider> vulkan_context_provider,
+      gl::GLDisplay* display,
       bool enable_watchdog);
 
   bool Initialize();
@@ -85,6 +91,10 @@
 
   scoped_refptr<VulkanContextProvider> vulkan_context_provider_;
 
+  // The GLDisplay lives in GLDisplayManager, which never deletes displays once
+  // they are lazily created.
+  raw_ptr<gl::GLDisplay> display_ = nullptr;
+
   // WatchdogThread to monitor CompositorGpuThread. Ensure that the members
   // which needs to be monitored by |watchdog_thread_| should be destroyed
   // before it by either adding them below it or explicitly destroying them
diff --git a/components/viz/service/display_embedder/output_presenter_gl.cc b/components/viz/service/display_embedder/output_presenter_gl.cc
index 06d31061..3345d8f 100644
--- a/components/viz/service/display_embedder/output_presenter_gl.cc
+++ b/components/viz/service/display_embedder/output_presenter_gl.cc
@@ -185,8 +185,8 @@
     return nullptr;
   // TODO(https://crbug.com/1012401): don't depend on GL.
   auto gl_surface = base::MakeRefCounted<gl::GLSurfaceEGLSurfaceControl>(
-      gl::GLSurfaceEGL::GetGLDisplayEGL(), window,
-      base::ThreadTaskRunnerHandle::Get());
+      deps->GetSharedContextState()->display()->GetAs<gl::GLDisplayEGL>(),
+      window, base::ThreadTaskRunnerHandle::Get());
   if (!gl_surface->Initialize(gl::GLSurfaceFormat())) {
     LOG(ERROR) << "Failed to initialize GLSurfaceEGLSurfaceControl.";
     return nullptr;
diff --git a/components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc b/components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc
index a4e5450c..067a10d 100644
--- a/components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc
@@ -17,7 +17,6 @@
 #include "gpu/command_buffer/service/scheduler.h"
 #include "gpu/command_buffer/service/scheduler_sequence.h"
 #include "gpu/ipc/service/image_transport_surface.h"
-#include "ui/gl/gl_utils.h"
 #include "ui/gl/init/gl_factory.h"
 
 namespace viz {
@@ -108,11 +107,11 @@
     base::WeakPtr<gpu::ImageTransportSurfaceDelegate> stub,
     gl::GLSurfaceFormat format) {
   if (IsOffscreen()) {
-    return gl::init::CreateOffscreenGLSurfaceWithFormat(gl::GetDefaultDisplay(),
-                                                        gfx::Size(), format);
+    return gl::init::CreateOffscreenGLSurfaceWithFormat(
+        GetSharedContextState()->display(), gfx::Size(), format);
   } else {
     return gpu::ImageTransportSurface::CreateNativeSurface(
-        gl::GetDefaultDisplay(), stub, surface_handle_, format);
+        GetSharedContextState()->display(), stub, surface_handle_, format);
   }
 }
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index cb06a92..937f9190 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -1212,10 +1212,6 @@
   return dependency_->CacheGLSurface(impl_on_gpu_->gl_surface());
 }
 
-gpu::SharedImageInterface* SkiaOutputSurfaceImpl::GetSharedImageInterface() {
-  return display_compositor_controller_->shared_image_interface();
-}
-
 void SkiaOutputSurfaceImpl::AddContextLostObserver(
     ContextLostObserver* observer) {
   observers_.AddObserver(observer);
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index da1fdf4..7b61e33 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -144,7 +144,6 @@
   void AddContextLostObserver(ContextLostObserver* observer) override;
   void RemoveContextLostObserver(ContextLostObserver* observer) override;
   void PreserveChildSurfaceControls() override;
-  gpu::SharedImageInterface* GetSharedImageInterface() override;
   gpu::SyncToken Flush() override;
   bool EnsureMinNumberOfBuffers(int n) override;
   gpu::Mailbox CreateSharedImage(ResourceFormat format,
diff --git a/components/viz/service/frame_sinks/gmb_video_frame_pool_context_provider_impl.cc b/components/viz/service/frame_sinks/gmb_video_frame_pool_context_provider_impl.cc
index 85b92a98..76c3471 100644
--- a/components/viz/service/frame_sinks/gmb_video_frame_pool_context_provider_impl.cc
+++ b/components/viz/service/frame_sinks/gmb_video_frame_pool_context_provider_impl.cc
@@ -33,6 +33,7 @@
                                std::move(on_context_lost))) {
     DETACH_FROM_SEQUENCE(gpu_sequence_checker_);
 
+    // TODO(vikassoni): Verify this is the right GPU thread/sequence for DrDC.
     sequence_ = std::make_unique<gpu::SchedulerSequence>(
         gpu_service_->GetGpuScheduler(), gpu_service_->main_runner(),
         /*target_thread_is_always_available=*/true);
@@ -116,7 +117,7 @@
         gpu_service_->gpu_driver_bug_workarounds(),
         gpu_service_->gpu_feature_info(), shared_context_state_.get(),
         gpu_service_->shared_image_manager(), gpu_service_->gpu_image_factory(),
-        shared_context_state_->memory_tracker());
+        /*is_for_display_compositor=*/false);
     DCHECK(sii_in_process_);
 
     initialized_ = true;
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index c38d4612..895a356 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -530,7 +530,7 @@
 
 void GpuServiceImpl::UpdateGPUInfoGL() {
   DCHECK(main_runner_->BelongsToCurrentThread());
-  gpu::CollectGraphicsInfoGL(&gpu_info_, gl::GetDefaultDisplay());
+  gpu::CollectGraphicsInfoGL(&gpu_info_, GetContextState()->display());
   gpu_host_->DidUpdateGPUInfo(gpu_info_);
 }
 
@@ -625,6 +625,9 @@
 #else
       nullptr, nullptr,
 #endif
+      gpu_channel_manager_->default_offscreen_surface()
+          ? gpu_channel_manager_->default_offscreen_surface()->GetGLDisplay()
+          : nullptr,
       !!watchdog_thread_);
 }
 
diff --git a/components/viz/test/fake_skia_output_surface.h b/components/viz/test/fake_skia_output_surface.h
index 79b3f3f6..4fee11292 100644
--- a/components/viz/test/fake_skia_output_surface.h
+++ b/components/viz/test/fake_skia_output_surface.h
@@ -98,7 +98,6 @@
                   const gpu::Mailbox& mailbox) override;
   void AddContextLostObserver(ContextLostObserver* observer) override;
   void RemoveContextLostObserver(ContextLostObserver* observer) override;
-  gpu::SharedImageInterface* GetSharedImageInterface() override;
   gpu::SyncToken Flush() override;
   bool EnsureMinNumberOfBuffers(int n) override;
   void PreserveChildSurfaceControls() override {}
@@ -121,6 +120,8 @@
       sk_sp<SkColorSpace> color_space,
       bool raw_draw_if_possible) override;
 
+  gpu::SharedImageInterface* GetSharedImageInterface();
+
   // If set true, callbacks triggering will be in a reverse order as SignalQuery
   // calls.
   void SetOutOfOrderCallbacks(bool out_of_order_callbacks);
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 14d2343..c66168c 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -223,14 +223,6 @@
   ParentConnectionChanged(parent);
 }
 
-void BrowserAccessibilityManager::Initialize(
-    const ui::AXTreeUpdate& initial_tree) {
-  if (!ax_tree()->Unserialize(initial_tree)) {
-    LOG(FATAL) << "No recovery is possible if the initial tree is broken: "
-               << ax_tree()->error();
-  }
-}
-
 // A flag for use in tests to ensure events aren't suppressed or delayed.
 // static
 bool BrowserAccessibilityManager::never_suppress_or_delay_events_for_testing_ =
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index ed0c5d11d..81b8fc1 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -164,8 +164,6 @@
 
   ~BrowserAccessibilityManager() override;
 
-  void Initialize(const ui::AXTreeUpdate& initial_tree);
-
   static ui::AXTreeUpdate GetEmptyDocument();
 
   enum RetargetEventType {
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index aa5fac11..bf81ebe 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -17,7 +17,6 @@
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
 #include "content/browser/web_contents/web_contents_impl.h"
-#include "content/public/browser/guest_host.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_widget_host_view.h"
@@ -42,13 +41,6 @@
   RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Create"));
 }
 
-void BrowserPluginGuest::WillDestroy() {
-  // It is important that the WebContents is notified before detaching.
-  GetWebContents()->BrowserPluginGuestWillDetach();
-
-  owner_web_contents_ = nullptr;
-}
-
 void BrowserPluginGuest::Init() {
   if (initialized_)
     return;
@@ -120,7 +112,6 @@
     WebContentsImpl* web_contents,
     BrowserPluginGuestDelegate* delegate) {
   auto guest = base::WrapUnique(new BrowserPluginGuest(web_contents, delegate));
-  delegate->SetGuestHost(guest.get());
   web_contents->SetBrowserPluginGuest(std::move(guest));
 }
 
@@ -218,13 +209,11 @@
     bool right_aligned,
     bool allow_multiple_selection) {
   gfx::Rect translated_bounds(bounds);
-  WebContents* guest = web_contents();
+  auto* guest_rwhv = render_frame_host->GetView();
   translated_bounds.set_origin(
-      guest->GetRenderWidgetHostView()->TransformPointToRootCoordSpace(
-          translated_bounds.origin()));
-  BrowserPluginPopupMenuHelper popup_menu_helper(
-      owner_web_contents_->GetPrimaryMainFrame(), render_frame_host,
-      std::move(*popup_client));
+      guest_rwhv->TransformPointToRootCoordSpace(translated_bounds.origin()));
+  BrowserPluginPopupMenuHelper popup_menu_helper(render_frame_host,
+                                                 std::move(*popup_client));
   popup_menu_helper.ShowPopupMenu(translated_bounds, item_height, font_size,
                                   selected_item, std::move(*menu_items),
                                   right_aligned, allow_multiple_selection);
diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h
index 4dcb038..83ca5f3e 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.h
+++ b/content/browser/browser_plugin/browser_plugin_guest.h
@@ -24,7 +24,6 @@
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "content/public/browser/browser_plugin_guest_delegate.h"
-#include "content/public/browser/guest_host.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/choosers/popup_menu.mojom.h"
@@ -50,7 +49,7 @@
 // dropped on the floor since we don't have a BrowserPlugin.
 // TODO(wjmaclean): Get rid of "BrowserPlugin" in the name of this class.
 // Perhaps "InnerWebContentsGuestConnector"?
-class BrowserPluginGuest : public GuestHost, public WebContentsObserver {
+class BrowserPluginGuest : public WebContentsObserver {
  public:
   BrowserPluginGuest(const BrowserPluginGuest&) = delete;
   BrowserPluginGuest& operator=(const BrowserPluginGuest&) = delete;
@@ -108,9 +107,6 @@
       bool allow_multiple_selection);
 #endif
 
-  // GuestHost implementation.
-  void WillDestroy() override;
-
   // Exposes the protected web_contents() from WebContentsObserver.
   WebContentsImpl* GetWebContents() const;
 
diff --git a/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h b/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h
index 0b2ce50..efca065f 100644
--- a/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h
+++ b/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h
@@ -5,7 +5,6 @@
 #ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_POPUP_MENU_HELPER_MAC_H_
 #define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_POPUP_MENU_HELPER_MAC_H_
 
-#include "base/memory/raw_ptr.h"
 #include "content/browser/renderer_host/popup_menu_helper_mac.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/choosers/popup_menu.mojom.h"
@@ -13,18 +12,18 @@
 namespace content {
 
 class RenderFrameHost;
-class RenderFrameHostImpl;
 
 // This class is similiar to PopupMenuHelperMac but positions the popup relative
 // to the embedder, and issues a reply to the guest.
+// TODO(533069): This class no longer appears to serve a purpose. The base
+// PopupMenuHelper already handles the coordinate transformations correctly.
 class BrowserPluginPopupMenuHelper : public PopupMenuHelper,
                                      public PopupMenuHelper::Delegate {
  public:
   // Creates a BrowserPluginPopupMenuHelper that positions popups relative to
-  // |embedder_rfh| and will notify |guest_rfh| when a user selects or cancels
-  // the popup.
+  // the embedder of `guest_rfh` and will notify `guest_rfh` when a user
+  // selects or cancels the popup.
   BrowserPluginPopupMenuHelper(
-      RenderFrameHostImpl* embedder_rfh,
       RenderFrameHost* guest_rfh,
       mojo::PendingRemote<blink::mojom::PopupMenuClient> popup_client);
 
@@ -33,13 +32,8 @@
       delete;
 
  private:
-  // PopupMenuHelper:
-  RenderWidgetHostViewMac* GetRenderWidgetHostView() const override;
-
   // PopupMenuHelper:Delegate:
   void OnMenuClosed() override;
-
-  raw_ptr<RenderFrameHostImpl> embedder_rfh_;
 };
 
 }  // namespace content
diff --git a/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.mm b/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.mm
index e1f84cf..63537a0 100644
--- a/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.mm
+++ b/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.mm
@@ -4,23 +4,12 @@
 
 #include "content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h"
 
-#include "content/browser/renderer_host/render_frame_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_view_mac.h"
-#include "content/public/browser/render_widget_host.h"
-
 namespace content {
 
 BrowserPluginPopupMenuHelper::BrowserPluginPopupMenuHelper(
-    RenderFrameHostImpl* embedder_rfh,
     RenderFrameHost* guest_rfh,
     mojo::PendingRemote<blink::mojom::PopupMenuClient> popup_client)
-    : PopupMenuHelper(this, guest_rfh, std::move(popup_client)),
-      embedder_rfh_(embedder_rfh) {}
-
-RenderWidgetHostViewMac*
-    BrowserPluginPopupMenuHelper::GetRenderWidgetHostView() const {
-  return static_cast<RenderWidgetHostViewMac*>(embedder_rfh_->GetView());
-}
+    : PopupMenuHelper(this, guest_rfh, std::move(popup_client)) {}
 
 void BrowserPluginPopupMenuHelper::OnMenuClosed() {
   // BrowserPluginGuest doesn't support cancellation of popup menus, so the
diff --git a/content/browser/first_party_sets/first_party_set_parser.cc b/content/browser/first_party_sets/first_party_set_parser.cc
index 14d7a967..c2f69d4 100644
--- a/content/browser/first_party_sets/first_party_set_parser.cc
+++ b/content/browser/first_party_sets/first_party_set_parser.cc
@@ -10,14 +10,11 @@
 #include <utility>
 #include <vector>
 
-#include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
-#include "base/files/file_util.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_string_value_serializer.h"
 #include "base/logging.h"
-#include "base/path_service.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_util.h"
 #include "base/types/expected.h"
@@ -69,12 +66,26 @@
   return site;
 }
 
+// Struct to hold metadata describing a particular "subset" during parsing.
+struct SubsetDescriptor {
+  std::string field_name;
+  net::SiteType site_type;
+  bool keep_indices;
+};
+
 const char kFirstPartySetPrimaryField[] = "primary";
 const char kFirstPartySetAssociatedSitesField[] = "associatedSites";
+const char kFirstPartySetServiceSitesField[] = "serviceSites";
 const char kCCTLDsField[] = "ccTLDs";
 const char kFirstPartySetPolicyReplacementsField[] = "replacements";
 const char kFirstPartySetPolicyAdditionsField[] = "additions";
 
+bool IsSingletonSet(const std::vector<SetsMap::value_type>& set_entries,
+                    const Aliases& aliases) {
+  // There's no point in having a set with only one site and no aliases.
+  return set_entries.size() + aliases.size() < 2;
+}
+
 // Parses a single base::Value into a net::SchemefulSite, and verifies that it
 // is not already included in this set or any other.
 base::expected<net::SchemefulSite, ParseError> ParseSiteAndValidate(
@@ -165,6 +176,43 @@
   return aliases;
 }
 
+// Parses a given optional subset, ensuring that it is disjoint from all other
+// subsets in this set, and from all other sets that have previously been
+// parsed.
+absl::optional<ParseError> ParseSubset(
+    const base::Value::Dict& set_declaration,
+    const net::SchemefulSite& primary,
+    bool keep_indices,
+    const SubsetDescriptor& descriptor,
+    const base::flat_set<net::SchemefulSite>& other_sets_sites,
+    std::vector<std::pair<net::SchemefulSite, net::FirstPartySetEntry>>&
+        set_entries) {
+  const base::Value* field_value = set_declaration.Find(descriptor.field_name);
+  if (!field_value)
+    return absl::nullopt;
+  if (!field_value->is_list())
+    return ParseError::kInvalidType;
+
+  // Add each site to our mapping (after validating).
+  uint32_t index = 0;
+  for (const auto& item : field_value->GetList()) {
+    base::expected<net::SchemefulSite, ParseError> site_or_error =
+        ParseSiteAndValidate(item, set_entries, other_sets_sites);
+    if (!site_or_error.has_value())
+      return site_or_error.error();
+    set_entries.emplace_back(
+        site_or_error.value(),
+        net::FirstPartySetEntry(
+            primary, descriptor.site_type,
+            keep_indices && descriptor.keep_indices
+                ? absl::make_optional(net::FirstPartySetEntry::SiteIndex(index))
+                : absl::nullopt));
+    ++index;
+  }
+
+  return absl::nullopt;
+}
+
 // Validates a single First-Party Set and parses it into a SingleSet.
 // Note that this is intended for use *only* on sets that were received via the
 // Component Updater or from enterprise policy, so this does not check
@@ -205,32 +253,24 @@
           {{primary, net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
                                              absl::nullopt)}});
 
-  // Confirm that the associatedSites field is present, and is an array of
-  // strings.
-  const base::Value::List* maybe_associated_sites_list =
-      set_declaration.FindList(kFirstPartySetAssociatedSitesField);
-  if (!maybe_associated_sites_list)
-    return base::unexpected(ParseError::kInvalidType);
-
-  if (maybe_associated_sites_list->empty())
-    return base::unexpected(ParseError::kSingletonSet);
-
-  // Add each associated site to our mapping (after validating).
-  uint32_t index = 0;
-  for (const auto& item : *maybe_associated_sites_list) {
-    base::expected<net::SchemefulSite, ParseError> site_or_error =
-        ParseSiteAndValidate(item, set_entries, elements);
-    if (!site_or_error.has_value()) {
-      return base::unexpected(site_or_error.error());
+  for (const SubsetDescriptor& descriptor : {
+           SubsetDescriptor{
+               .field_name = kFirstPartySetAssociatedSitesField,
+              .site_type = net::SiteType::kAssociated,
+              .keep_indices = true,
+           },
+           {
+               .field_name = kFirstPartySetServiceSitesField,
+              .site_type = net::SiteType::kService,
+              .keep_indices = false,
+           },
+       }) {
+    if (absl::optional<ParseError> error =
+            ParseSubset(set_declaration, primary, keep_indices, descriptor,
+                        elements, set_entries);
+        error.has_value()) {
+      return base::unexpected(error.value());
     }
-    set_entries.emplace_back(
-        site_or_error.value(),
-        net::FirstPartySetEntry(
-            primary, net::SiteType::kAssociated,
-            keep_indices
-                ? absl::make_optional(net::FirstPartySetEntry::SiteIndex(index))
-                : absl::nullopt));
-    ++index;
   }
 
   const base::expected<Aliases, ParseError> aliases_or_error =
@@ -240,6 +280,9 @@
 
   const Aliases& aliases = aliases_or_error.value();
 
+  if (IsSingletonSet(set_entries, aliases))
+    return base::unexpected(ParseError::kSingletonSet);
+
   for (const std::pair<net::SchemefulSite, net::FirstPartySetEntry>&
            site_and_entry : set_entries) {
     bool inserted = elements.insert(site_and_entry.first).second;
diff --git a/content/browser/first_party_sets/first_party_set_parser_unittest.cc b/content/browser/first_party_sets/first_party_set_parser_unittest.cc
index f5eda97..37b79ea5 100644
--- a/content/browser/first_party_sets/first_party_set_parser_unittest.cc
+++ b/content/browser/first_party_sets/first_party_set_parser_unittest.cc
@@ -56,7 +56,7 @@
   EXPECT_THAT(ParseSets(input), Pair(IsEmpty(), IsEmpty()));
 }
 
-TEST(FirstPartySetParser, AcceptsMinimal) {
+TEST(FirstPartySetParser, AcceptsMinimal_Associated) {
   const std::string input =
       R"({"primary": "https://example.test", "associatedSites": ["https://aaaa.test"]})";
 
@@ -75,6 +75,55 @@
            IsEmpty()));
 }
 
+TEST(FirstPartySetParser, AcceptsMinimal_Service) {
+  std::istringstream stream(
+      R"({"primary": "https://example.test", "serviceSites": ["https://aaaa.test"]})");
+  EXPECT_THAT(
+      FirstPartySetParser::ParseSetsFromStream(stream),
+      Pair(UnorderedElementsAre(
+               Pair(SerializesTo("https://example.test"),
+                    net::FirstPartySetEntry(
+                        net::SchemefulSite(GURL("https://example.test")),
+                        net::SiteType::kPrimary, absl::nullopt)),
+               Pair(SerializesTo("https://aaaa.test"),
+                    net::FirstPartySetEntry(
+                        net::SchemefulSite(GURL("https://example.test")),
+                        net::SiteType::kService, absl::nullopt))),
+           IsEmpty()));
+}
+
+TEST(FirstPartySetParser, AcceptsMinimal_AllSubsets_WithCcTLDs) {
+  net::SchemefulSite example(GURL("https://example.test"));
+  net::SchemefulSite example_cctld(GURL("https://example.cctld"));
+  net::SchemefulSite a(GURL("https://a.test"));
+  net::SchemefulSite a_cctld(GURL("https://a.cctld"));
+  net::SchemefulSite b(GURL("https://b.test"));
+  net::SchemefulSite b_cctld(GURL("https://b.cctld"));
+
+  std::istringstream stream(
+      R"({"primary": "https://example.test",)"
+      R"("associatedSites": ["https://a.test"],)"
+      R"("serviceSites": ["https://b.test"],)"
+      R"("ccTLDs": {)"
+      R"("https://example.test": ["https://example.cctld"],)"
+      R"("https://a.test": ["https://a.cctld"],)"
+      R"("https://b.test": ["https://b.cctld"])"
+      R"(})"
+      R"(})");
+  EXPECT_THAT(
+      FirstPartySetParser::ParseSetsFromStream(stream),
+      Pair(UnorderedElementsAre(
+               Pair(example,
+                    net::FirstPartySetEntry(example, net::SiteType::kPrimary,
+                                            absl::nullopt)),
+               Pair(a, net::FirstPartySetEntry(example,
+                                               net::SiteType::kAssociated, 0)),
+               Pair(b, net::FirstPartySetEntry(example, net::SiteType::kService,
+                                               absl::nullopt))),
+           UnorderedElementsAre(Pair(example_cctld, example), Pair(a_cctld, a),
+                                Pair(b_cctld, b))));
+}
+
 TEST(FirstPartySetParser, RejectsMissingPrimary) {
   const std::string input = R"({"associatedSites": ["https://aaaa.test"]})";
 
@@ -667,27 +716,6 @@
 }
 
 TEST(FirstPartySets_ParseSetsFromEnterprisePolicyTest,
-     InvalidTypeError_MissingAssociatedSites) {
-  base::Value policy_value = base::JSONReader::Read(R"(
-              {
-                "replacements": [
-                  {
-                    "primary": "https://primary1.test"
-                  }
-                ],
-                "additions": []
-              }
-            )")
-                                 .value();
-  EXPECT_THAT(
-      FirstPartySetParser::ParseSetsFromEnterprisePolicy(policy_value.GetDict())
-          .error(),
-      FirstPartySetParser::PolicyParsingError(
-          {FirstPartySetParser::ParseError::kInvalidType,
-           FirstPartySetParser::PolicySetType::kReplacement, 0}));
-}
-
-TEST(FirstPartySets_ParseSetsFromEnterprisePolicyTest,
      InvalidTypeError_WrongPrimaryType) {
   base::Value policy_value = base::JSONReader::Read(R"(
               {
diff --git a/content/browser/indexed_db/indexed_db_internals_ui.cc b/content/browser/indexed_db/indexed_db_internals_ui.cc
index 070c49f..10dada8 100644
--- a/content/browser/indexed_db/indexed_db_internals_ui.cc
+++ b/content/browser/indexed_db/indexed_db_internals_ui.cc
@@ -13,7 +13,8 @@
 #include "base/task/thread_pool.h"
 #include "base/threading/platform_thread.h"
 #include "base/values.h"
-#include "content/grit/dev_ui_content_resources.h"
+#include "content/grit/indexed_db_resources.h"
+#include "content/grit/indexed_db_resources_map.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -45,11 +46,9 @@
       network::mojom::CSPDirectiveName::TrustedTypes,
       "trusted-types jstemplate;");
   source->UseStringsJs();
-  source->AddResourcePath("indexeddb_internals.js",
-                          IDR_INDEXED_DB_INTERNALS_JS);
-  source->AddResourcePath("indexeddb_internals.css",
-                          IDR_INDEXED_DB_INTERNALS_CSS);
-  source->SetDefaultResource(IDR_INDEXED_DB_INTERNALS_HTML);
+  source->AddResourcePaths(
+      base::make_span(kIndexedDbResources, kIndexedDbResourcesSize));
+  source->AddResourcePath("", IDR_INDEXED_DB_INDEXEDDB_INTERNALS_HTML);
 }
 
 IndexedDBInternalsUI::~IndexedDBInternalsUI() = default;
@@ -84,26 +83,25 @@
   BrowserContext* browser_context =
       web_ui()->GetWebContents()->GetBrowserContext();
 
-  browser_context->ForEachStoragePartition(
-      base::BindRepeating(
-          [](base::WeakPtr<IndexedDBInternalsHandler> handler,
-             StoragePartition* partition) {
-            if (!handler)
-              return;
-            auto& control = partition->GetIndexedDBControl();
-            control.GetAllBucketsDetails(base::BindOnce(
-                [](base::WeakPtr<IndexedDBInternalsHandler> handler,
-                   base::FilePath partition_path, bool incognito,
-                   base::Value::List info_list) {
-                  if (!handler)
-                    return;
+  browser_context->ForEachStoragePartition(base::BindRepeating(
+      [](base::WeakPtr<IndexedDBInternalsHandler> handler,
+         StoragePartition* partition) {
+        if (!handler)
+          return;
+        auto& control = partition->GetIndexedDBControl();
+        control.GetAllBucketsDetails(base::BindOnce(
+            [](base::WeakPtr<IndexedDBInternalsHandler> handler,
+               base::FilePath partition_path, bool incognito,
+               base::Value::List info_list) {
+              if (!handler)
+                return;
 
-                  handler->OnBucketsReady(
-                      info_list, incognito ? base::FilePath() : partition_path);
-                },
-                handler, partition->GetPath()));
-          },
-          weak_factory_.GetWeakPtr()));
+              handler->OnBucketsReady(
+                  info_list, incognito ? base::FilePath() : partition_path);
+            },
+            handler, partition->GetPath()));
+      },
+      weak_factory_.GetWeakPtr()));
 }
 
 void IndexedDBInternalsHandler::OnBucketsReady(
diff --git a/content/browser/interest_group/ad_auction_service_impl.cc b/content/browser/interest_group/ad_auction_service_impl.cc
index 86699ed..8960f02 100644
--- a/content/browser/interest_group/ad_auction_service_impl.cc
+++ b/content/browser/interest_group/ad_auction_service_impl.cc
@@ -508,7 +508,8 @@
           contributions;
       contributions.push_back(std::move(request->contribution));
       remote->SendHistogramReport(std::move(contributions),
-                                  request->aggregation_mode);
+                                  request->aggregation_mode,
+                                  std::move(request->debug_mode_details));
     }
   }
 }
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index a1cb1153..88ec907 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -38,6 +38,7 @@
 #include "content/browser/interest_group/interest_group_storage.h"
 #include "content/common/aggregatable_report.mojom-shared.h"
 #include "content/common/private_aggregation_features.h"
+#include "content/common/private_aggregation_host.mojom.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/services/auction_worklet/auction_v8_helper.h"
@@ -113,7 +114,8 @@
             content::mojom::AggregatableReportHistogramContribution::New(
                 /*bucket=*/1,
                 /*value=*/2),
-            content::mojom::AggregationServiceMode::kDefault);
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New());
 
 const auction_worklet::mojom::PrivateAggregationRequestPtr
     kExpectedReportWinPrivateAggregationRequest =
@@ -121,7 +123,8 @@
             content::mojom::AggregatableReportHistogramContribution::New(
                 /*bucket=*/3,
                 /*value=*/4),
-            content::mojom::AggregationServiceMode::kDefault);
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New());
 
 const auction_worklet::mojom::PrivateAggregationRequestPtr
     kExpectedScoreAdPrivateAggregationRequest =
@@ -129,7 +132,8 @@
             content::mojom::AggregatableReportHistogramContribution::New(
                 /*bucket=*/5,
                 /*value=*/6),
-            content::mojom::AggregationServiceMode::kDefault);
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New());
 
 const auction_worklet::mojom::PrivateAggregationRequestPtr
     kExpectedReportResultPrivateAggregationRequest =
@@ -137,7 +141,8 @@
             content::mojom::AggregatableReportHistogramContribution::New(
                 /*bucket=*/7,
                 /*value=*/8),
-            content::mojom::AggregationServiceMode::kDefault);
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New());
 
 // Helper to avoid excess boilerplate.
 template <typename... Ts>
diff --git a/content/browser/preloading/speculation_rules/README.md b/content/browser/preloading/speculation_rules/README.md
index adf2230..c30a3d7 100644
--- a/content/browser/preloading/speculation_rules/README.md
+++ b/content/browser/preloading/speculation_rules/README.md
@@ -1 +1 @@
-See [third_party/blink/renderer/core/speculation_rules/README.md](../../../third_party/blink/renderer/core/speculation_rules/README.md)
\ No newline at end of file
+See [third_party/blink/renderer/core/speculation_rules/README.md](../../../../third_party/blink/renderer/core/speculation_rules/README.md)
diff --git a/content/browser/private_aggregation/private_aggregation_host.cc b/content/browser/private_aggregation/private_aggregation_host.cc
index 2f5a1a57..6f573e1 100644
--- a/content/browser/private_aggregation/private_aggregation_host.cc
+++ b/content/browser/private_aggregation/private_aggregation_host.cc
@@ -81,7 +81,8 @@
 void PrivateAggregationHost::SendHistogramReport(
     std::vector<mojom::AggregatableReportHistogramContributionPtr>
         contribution_ptrs,
-    mojom::AggregationServiceMode aggregation_mode) {
+    mojom::AggregationServiceMode aggregation_mode,
+    mojom::DebugModeDetailsPtr debug_mode_details) {
   // TODO(alexmt): Consider updating or making a FeatureParam.
   static constexpr char kFledgeReportingPath[] =
       "/.well-known/private-aggregation/report-fledge";
@@ -103,6 +104,7 @@
       contribution_ptrs,
       [](const mojom::AggregatableReportHistogramContributionPtr&
              contribution_ptr) { return contribution_ptr.is_null(); }));
+  DCHECK(!debug_mode_details.is_null());
 
   if (contribution_ptrs.size() > kMaxNumberOfContributions) {
     // TODO(crbug.com/1323324): Add histograms for monitoring failures here,
@@ -128,7 +130,9 @@
       /*scheduled_report_time=*/GetScheduledReportTime(
           /*report_issued_time=*/now),
       /*report_id=*/base::GUID::GenerateRandomV4(), reporting_origin,
-      AggregatableReportSharedInfo::DebugMode::kDisabled,
+      debug_mode_details->is_enabled
+          ? AggregatableReportSharedInfo::DebugMode::kEnabled
+          : AggregatableReportSharedInfo::DebugMode::kDisabled,
       /*additional_fields=*/base::Value::Dict(),
       /*api_version=*/kApiReportVersion,
       /*api_identifier=*/kApiIdentifier);
@@ -143,10 +147,19 @@
       break;
   }
 
+  absl::optional<uint64_t> debug_key;
+  if (!debug_mode_details->debug_key.is_null()) {
+    if (!debug_mode_details->is_enabled) {
+      mojo::ReportBadMessage("Debug key present but debug mode is not enabled");
+      return;
+    }
+    debug_key = debug_mode_details->debug_key->value;
+  }
+
   absl::optional<AggregatableReportRequest> report_request =
       AggregatableReportRequest::Create(std::move(payload_contents),
                                         std::move(shared_info),
-                                        std::move(reporting_path));
+                                        std::move(reporting_path), debug_key);
   if (!report_request.has_value()) {
     // TODO(crbug.com/1323324): Add histograms for monitoring failures here,
     // possibly broken out by failure reason.
diff --git a/content/browser/private_aggregation/private_aggregation_host.h b/content/browser/private_aggregation/private_aggregation_host.h
index 019f273..3eebc7b 100644
--- a/content/browser/private_aggregation/private_aggregation_host.h
+++ b/content/browser/private_aggregation/private_aggregation_host.h
@@ -11,6 +11,7 @@
 #include "base/memory/raw_ref.h"
 #include "content/browser/private_aggregation/private_aggregation_budget_key.h"
 #include "content/common/content_export.h"
+#include "content/common/private_aggregation_host.mojom-forward.h"
 #include "content/common/private_aggregation_host.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -65,7 +66,8 @@
   void SendHistogramReport(
       std::vector<mojom::AggregatableReportHistogramContributionPtr>
           contributions,
-      mojom::AggregationServiceMode aggregation_mode) override;
+      mojom::AggregationServiceMode aggregation_mode,
+      mojom::DebugModeDetailsPtr debug_mode_details) override;
 
  private:
   struct ReceiverContext;
diff --git a/content/browser/private_aggregation/private_aggregation_host_unittest.cc b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
index a16e9a5..04ca3017 100644
--- a/content/browser/private_aggregation/private_aggregation_host_unittest.cc
+++ b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
@@ -88,7 +88,8 @@
   contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
       /*bucket=*/123, /*value=*/456));
   remote->SendHistogramReport(std::move(contributions),
-                              mojom::AggregationServiceMode::kDefault);
+                              mojom::AggregationServiceMode::kDefault,
+                              mojom::DebugModeDetails::New());
 
   remote.FlushForTesting();
   EXPECT_TRUE(remote.is_connected());
@@ -154,7 +155,8 @@
     contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
         /*bucket=*/123, /*value=*/456));
     remotes[i]->SendHistogramReport(std::move(contributions),
-                                    mojom::AggregationServiceMode::kDefault);
+                                    mojom::AggregationServiceMode::kDefault,
+                                    mojom::DebugModeDetails::New());
 
     remotes[i].FlushForTesting();
     EXPECT_TRUE(remotes[i].is_connected());
@@ -168,6 +170,61 @@
             "/.well-known/private-aggregation/report-shared-storage");
 }
 
+TEST_F(PrivateAggregationHostTest, DebugModeDetails_ReflectedInReport) {
+  const url::Origin kExampleOrigin =
+      url::Origin::Create(GURL("https://example.com"));
+  const url::Origin kMainFrameOrigin =
+      url::Origin::Create(GURL("https://main_frame.com"));
+
+  std::vector<mojom::DebugModeDetailsPtr> debug_mode_details_args;
+  debug_mode_details_args.push_back(mojom::DebugModeDetails::New());
+  debug_mode_details_args.push_back(
+      mojom::DebugModeDetails::New(/*is_enabled=*/true, /*debug_key=*/nullptr));
+  debug_mode_details_args.push_back(mojom::DebugModeDetails::New(
+      /*is_enabled=*/true,
+      /*debug_key=*/mojom::DebugKey::New(/*value=*/1234u)));
+
+  mojo::Remote<mojom::PrivateAggregationHost> remote;
+  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
+                                     PrivateAggregationBudgetKey::Api::kFledge,
+                                     remote.BindNewPipeAndPassReceiver()));
+
+  std::vector<absl::optional<AggregatableReportRequest>> validated_requests{
+      /*n=*/3};
+  EXPECT_CALL(mock_callback_, Run)
+      .WillOnce(MoveArg<0>(&validated_requests[0]))
+      .WillOnce(MoveArg<0>(&validated_requests[1]))
+      .WillOnce(MoveArg<0>(&validated_requests[2]));
+
+  for (auto& debug_mode_details_arg : debug_mode_details_args) {
+    std::vector<mojom::AggregatableReportHistogramContributionPtr>
+        contributions;
+    contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
+        /*bucket=*/123, /*value=*/456));
+    remote->SendHistogramReport(std::move(contributions),
+                                mojom::AggregationServiceMode::kDefault,
+                                debug_mode_details_arg->Clone());
+  }
+
+  remote.FlushForTesting();
+  EXPECT_TRUE(remote.is_connected());
+
+  ASSERT_TRUE(validated_requests[0].has_value());
+  ASSERT_TRUE(validated_requests[1].has_value());
+  ASSERT_TRUE(validated_requests[2].has_value());
+
+  EXPECT_EQ(validated_requests[0]->shared_info().debug_mode,
+            AggregatableReportSharedInfo::DebugMode::kDisabled);
+  EXPECT_EQ(validated_requests[1]->shared_info().debug_mode,
+            AggregatableReportSharedInfo::DebugMode::kEnabled);
+  EXPECT_EQ(validated_requests[2]->shared_info().debug_mode,
+            AggregatableReportSharedInfo::DebugMode::kEnabled);
+
+  EXPECT_EQ(validated_requests[0]->debug_key(), absl::nullopt);
+  EXPECT_EQ(validated_requests[1]->debug_key(), absl::nullopt);
+  EXPECT_EQ(validated_requests[2]->debug_key(), 1234u);
+}
+
 TEST_F(PrivateAggregationHostTest,
        MultipleReceievers_SendHistogramReportCallsRoutedCorrectly) {
   const url::Origin kExampleOriginA =
@@ -226,7 +283,8 @@
     contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
         /*bucket=*/1, /*value=*/123));
     remotes[1]->SendHistogramReport(std::move(contributions),
-                                    mojom::AggregationServiceMode::kDefault);
+                                    mojom::AggregationServiceMode::kDefault,
+                                    mojom::DebugModeDetails::New());
   }
 
   {
@@ -235,7 +293,8 @@
     contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
         /*bucket=*/2, /*value=*/123));
     remotes[2]->SendHistogramReport(std::move(contributions),
-                                    mojom::AggregationServiceMode::kDefault);
+                                    mojom::AggregationServiceMode::kDefault,
+                                    mojom::DebugModeDetails::New());
   }
 
   for (auto& remote : remotes) {
@@ -268,7 +327,8 @@
   contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
       /*bucket=*/123, /*value=*/456));
   remote_1->SendHistogramReport(std::move(contributions),
-                                mojom::AggregationServiceMode::kDefault);
+                                mojom::AggregationServiceMode::kDefault,
+                                mojom::DebugModeDetails::New());
 
   // Flush to ensure disconnection and the SendHistogramReport call have had
   // time to be processed.
@@ -305,11 +365,24 @@
             /*bucket=*/123, /*value=*/1));
   }
 
+  std::vector<mojom::AggregatableReportHistogramContributionPtr>
+      valid_contributions;
+  valid_contributions.push_back(
+      mojom::AggregatableReportHistogramContribution::New(
+          /*bucket=*/123, /*value=*/456));
+
   EXPECT_CALL(mock_callback_, Run(_, _)).Times(0);
   remote->SendHistogramReport(std::move(negative_contributions),
-                              mojom::AggregationServiceMode::kDefault);
+                              mojom::AggregationServiceMode::kDefault,
+                              mojom::DebugModeDetails::New());
   remote->SendHistogramReport(std::move(too_many_contributions),
-                              mojom::AggregationServiceMode::kDefault);
+                              mojom::AggregationServiceMode::kDefault,
+                              mojom::DebugModeDetails::New());
+  remote->SendHistogramReport(
+      std::move(valid_contributions), mojom::AggregationServiceMode::kDefault,
+      // Debug mode must be enabled for a debug key to be set.
+      mojom::DebugModeDetails::New(
+          /*is_enabled=*/false, /*debug_key=*/mojom::DebugKey::New(1234u)));
   remote.FlushForTesting();
 }
 
@@ -337,7 +410,8 @@
   contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
       /*bucket=*/123, /*value=*/456));
   remote->SendHistogramReport(std::move(contributions),
-                              mojom::AggregationServiceMode::kDefault);
+                              mojom::AggregationServiceMode::kDefault,
+                              mojom::DebugModeDetails::New());
 
   remote.FlushForTesting();
   EXPECT_TRUE(remote.is_connected());
@@ -367,7 +441,8 @@
   contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
       /*bucket=*/123, /*value=*/456));
   remote->SendHistogramReport(std::move(contributions),
-                              mojom::AggregationServiceMode::kDefault);
+                              mojom::AggregationServiceMode::kDefault,
+                              mojom::DebugModeDetails::New());
 
   remote.FlushForTesting();
   EXPECT_TRUE(remote.is_connected());
diff --git a/content/browser/private_aggregation/private_aggregation_test_utils.h b/content/browser/private_aggregation/private_aggregation_test_utils.h
index 0d1633f..c881d5c 100644
--- a/content/browser/private_aggregation/private_aggregation_test_utils.h
+++ b/content/browser/private_aggregation/private_aggregation_test_utils.h
@@ -14,6 +14,7 @@
 #include "content/browser/private_aggregation/private_aggregation_host.h"
 #include "content/browser/private_aggregation/private_aggregation_manager.h"
 #include "content/common/aggregatable_report.mojom-forward.h"
+#include "content/common/private_aggregation_host.mojom-forward.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/test/test_content_browser_client.h"
@@ -69,7 +70,8 @@
   MOCK_METHOD(void,
               SendHistogramReport,
               (std::vector<mojom::AggregatableReportHistogramContributionPtr>,
-               mojom::AggregationServiceMode),
+               mojom::AggregationServiceMode,
+               mojom::DebugModeDetailsPtr),
               (override));
 
  private:
diff --git a/content/browser/renderer_host/popup_menu_helper_mac.h b/content/browser/renderer_host/popup_menu_helper_mac.h
index 0aef65fec..769a4b693 100644
--- a/content/browser/renderer_host/popup_menu_helper_mac.h
+++ b/content/browser/renderer_host/popup_menu_helper_mac.h
@@ -69,15 +69,14 @@
   // Immediately return from ShowPopupMenu.
   CONTENT_EXPORT static void DontShowPopupMenuForTesting();
 
- protected:
-  virtual RenderWidgetHostViewMac* GetRenderWidgetHostView() const;
-
  private:
   // RenderWidgetHostObserver implementation:
   void RenderWidgetHostVisibilityChanged(RenderWidgetHost* widget_host,
                                          bool became_visible) override;
   void RenderWidgetHostDestroyed(RenderWidgetHost* widget_host) override;
 
+  RenderWidgetHostViewMac* GetRenderWidgetHostView() const;
+
   raw_ptr<Delegate> delegate_;  // Weak. Owns |this|.
 
   base::ScopedObservation<RenderWidgetHost, RenderWidgetHostObserver>
diff --git a/content/browser/renderer_host/popup_menu_helper_mac.mm b/content/browser/renderer_host/popup_menu_helper_mac.mm
index 067bb7f..c50caf4d 100644
--- a/content/browser/renderer_host/popup_menu_helper_mac.mm
+++ b/content/browser/renderer_host/popup_menu_helper_mac.mm
@@ -63,8 +63,7 @@
   // dealloced if my Destroy() method is called while the pop-up's up (which
   // would in turn delete me, causing a crash once the -runMenuInView
   // call returns. That's what was happening in <http://crbug.com/33250>).
-  RenderWidgetHostViewMac* rwhvm =
-      static_cast<RenderWidgetHostViewMac*>(GetRenderWidgetHostView());
+  RenderWidgetHostViewMac* rwhvm = GetRenderWidgetHostView();
   base::scoped_nsobject<RenderWidgetHostViewCocoa> cocoa_view(
       [rwhvm->GetInProcessNSView() retain]);
 
@@ -144,11 +143,7 @@
 
 RenderWidgetHostViewMac* PopupMenuHelper::GetRenderWidgetHostView() const {
   return static_cast<RenderWidgetHostViewMac*>(
-      render_frame_host_->frame_tree_node()
-          ->frame_tree()
-          ->root()
-          ->current_frame_host()
-          ->GetView());
+      render_frame_host_->GetOutermostMainFrameOrEmbedder()->GetView());
 }
 
 void PopupMenuHelper::RenderWidgetHostVisibilityChanged(
diff --git a/content/browser/resources/BUILD.gn b/content/browser/resources/BUILD.gn
index a8a3cc7..82158def 100644
--- a/content/browser/resources/BUILD.gn
+++ b/content/browser/resources/BUILD.gn
@@ -3,5 +3,8 @@
 # found in the LICENSE file.
 
 group("resources") {
-  public_deps = [ "quota:resources" ]
+  public_deps = [
+    "indexed_db:resources",
+    "quota:resources",
+  ]
 }
diff --git a/content/browser/resources/indexed_db/BUILD.gn b/content/browser/resources/indexed_db/BUILD.gn
new file mode 100644
index 0000000..6837120
--- /dev/null
+++ b/content/browser/resources/indexed_db/BUILD.gn
@@ -0,0 +1,34 @@
+# 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.
+import("//chrome/common/features.gni")
+import("//tools/grit/grit_rule.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+grit("resources") {
+  defines = chrome_grit_defines
+
+  # These arguments are needed since the grd is generated at build time.
+  enable_input_discovery_for_gn_analyze = false
+  source = "$target_gen_dir/resources.grd"
+  deps = [ ":build_grd" ]
+
+  outputs = [
+    "grit/indexed_db_resources.h",
+    "grit/indexed_db_resources_map.cc",
+    "grit/indexed_db_resources_map.h",
+    "indexed_db_resources.pak",
+  ]
+  output_dir = "$root_gen_dir/content"
+}
+
+generate_grd("build_grd") {
+  grd_prefix = "indexed_db"
+  out_grd = "$target_gen_dir/resources.grd"
+  input_files_base_dir = rebase_path(".", "//")
+  input_files = [
+    "indexeddb_internals.css",
+    "indexeddb_internals.html",
+    "indexeddb_internals.js",
+  ]
+}
diff --git a/content/browser/ssl/ssl_manager.cc b/content/browser/ssl/ssl_manager.cc
index 712e286..5723eab 100644
--- a/content/browser/ssl/ssl_manager.cc
+++ b/content/browser/ssl/ssl_manager.cc
@@ -54,6 +54,7 @@
 };
 
 void OnAllowCertificate(SSLErrorHandler* handler,
+                        StoragePartition* storage_partition,
                         SSLHostStateDelegate* state_delegate,
                         bool record_decision,
                         CertificateRequestResultType decision) {
@@ -73,10 +74,7 @@
       if (record_decision && state_delegate) {
         state_delegate->AllowCert(handler->request_url().host(),
                                   *handler->ssl_info().cert.get(),
-                                  handler->cert_error(),
-                                  handler->web_contents()
-                                      ->GetPrimaryMainFrame()
-                                      ->GetStoragePartition());
+                                  handler->cert_error(), storage_partition);
       }
       handler->ContinueRequest();
       return;
@@ -347,9 +345,7 @@
     judgment = ssl_host_state_delegate_->QueryPolicy(
         handler->request_url().host(), *handler->ssl_info().cert.get(),
         handler->cert_error(),
-        // TODO(crbug/1353781): Avoid WebContents for MPArch GuestView. Get
-        // StoragePartition from navi controller's frame tree instead.
-        handler->web_contents()->GetPrimaryMainFrame()->GetStoragePartition());
+        controller_->frame_tree().GetMainFrame()->GetStoragePartition());
   } else {
     judgment = SSLHostStateDelegate::DENIED;
   }
@@ -376,11 +372,8 @@
   // any previous decisions that have occurred.
   if (!ssl_host_state_delegate_ ||
       !ssl_host_state_delegate_->HasAllowException(
-          // TODO(crbug/1353781): Avoid WebContents for MPArch GuestView. Get
-          // StoragePartition from navi controller's frame tree instead.
-          host, controller_->DeprecatedGetWebContents()
-                    ->GetPrimaryMainFrame()
-                    ->GetStoragePartition())) {
+          host,
+          controller_->frame_tree().GetMainFrame()->GetStoragePartition())) {
     return;
   }
 
@@ -400,9 +393,10 @@
   bool fatal = handler->fatal();
 
   base::RepeatingCallback<void(bool, content::CertificateRequestResultType)>
-      callback = base::BindRepeating(&OnAllowCertificate,
-                                     base::Owned(handler.release()),
-                                     ssl_host_state_delegate_);
+      callback = base::BindRepeating(
+          &OnAllowCertificate, base::Owned(handler.release()),
+          controller_->frame_tree().GetMainFrame()->GetStoragePartition(),
+          ssl_host_state_delegate_);
 
   if (devtools_instrumentation::HandleCertificateError(
           web_contents, cert_error, request_url,
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 8f0afba..c31e6e14 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -8787,14 +8787,6 @@
   NotifyPreferencesChanged();
 }
 
-void WebContentsImpl::BrowserPluginGuestWillDetach() {
-  OPTIONAL_TRACE_EVENT0("content",
-                        "WebContentsImpl::BrowserPluginGuestWillDetach");
-  WebContentsImpl* outermost = GetOutermostWebContents();
-  if (this != outermost && ContainsOrIsFocusedWebContents())
-    outermost->SetAsFocusedWebContentsIfNecessary();
-}
-
 PictureInPictureResult WebContentsImpl::EnterPictureInPicture() {
   OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterPictureInPicture");
   return delegate_ ? delegate_->EnterPictureInPicture(this)
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 345eba7..b6669772 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1173,10 +1173,6 @@
   // contained within it (but not owned by another WebContents).
   void SetFocusedFrameTree(FrameTree* frame_tree_to_focus);
 
-  // Called by this WebContents's BrowserPluginGuest (if one exists) to indicate
-  // that the guest will be detached.
-  void BrowserPluginGuestWillDetach();
-
   // Notifies the Picture-in-Picture controller that there is a new video player
   // entering video Picture-in-Picture. (This is not used for document
   // Picture-in-Picture,
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index 94c5c6f..2f21d9c6 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -1461,7 +1461,7 @@
                 : AttestationErasureOption::kEraseAttestationButIncludeAaguid;
       } else if (attestation ==
                      device::AttestationConveyancePreference::kNone &&
-                 response_data->IsSelfAttestation()) {
+                 response_data->attestation_object.IsSelfAttestation()) {
         attestation_erasure = AttestationErasureOption::kIncludeAttestation;
       } else if (attestation ==
                  device::AttestationConveyancePreference::kNone) {
@@ -1476,8 +1476,7 @@
       if (attestation_erasure ==
               AttestationErasureOption::kEraseAttestationAndAaguid &&
           device_public_key_output.has_value() &&
-          !response_data->attestation_object()
-               .authenticator_data()
+          !response_data->attestation_object.authenticator_data()
                .attested_data()
                ->IsAaguidZero()) {
         // Zeroing the AAGUID would invalidate the DPK signature. The
@@ -1552,7 +1551,8 @@
   // the authenticator: If an RP sees a "none" attestation from Chrome after
   // requesting direct attestation then it knows that it was one of the
   // tokens with inappropriate certs.
-  if (response_data.IsAttestationCertificateInappropriatelyIdentifying() &&
+  if (response_data.attestation_object
+          .IsAttestationCertificateInappropriatelyIdentifying() &&
       !GetWebAuthenticationDelegate()->ShouldPermitIndividualAttestation(
           GetBrowserContext(), caller_origin_, relying_party_id_)) {
     // The attestation response is incorrectly individually identifiable, but
@@ -1799,13 +1799,13 @@
   auto common_info = blink::mojom::CommonCredentialInfo::New();
   common_info->client_data_json.assign(client_data_json_.begin(),
                                        client_data_json_.end());
-  common_info->raw_id = response_data.attestation_object().GetCredentialId();
+  common_info->raw_id = response_data.attestation_object.GetCredentialId();
   common_info->id = Base64UrlEncode(common_info->raw_id);
 
   response->authenticator_attachment =
-      response_data.transport_used()
+      response_data.transport_used
           ? device::AuthenticatorAttachmentFromTransport(
-                *response_data.transport_used())
+                *response_data.transport_used)
           : device::AuthenticatorAttachment::kAny;
 
   base::flat_set<device::FidoTransportProtocol> transports;
@@ -1813,8 +1813,8 @@
   // considered to be sufficient complete to report back to the website.
   bool transports_authoritative = false;
 
-  if (response_data.transport_used()) {
-    transports.insert(*response_data.transport_used());
+  if (response_data.transport_used) {
+    transports.insert(*response_data.transport_used);
   }
   if (response_data.transports) {
     transports.insert(response_data.transports->begin(),
@@ -1823,8 +1823,7 @@
   }
   // Also include any transports from the attestation certificate.
   absl::optional<base::span<const uint8_t>> leaf_cert =
-      response_data.attestation_object()
-          .attestation_statement()
+      response_data.attestation_object.attestation_statement()
           .GetLeafCertificate();
   if (leaf_cert) {
     transports_authoritative |=
@@ -1841,7 +1840,7 @@
   bool did_store_cred_blob = false;
   absl::optional<std::vector<uint8_t>> device_public_key_authenticator_output;
   const absl::optional<cbor::Value>& maybe_extensions =
-      response_data.attestation_object().authenticator_data().extensions();
+      response_data.attestation_object.authenticator_data().extensions();
   if (maybe_extensions) {
     DCHECK(maybe_extensions->is_map());
     const cbor::Value::MapValue& extensions = maybe_extensions->GetMap();
@@ -1889,7 +1888,7 @@
       case RequestExtension::kLargeBlobEnable:
         response->echo_large_blob = true;
         response->supports_large_blob =
-            response_data.large_blob_key().has_value();
+            response_data.large_blob_key.has_value();
         break;
       case RequestExtension::kCredBlob:
         response->echo_cred_blob = true;
@@ -1927,25 +1926,25 @@
     case AttestationErasureOption::kIncludeAttestation:
       break;
     case AttestationErasureOption::kEraseAttestationButIncludeAaguid:
-      response_data.EraseAttestationStatement(
+      response_data.attestation_object.EraseAttestationStatement(
           device::AttestationObject::AAGUID::kInclude);
       break;
     case AttestationErasureOption::kEraseAttestationAndAaguid:
-      response_data.EraseAttestationStatement(
+      response_data.attestation_object.EraseAttestationStatement(
           device::AttestationObject::AAGUID::kErase);
       break;
   }
   response->attestation_object =
       response_data.GetCBOREncodedAttestationObject();
-  common_info->authenticator_data = response_data.attestation_object()
-                                        .authenticator_data()
-                                        .SerializeToByteArray();
+  common_info->authenticator_data =
+      response_data.attestation_object.authenticator_data()
+          .SerializeToByteArray();
   response->info = std::move(common_info);
 
-  const device::PublicKey* public_key = response_data.attestation_object()
-                                            .authenticator_data()
-                                            .attested_data()
-                                            ->public_key();
+  const device::PublicKey* public_key =
+      response_data.attestation_object.authenticator_data()
+          .attested_data()
+          ->public_key();
   response->public_key_algo = public_key->algorithm;
   const absl::optional<std::vector<uint8_t>>& public_key_der =
       public_key->der_bytes;
diff --git a/content/browser/webid/fake_identity_request_dialog_controller.cc b/content/browser/webid/fake_identity_request_dialog_controller.cc
index 7eff310..9471d1f 100644
--- a/content/browser/webid/fake_identity_request_dialog_controller.cc
+++ b/content/browser/webid/fake_identity_request_dialog_controller.cc
@@ -15,6 +15,7 @@
 
 void FakeIdentityRequestDialogController::ShowAccountsDialog(
     content::WebContents* rp_web_contents,
+    const std::string& rp_for_display,
     const std::vector<content::IdentityProviderData>& identity_provider_data,
     IdentityRequestAccount::SignInMode sign_in_mode,
     AccountSelectionCallback on_selected,
diff --git a/content/browser/webid/fake_identity_request_dialog_controller.h b/content/browser/webid/fake_identity_request_dialog_controller.h
index 35489e6..dfd9c88 100644
--- a/content/browser/webid/fake_identity_request_dialog_controller.h
+++ b/content/browser/webid/fake_identity_request_dialog_controller.h
@@ -24,6 +24,7 @@
 
   void ShowAccountsDialog(
       content::WebContents* rp_web_contents,
+      const std::string& rp_for_display,
       const std::vector<content::IdentityProviderData>& identity_provider_data,
       IdentityRequestAccount::SignInMode sign_in_mode,
       AccountSelectionCallback on_selected,
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index e5b18eb..366c319 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -14,11 +14,11 @@
 #include "base/strings/string_piece.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
+#include "components/url_formatter/elide_url.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/webid/fake_identity_request_dialog_controller.h"
 #include "content/browser/webid/flags.h"
-#include "content/browser/webid/webid_utils.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/federated_identity_active_session_permission_context_delegate.h"
@@ -29,8 +29,9 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/page_visibility_state.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "net/base/url_util.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
 #include "ui/accessibility/ax_mode.h"
 #include "url/url_constants.h"
@@ -282,6 +283,23 @@
   return kMaxRejectionTime * base::RandDouble();
 }
 
+std::string FormatUrlForDisplay(const GURL& url) {
+  // We do not use url_formatter::FormatUrlForSecurityDisplay() directly because
+  // our UI intentionally shows only the eTLD+1, as it makes for a shorter text
+  // that is also clearer to users. The identity provider's root manifest is in
+  // the root of the eTLD+1, and sign-in status within identity provider and
+  // relying party can be domain-wide because it relies on cookies.
+  std::string formatted_url_str =
+      net::IsLocalhost(url)
+          ? url.host()
+          : net::registry_controlled_domains::GetDomainAndRegistry(
+                url,
+                net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+  return base::UTF16ToUTF8(url_formatter::FormatUrlForSecurityDisplay(
+      GURL(url.scheme() + "://" + formatted_url_str),
+      url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS));
+}
+
 bool ShouldFailIfNotSignedInWithIdp(
     const GURL& idp_url,
     FederatedIdentitySharingPermissionContextDelegate*
@@ -805,7 +823,9 @@
   DCHECK(render_frame_host().GetMainFrame()->IsInPrimaryMainFrame());
 
   request_dialog_controller_->ShowFailureDialog(
-      rp_web_contents, idp_url,
+      rp_web_contents,
+      FormatUrlForDisplay(rp_web_contents->GetLastCommittedURL()),
+      FormatUrlForDisplay(idp_url),
       base::BindOnce(
           &FederatedAuthRequestImpl::OnDismissFailureDialog,
           weak_ptr_factory_.GetWeakPtr(), FederatedAuthRequestResult::kError,
@@ -883,13 +903,17 @@
                                                    start_time_);
 
       std::vector<IdentityProviderData> identity_providers_data;
-      IdentityProviderData identity_provider_data(idp_info.provider.config_url,
-                                                  accounts, *idp_info.metadata,
-                                                  client_id_data);
+
+      std::string idp_for_display =
+          FormatUrlForDisplay(idp_info.provider.config_url);
+      IdentityProviderData identity_provider_data(
+          idp_for_display, accounts, *idp_info.metadata, client_id_data);
       identity_providers_data.push_back(identity_provider_data);
 
+      std::string rp_url_for_display =
+          FormatUrlForDisplay(rp_web_contents->GetLastCommittedURL());
       request_dialog_controller_->ShowAccountsDialog(
-          rp_web_contents, identity_providers_data,
+          rp_web_contents, rp_url_for_display, identity_providers_data,
           is_auto_sign_in ? SignInMode::kAuto : SignInMode::kExplicit,
           base::BindOnce(&FederatedAuthRequestImpl::OnAccountSelected,
                          weak_ptr_factory_.GetWeakPtr(),
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 14978833..5c44a59 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
-#include "base/strings/string_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
@@ -29,7 +28,6 @@
 #include "content/public/browser/browser_accessibility_state.h"
 #include "content/public/browser/identity_request_dialog_controller.h"
 #include "content/public/common/content_features.h"
-#include "content/public/test/render_frame_host_test_support.h"
 #include "content/test/test_render_frame_host.h"
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_web_contents.h"
@@ -38,7 +36,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
 #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
@@ -767,9 +764,11 @@
         // even though the bit is set we may not exercise the AutoSignIn flow.
         // e.g. for sign up flow, multiple accounts, user opt-out etc. In this
         // case, it's up to the test to expect this mock function call.
-        EXPECT_CALL(*mock_dialog_controller_, ShowAccountsDialog(_, _, _, _, _))
+        EXPECT_CALL(*mock_dialog_controller_,
+                    ShowAccountsDialog(_, _, _, _, _, _))
             .WillOnce(Invoke(
                 [&](content::WebContents* rp_web_contents,
+                    const std::string& rp_for_display,
                     const std::vector<IdentityProviderData>&
                         identity_provider_data,
                     SignInMode sign_in_mode,
@@ -789,7 +788,8 @@
                 }));
       }
     } else {
-      EXPECT_CALL(*mock_dialog_controller_, ShowAccountsDialog(_, _, _, _, _))
+      EXPECT_CALL(*mock_dialog_controller_,
+                  ShowAccountsDialog(_, _, _, _, _, _))
           .Times(0);
     }
   }
@@ -1295,9 +1295,10 @@
                            OriginFromString(kProviderUrlFull), kAccountId))
       .WillOnce(Return(true));
 
-  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _))
+  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _, _))
       .WillOnce(Invoke(
           [&](content::WebContents* rp_web_contents,
+              const std::string& rp_for_display,
               const std::vector<IdentityProviderData>& identity_provider_data,
               SignInMode sign_in_mode,
               IdentityRequestDialogController::AccountSelectionCallback
@@ -1327,9 +1328,10 @@
       {{features::kFedCmAutoSigninFieldTrialParamName, "true"}});
 
   AccountList displayed_accounts;
-  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _))
+  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _, _))
       .WillOnce(Invoke(
           [&](content::WebContents* rp_web_contents,
+              const std::string& rp_for_display,
               const std::vector<IdentityProviderData>& identity_provider_data,
               SignInMode sign_in_mode,
               IdentityRequestDialogController::AccountSelectionCallback
@@ -1369,9 +1371,10 @@
                            OriginFromString(kProviderUrlFull), kAccountId))
       .WillOnce(Return(true));
 
-  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _))
+  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _, _))
       .WillOnce(Invoke(
           [&](content::WebContents* rp_web_contents,
+              const std::string& rp_for_display,
               const std::vector<IdentityProviderData>& identity_provider_data,
               SignInMode sign_in_mode,
               IdentityRequestDialogController::AccountSelectionCallback
@@ -1439,9 +1442,10 @@
   base::HistogramTester histogram_tester_;
 
   AccountList displayed_accounts;
-  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _))
+  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _, _))
       .WillOnce(Invoke(
           [&](content::WebContents* rp_web_contents,
+              const std::string& rp_for_display,
               const std::vector<IdentityProviderData>& identity_provider_data,
               SignInMode sign_in_mode,
               IdentityRequestDialogController::AccountSelectionCallback
@@ -1501,9 +1505,10 @@
   EXPECT_CALL(*mock_dialog_controller(), DestructorCalled()).Times(0);
 
   AccountList displayed_accounts;
-  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _))
+  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _, _))
       .WillOnce(Invoke(
           [&](content::WebContents* rp_web_contents,
+              const std::string& rp_for_display,
               const std::vector<IdentityProviderData>& identity_provider_data,
               SignInMode sign_in_mode,
               IdentityRequestDialogController::AccountSelectionCallback
@@ -1792,9 +1797,10 @@
   MockConfiguration configuration = kConfigurationValid;
   configuration.customized_dialog = true;
 
-  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _))
+  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _, _))
       .WillOnce(Invoke(
           [&](content::WebContents* rp_web_contents,
+              const std::string& rp_for_display,
               const std::vector<IdentityProviderData>& identity_provider_data,
               SignInMode sign_in_mode,
               IdentityRequestDialogController::AccountSelectionCallback
@@ -1889,9 +1895,10 @@
 TEST_F(FederatedAuthRequestImplTest, ApiDisabledAfterAccountsDialogShown) {
   base::HistogramTester histogram_tester_;
 
-  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _))
+  EXPECT_CALL(*mock_dialog_controller(), ShowAccountsDialog(_, _, _, _, _, _))
       .WillOnce(Invoke(
           [&](content::WebContents* rp_web_contents,
+              const std::string& rp_for_display,
               const std::vector<IdentityProviderData>& identity_provider_data,
               SignInMode sign_in_mode,
               IdentityRequestDialogController::AccountSelectionCallback
@@ -2042,7 +2049,7 @@
       std::make_unique<IdpNetworkRequestManagerAccountListTaskRunner>(
           base::BindOnce(&NavigateToUrl, web_contents(), GURL(kRpOtherUrl))));
 
-  EXPECT_CALL(*mock_dialog_controller_, ShowAccountsDialog(_, _, _, _, _))
+  EXPECT_CALL(*mock_dialog_controller_, ShowAccountsDialog(_, _, _, _, _, _))
       .Times(0);
   MockConfiguration configuration = kConfigurationValid;
   configuration.customized_dialog = true;
@@ -2067,7 +2074,7 @@
       std::make_unique<IdpNetworkRequestManagerAccountListTaskRunner>(
           base::BindOnce(&NavigateToUrl, web_contents(), GURL(kRpOtherUrl))));
 
-  EXPECT_CALL(*mock_dialog_controller_, ShowAccountsDialog(_, _, _, _, _))
+  EXPECT_CALL(*mock_dialog_controller_, ShowAccountsDialog(_, _, _, _, _, _))
       .Times(0);
   MockConfiguration configuration = kConfigurationValid;
   configuration.customized_dialog = true;
@@ -2135,7 +2142,7 @@
   EXPECT_CALL(*mock_sharing_permission_delegate_,
               SetIdpSigninStatus(OriginFromString(kProviderUrlFull), false))
       .Times(1);
-  EXPECT_CALL(*mock_dialog_controller_, ShowFailureDialog(_, _, _)).Times(0);
+  EXPECT_CALL(*mock_dialog_controller_, ShowFailureDialog(_, _, _, _)).Times(0);
   MockConfiguration configuration = kConfigurationValid;
   configuration.accounts_response = FetchStatus::kInvalidResponseError;
   RequestExpectations expectations = {
@@ -2154,9 +2161,10 @@
       features::kFedCm,
       {{features::kFedCmIdpSigninStatusFieldTrialParamName, "true"}});
 
-  EXPECT_CALL(*mock_dialog_controller_, ShowFailureDialog(_, _, _))
+  EXPECT_CALL(*mock_dialog_controller_, ShowFailureDialog(_, _, _, _))
       .WillOnce(
-          Invoke([&](content::WebContents* rp_web_contents, const GURL& idp_url,
+          Invoke([&](content::WebContents* rp_web_contents,
+                     const std::string& rp_url, const std::string& idp_url,
                      IdentityRequestDialogController::DismissCallback
                          dismiss_callback) {
             std::move(dismiss_callback).Run(DismissReason::CLOSE_BUTTON);
@@ -2189,7 +2197,7 @@
               GetIdpSigninStatus(OriginFromString(kProviderUrlFull)))
       .WillOnce(Return(false));
 
-  EXPECT_CALL(*mock_dialog_controller_, ShowFailureDialog(_, _, _)).Times(0);
+  EXPECT_CALL(*mock_dialog_controller_, ShowFailureDialog(_, _, _, _)).Times(0);
   MockConfiguration configuration = kConfigurationValid;
   RequestExpectations expectations = {RequestTokenStatus::kError,
                                       FederatedAuthRequestResult::kError,
diff --git a/content/browser/webid/test/mock_identity_request_dialog_controller.h b/content/browser/webid/test/mock_identity_request_dialog_controller.h
index b9f6f43..e38a35ac 100644
--- a/content/browser/webid/test/mock_identity_request_dialog_controller.h
+++ b/content/browser/webid/test/mock_identity_request_dialog_controller.h
@@ -7,7 +7,6 @@
 
 #include "content/public/browser/identity_request_dialog_controller.h"
 
-#include "base/containers/span.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace content {
@@ -24,15 +23,19 @@
   MockIdentityRequestDialogController& operator=(
       const MockIdentityRequestDialogController&) = delete;
 
-  MOCK_METHOD5(ShowAccountsDialog,
+  MOCK_METHOD6(ShowAccountsDialog,
                void(WebContents*,
+                    const std::string&,
                     const std::vector<content::IdentityProviderData>&,
                     IdentityRequestAccount::SignInMode,
                     AccountSelectionCallback,
                     DismissCallback));
   MOCK_METHOD0(DestructorCalled, void());
-  MOCK_METHOD3(ShowFailureDialog,
-               void(WebContents*, const GURL&, DismissCallback));
+  MOCK_METHOD4(ShowFailureDialog,
+               void(WebContents*,
+                    const std::string&,
+                    const std::string&,
+                    DismissCallback));
 };
 
 }  // namespace content
diff --git a/content/common/private_aggregation_host.mojom b/content/common/private_aggregation_host.mojom
index df9a20c..0bb39da 100644
--- a/content/common/private_aggregation_host.mojom
+++ b/content/common/private_aggregation_host.mojom
@@ -6,6 +6,20 @@
 
 import "content/common/aggregatable_report.mojom";
 
+// Wrapper to allow for a null debug key.
+struct DebugKey {
+  uint64 value;
+};
+
+struct DebugModeDetails {
+  // Whether debug mode is enabled.
+  bool is_enabled = false;
+
+  // Must be null if the debug mode is disabled. Can still be null even if the
+  // debug mode is enabled.
+  DebugKey? debug_key;
+};
+
 // Interface implemented in the browser for worklets and renderers to forward
 // histogram report requests.
 interface PrivateAggregationHost {
@@ -14,5 +28,6 @@
   // are controllable by the worklet/renderer.
   SendHistogramReport(
       array<AggregatableReportHistogramContribution> contributions,
-      AggregationServiceMode aggregation_mode);
+      AggregationServiceMode aggregation_mode,
+      DebugModeDetails debug_mode_details);
 };
diff --git a/content/dev_ui_content_resources.grd b/content/dev_ui_content_resources.grd
index f75bbe67..8550341 100644
--- a/content/dev_ui_content_resources.grd
+++ b/content/dev_ui_content_resources.grd
@@ -39,9 +39,6 @@
       <include name="IDR_GPU_INTERNALS_INFO_VIEW_TABLE_ROW_HTML_JS" file="${root_gen_dir}/content/browser/resources/gpu/info_view_table_row.html.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_GPU_INTERNALS_INFO_VIEW_TABLE_ROW_JS" file="browser/resources/gpu/info_view_table_row.js" type="BINDATA" />
       <include name="IDR_GPU_VULKAN_INFO_JS" file="browser/resources/gpu/vulkan_info.js" type="BINDATA" />
-      <include name="IDR_INDEXED_DB_INTERNALS_HTML" file="browser/resources/indexed_db/indexeddb_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
-      <include name="IDR_INDEXED_DB_INTERNALS_JS" file="browser/resources/indexed_db/indexeddb_internals.js" type="BINDATA" />
-      <include name="IDR_INDEXED_DB_INTERNALS_CSS" file="browser/resources/indexed_db/indexeddb_internals.css" type="BINDATA" />
       <include name="IDR_NETWORK_ERROR_LISTING_HTML" file="browser/resources/net/network_errors_listing.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_NETWORK_ERROR_LISTING_JS" file="browser/resources/net/network_errors_listing.js" type="BINDATA" />
       <include name="IDR_NETWORK_ERROR_LISTING_CSS" file="browser/resources/net/network_errors_listing.css" type="BINDATA" />
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index fae7d53..bed6cfc0 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -197,7 +197,6 @@
     "gpu_service_registry.h",
     "gpu_utils.cc",
     "gpu_utils.h",
-    "guest_host.h",
     "hid_chooser.h",
     "hid_delegate.h",
     "histogram_fetcher.h",
diff --git a/content/public/browser/browser_plugin_guest_delegate.h b/content/public/browser/browser_plugin_guest_delegate.h
index 5830f50..8e4cb67 100644
--- a/content/public/browser/browser_plugin_guest_delegate.h
+++ b/content/public/browser/browser_plugin_guest_delegate.h
@@ -10,8 +10,6 @@
 
 namespace content {
 
-class GuestHost;
-
 // Objects implement this interface to get notified about changes in the guest
 // WebContents and to provide necessary functionality.
 class CONTENT_EXPORT BrowserPluginGuestDelegate {
@@ -23,10 +21,6 @@
 
   // Returns the WebContents that currently owns this guest.
   virtual WebContents* GetOwnerWebContents();
-
-  // Provides the delegate with an interface with which to communicate with the
-  // content module.
-  virtual void SetGuestHost(GuestHost* guest_host) {}
 };
 
 }  // namespace content
diff --git a/content/public/browser/guest_host.h b/content/public/browser/guest_host.h
deleted file mode 100644
index 632ceb65..0000000
--- a/content/public/browser/guest_host.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2015 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 CONTENT_PUBLIC_BROWSER_GUEST_HOST_H_
-#define CONTENT_PUBLIC_BROWSER_GUEST_HOST_H_
-
-namespace content {
-
-// A GuestHost is the content API for a guest WebContents.
-// Guests are top-level frames that can be embedded within other pages.
-// The content module manages routing of input events and compositing, but all
-// other operations are managed outside of content. To limit exposure of
-// implementation details within content, content embedders must use this
-// interface for loading, sizing, and cleanup of guests.
-//
-// This class currently only serves as a base class for BrowserPluginGuest, and
-// its API can only be accessed by a BrowserPluginGuestDelegate.
-class GuestHost {
- public:
-  // Called when the GuestHost is about to be destroyed.
-  virtual void WillDestroy() = 0;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_BROWSER_GUEST_HOST_H_
diff --git a/content/public/browser/identity_request_dialog_controller.cc b/content/public/browser/identity_request_dialog_controller.cc
index 169bbf1..6fb1e88 100644
--- a/content/public/browser/identity_request_dialog_controller.cc
+++ b/content/public/browser/identity_request_dialog_controller.cc
@@ -52,11 +52,11 @@
     const IdentityProviderMetadata& other) = default;
 
 IdentityProviderData::IdentityProviderData(
-    const GURL& idp_config_url,
+    const std::string& idp_for_display,
     const std::vector<IdentityRequestAccount>& accounts,
     const IdentityProviderMetadata& idp_metadata,
     const ClientIdData& client_id_data)
-    : idp_config_url{idp_config_url},
+    : idp_for_display{idp_for_display},
       accounts{accounts},
       idp_metadata{idp_metadata},
       client_id_data{client_id_data} {}
@@ -75,6 +75,7 @@
 
 void IdentityRequestDialogController::ShowAccountsDialog(
     WebContents* rp_web_contents,
+    const std::string& rp_for_display,
     const std::vector<IdentityProviderData>& identity_provider_data,
     IdentityRequestAccount::SignInMode sign_in_mode,
     AccountSelectionCallback on_selected,
@@ -84,7 +85,8 @@
 
 void IdentityRequestDialogController::ShowFailureDialog(
     WebContents* rp_web_contents,
-    const GURL& idp_for_display,
+    const std::string& rp_for_display,
+    const std::string& idp_for_display,
     DismissCallback dismiss_callback) {
   std::move(dismiss_callback).Run(DismissReason::OTHER);
 }
diff --git a/content/public/browser/identity_request_dialog_controller.h b/content/public/browser/identity_request_dialog_controller.h
index 8a46355..e1a5daf9 100644
--- a/content/public/browser/identity_request_dialog_controller.h
+++ b/content/public/browser/identity_request_dialog_controller.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/containers/span.h"
 #include "content/common/content_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -86,14 +85,14 @@
 };
 
 struct CONTENT_EXPORT IdentityProviderData {
-  IdentityProviderData(const GURL& idp_config_url,
+  IdentityProviderData(const std::string& idp_url_for_display,
                        const std::vector<IdentityRequestAccount>& accounts,
                        const IdentityProviderMetadata& idp_metadata,
                        const ClientIdData& client_id_data);
   IdentityProviderData(const IdentityProviderData& other);
   ~IdentityProviderData();
 
-  GURL idp_config_url;
+  std::string idp_for_display;
   std::vector<IdentityRequestAccount> accounts;
   IdentityProviderMetadata idp_metadata;
   ClientIdData client_id_data;
@@ -144,6 +143,7 @@
   // |sign_in_mode| represents whether this is an auto sign in flow.
   virtual void ShowAccountsDialog(
       WebContents* rp_web_contents,
+      const std::string& rp_for_display,
       const std::vector<IdentityProviderData>& identity_provider_data,
       IdentityRequestAccount::SignInMode sign_in_mode,
       AccountSelectionCallback on_selected,
@@ -153,7 +153,8 @@
   // observable by users. This could happen when an IDP claims that the user is
   // signed in but not respond with any user account during browser fetches.
   virtual void ShowFailureDialog(WebContents* rp_web_contents,
-                                 const GURL& idp_for_display,
+                                 const std::string& rp_for_display,
+                                 const std::string& idp_for_display,
                                  DismissCallback dismiss_callback);
 };
 
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 26883bb..1e9d65b 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1077,6 +1077,10 @@
 const base::Feature kV8VmFuture{"V8VmFuture",
                                 base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables experimental JavaScript shared memory features.
+const base::Feature kJavaScriptExperimentalSharedMemory{
+    "JavaScriptExperimentalSharedMemory", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable window controls overlays for desktop PWAs
 const base::Feature kWebAppWindowControlsOverlay{
     "WebAppWindowControlsOverlay", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 305a739..5deb1dc 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -279,6 +279,7 @@
 CONTENT_EXPORT extern const base::Feature kVerifyDidCommitParams;
 CONTENT_EXPORT extern const base::Feature kVideoPlaybackQuality;
 CONTENT_EXPORT extern const base::Feature kV8VmFuture;
+CONTENT_EXPORT extern const base::Feature kJavaScriptExperimentalSharedMemory;
 CONTENT_EXPORT extern const base::Feature kWebAppWindowControlsOverlay;
 CONTENT_EXPORT extern const base::Feature kWebAssemblyBaseline;
 CONTENT_EXPORT extern const base::Feature kWebAssemblyCodeProtection;
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index 7e2661b..c00f1b3d 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -127,6 +127,9 @@
   SetV8FlagIfHasSwitch(switches::kEnableExperimentalWebAssemblyFeatures,
                        "--wasm-staging");
 
+  SetV8FlagIfFeature(features::kJavaScriptExperimentalSharedMemory,
+                     "--shared-string-table --harmony-struct");
+
   SetV8FlagIfFeature(features::kV8VmFuture, "--future");
   SetV8FlagIfNotFeature(features::kV8VmFuture, "--no-future");
 
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index a982da5..166eea1d 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -4569,13 +4569,15 @@
           content::mojom::AggregatableReportHistogramContribution::New(
               /*bucket=*/123,
               /*value=*/45),
-          content::mojom::AggregationServiceMode::kDefault);
+          content::mojom::AggregationServiceMode::kDefault,
+          content::mojom::DebugModeDetails::New());
   auction_worklet::mojom::PrivateAggregationRequestPtr kExpectedRequest2 =
       auction_worklet::mojom::PrivateAggregationRequest::New(
           content::mojom::AggregatableReportHistogramContribution::New(
               /*bucket=*/absl::MakeInt128(/*high=*/1, /*low=*/0),
               /*value=*/1),
-          content::mojom::AggregationServiceMode::kDefault);
+          content::mojom::AggregationServiceMode::kDefault,
+          content::mojom::DebugModeDetails::New());
 
   {
     PrivateAggregationRequests expected_pa_requests;
@@ -4693,6 +4695,93 @@
         /*expected_set_priority=*/absl::nullopt,
         std::move(expected_pa_requests));
   }
+
+  // Debug mode enabled with debug key
+  {
+    PrivateAggregationRequests expected_pa_requests;
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest1->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, content::mojom::DebugKey::New(1234u))));
+
+    RunGenerateBidWithJavascriptExpectingResult(
+        CreateGenerateBidScript(
+            R"({ad: "ad", bid:1, render:"https://response.test/" })",
+            /*extra_code=*/R"(
+            privateAggregation.enableDebugMode({debug_key: 1234});
+            privateAggregation.sendHistogramReport({bucket: 123, value: 45});
+          )"),
+        /*expected_bid=*/
+        mojom::BidderWorkletBid::New(
+            "\"ad\"", 1, GURL("https://response.test/"),
+            /*ad_components=*/absl::nullopt, base::TimeDelta()),
+        /*expected_data_version=*/absl::nullopt,
+        /*expected_errors=*/{},
+        /*expected_debug_loss_report_url=*/absl::nullopt,
+        /*expected_debug_win_report_url=*/absl::nullopt,
+        /*expected_set_priority=*/absl::nullopt,
+        std::move(expected_pa_requests));
+  }
+
+  // Debug mode enabled without debug key, but with multiple requests
+  {
+    PrivateAggregationRequests expected_pa_requests;
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest1->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, /*debug_key=*/nullptr)));
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest2->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, /*debug_key=*/nullptr)));
+
+    RunGenerateBidWithJavascriptExpectingResult(
+        CreateGenerateBidScript(
+            R"({ad: "ad", bid:1, render:"https://response.test/" })",
+            /*extra_code=*/R"(
+            privateAggregation.enableDebugMode();
+            privateAggregation.sendHistogramReport({bucket: 123, value: 45});
+            privateAggregation.sendHistogramReport(
+                {bucket: 18446744073709551616n, value: 1});
+          )"),
+        /*expected_bid=*/
+        mojom::BidderWorkletBid::New(
+            "\"ad\"", 1, GURL("https://response.test/"),
+            /*ad_components=*/absl::nullopt, base::TimeDelta()),
+        /*expected_data_version=*/absl::nullopt,
+        /*expected_errors=*/{},
+        /*expected_debug_loss_report_url=*/absl::nullopt,
+        /*expected_debug_win_report_url=*/absl::nullopt,
+        /*expected_set_priority=*/absl::nullopt,
+        std::move(expected_pa_requests));
+  }
+
+  // Debug mode enabled twice
+  {
+    RunGenerateBidWithJavascriptExpectingResult(
+        CreateGenerateBidScript(
+            R"({ad: "ad", bid:1, render:"https://response.test/" })",
+            /*extra_code=*/R"(
+            privateAggregation.enableDebugMode();
+            privateAggregation.enableDebugMode();
+          )"),
+        /*expected_bid=*/
+        mojom::BidderWorkletBidPtr(),
+        /*expected_data_version=*/absl::nullopt,
+        /*expected_errors=*/
+        {"https://url.test/:6 Uncaught TypeError: enableDebugMode may be "
+         "called at most once."},
+        /*expected_debug_loss_report_url=*/absl::nullopt,
+        /*expected_debug_win_report_url=*/absl::nullopt,
+        /*expected_set_priority=*/absl::nullopt,
+        /*expected_pa_requests=*/{});
+  }
 }
 
 TEST_F(BidderWorkletPrivateAggregationEnabledTest, ReportWin) {
@@ -4701,13 +4790,15 @@
           content::mojom::AggregatableReportHistogramContribution::New(
               /*bucket=*/123,
               /*value=*/45),
-          content::mojom::AggregationServiceMode::kDefault);
+          content::mojom::AggregationServiceMode::kDefault,
+          content::mojom::DebugModeDetails::New());
   auction_worklet::mojom::PrivateAggregationRequestPtr kExpectedRequest2 =
       auction_worklet::mojom::PrivateAggregationRequest::New(
           content::mojom::AggregatableReportHistogramContribution::New(
               /*bucket=*/absl::MakeInt128(/*high=*/1, /*low=*/0),
               /*value=*/1),
-          content::mojom::AggregationServiceMode::kDefault);
+          content::mojom::AggregationServiceMode::kDefault,
+          content::mojom::DebugModeDetails::New());
 
   {
     PrivateAggregationRequests expected_pa_requests;
@@ -4784,6 +4875,54 @@
         {"https://url.test/:12 Uncaught ReferenceError: error is not "
          "defined."});
   }
+
+  // Debug mode enabled with debug key
+  {
+    PrivateAggregationRequests expected_pa_requests;
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest1->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, content::mojom::DebugKey::New(1234u))));
+
+    RunReportWinWithFunctionBodyExpectingResult(
+        R"(
+            privateAggregation.enableDebugMode({debug_key: 1234});
+            privateAggregation.sendHistogramReport({bucket: 123, value: 45});
+        )",
+        /*expected_report_url=*/absl::nullopt,
+        /*expected_ad_beacon_map=*/{}, std::move(expected_pa_requests),
+        /*expected_errors=*/{});
+  }
+
+  // Debug mode enabled without debug key, but with multiple requests
+  {
+    PrivateAggregationRequests expected_pa_requests;
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest1->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, /*debug_key=*/nullptr)));
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest2->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, /*debug_key=*/nullptr)));
+
+    RunReportWinWithFunctionBodyExpectingResult(
+        R"(
+            privateAggregation.enableDebugMode();
+            privateAggregation.sendHistogramReport({bucket: 123, value: 45});
+            privateAggregation.sendHistogramReport(
+                {bucket: 18446744073709551616n, value: 1});
+        )",
+        /*expected_report_url=*/absl::nullopt,
+        /*expected_ad_beacon_map=*/{}, std::move(expected_pa_requests),
+        /*expected_errors=*/{});
+  }
 }
 
 class BidderWorkletPrivateAggregationDisabledTest : public BidderWorkletTest {
diff --git a/content/services/auction_worklet/context_recycler_unittest.cc b/content/services/auction_worklet/context_recycler_unittest.cc
index ae9bc49..bfad4bb 100644
--- a/content/services/auction_worklet/context_recycler_unittest.cc
+++ b/content/services/auction_worklet/context_recycler_unittest.cc
@@ -16,6 +16,7 @@
 #include "content/common/aggregatable_report.mojom-shared.h"
 #include "content/common/aggregatable_report.mojom.h"
 #include "content/common/private_aggregation_features.h"
+#include "content/common/private_aggregation_host.mojom-forward.h"
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "content/services/auction_worklet/for_debugging_only_bindings.h"
 #include "content/services/auction_worklet/private_aggregation_bindings.h"
@@ -519,6 +520,15 @@
     scoped_feature_list_.InitAndEnableFeature(content::kPrivateAggregationApi);
   }
 
+  // Wraps a debug_key into the appropriate dictionary. Templated to allow both
+  // integers and strings.
+  template <typename T>
+  v8::Local<v8::Value> WrapDebugKey(T debug_key) {
+    gin::Dictionary dict = gin::Dictionary::CreateEmpty(helper_->isolate());
+    dict.Set("debug_key", debug_key);
+    return gin::ConvertToV8(helper_->isolate(), dict);
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -538,6 +548,19 @@
       }
       privateAggregation.sendHistogramReport(args);
     }
+    function enableDebugMode(arg) {
+      if (arg === undefined) {
+        privateAggregation.enableDebugMode();
+        return;
+      }
+
+      // Passing BigInts in directly is complicated so we construct them from
+      // strings.
+      if (typeof arg.debug_key === "string") {
+        arg.debug_key = BigInt(arg.debug_key);
+      }
+      privateAggregation.enableDebugMode(arg);
+    }
     function doNothing() {}
   )";
 
@@ -564,7 +587,8 @@
         expected_contribution(/*bucket=*/123, /*value=*/45);
     auction_worklet::mojom::PrivateAggregationRequest expected_request(
         expected_contribution.Clone(),
-        content::mojom::AggregationServiceMode::kDefault);
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New());
 
     PrivateAggregationRequests pa_requests =
         context_recycler.private_aggregation_bindings()
@@ -590,7 +614,8 @@
         expected_contribution(/*bucket=*/123, /*value=*/45);
     auction_worklet::mojom::PrivateAggregationRequest expected_request(
         expected_contribution.Clone(),
-        content::mojom::AggregationServiceMode::kDefault);
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New());
 
     PrivateAggregationRequests pa_requests =
         context_recycler.private_aggregation_bindings()
@@ -617,7 +642,8 @@
             /*bucket=*/absl::MakeUint128(/*high=*/1, /*low=*/0), /*value=*/45);
     auction_worklet::mojom::PrivateAggregationRequest expected_request(
         expected_contribution.Clone(),
-        content::mojom::AggregationServiceMode::kDefault);
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New());
 
     PrivateAggregationRequests pa_requests =
         context_recycler.private_aggregation_bindings()
@@ -643,7 +669,8 @@
         expected_contribution(/*bucket=*/absl::Uint128Max(), /*value=*/45);
     auction_worklet::mojom::PrivateAggregationRequest expected_request(
         expected_contribution.Clone(),
-        content::mojom::AggregationServiceMode::kDefault);
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New());
 
     PrivateAggregationRequests pa_requests =
         context_recycler.private_aggregation_bindings()
@@ -669,7 +696,8 @@
         expected_contribution(/*bucket=*/0, /*value=*/45);
     auction_worklet::mojom::PrivateAggregationRequest expected_request(
         expected_contribution.Clone(),
-        content::mojom::AggregationServiceMode::kDefault);
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New());
 
     PrivateAggregationRequests pa_requests =
         context_recycler.private_aggregation_bindings()
@@ -695,7 +723,8 @@
         expected_contribution(/*bucket=*/123, /*value=*/0);
     auction_worklet::mojom::PrivateAggregationRequest expected_request(
         expected_contribution.Clone(),
-        content::mojom::AggregationServiceMode::kDefault);
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New());
 
     PrivateAggregationRequests pa_requests =
         context_recycler.private_aggregation_bindings()
@@ -732,13 +761,15 @@
         expected_contribution_1(/*bucket=*/123, /*value=*/45);
     auction_worklet::mojom::PrivateAggregationRequest expected_request_1(
         expected_contribution_1.Clone(),
-        content::mojom::AggregationServiceMode::kDefault);
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New());
 
     content::mojom::AggregatableReportHistogramContribution
         expected_contribution_2(/*bucket=*/678, /*value=*/90);
     auction_worklet::mojom::PrivateAggregationRequest expected_request_2(
         expected_contribution_2.Clone(),
-        content::mojom::AggregationServiceMode::kDefault);
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New());
 
     PrivateAggregationRequests pa_requests =
         context_recycler.private_aggregation_bindings()
@@ -916,6 +947,301 @@
                     .empty());
   }
 
+  // Debug mode enabled with no debug key
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    Run(scope, script, "enableDebugMode", error_msgs);
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    gin::Dictionary dict = gin::Dictionary::CreateEmpty(helper_->isolate());
+    dict.Set("bucket", 123);
+    dict.Set("value", 45);
+
+    Run(scope, script, "test", error_msgs,
+        gin::ConvertToV8(helper_->isolate(), dict));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    content::mojom::AggregatableReportHistogramContribution
+        expected_contribution(
+            /*bucket=*/123, /*value=*/45);
+    auction_worklet::mojom::PrivateAggregationRequest expected_request(
+        expected_contribution.Clone(),
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New(/*is_enabled=*/true,
+                                              /*debug_key=*/nullptr));
+
+    PrivateAggregationRequests pa_requests =
+        context_recycler.private_aggregation_bindings()
+            ->TakePrivateAggregationRequests();
+    ASSERT_EQ(pa_requests.size(), 1u);
+    EXPECT_EQ(pa_requests[0], expected_request.Clone());
+  }
+
+  // Debug mode enabled with debug key
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    Run(scope, script, "enableDebugMode", error_msgs, WrapDebugKey(1234));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    gin::Dictionary dict = gin::Dictionary::CreateEmpty(helper_->isolate());
+    dict.Set("bucket", 123);
+    dict.Set("value", 45);
+
+    Run(scope, script, "test", error_msgs,
+        gin::ConvertToV8(helper_->isolate(), dict));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    content::mojom::AggregatableReportHistogramContribution
+        expected_contribution(
+            /*bucket=*/123, /*value=*/45);
+    auction_worklet::mojom::PrivateAggregationRequest expected_request(
+        expected_contribution.Clone(),
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New(
+            /*is_enabled=*/true,
+            /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+
+    PrivateAggregationRequests pa_requests =
+        context_recycler.private_aggregation_bindings()
+            ->TakePrivateAggregationRequests();
+    ASSERT_EQ(pa_requests.size(), 1u);
+    EXPECT_EQ(pa_requests[0], expected_request.Clone());
+  }
+
+  // Debug mode enabled with BigInt debug key
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    Run(scope, script, "enableDebugMode", error_msgs,
+        WrapDebugKey(std::string("18446744073709551615")));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    gin::Dictionary dict = gin::Dictionary::CreateEmpty(helper_->isolate());
+    dict.Set("bucket", 123);
+    dict.Set("value", 45);
+
+    Run(scope, script, "test", error_msgs,
+        gin::ConvertToV8(helper_->isolate(), dict));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    content::mojom::AggregatableReportHistogramContribution
+        expected_contribution(
+            /*bucket=*/123, /*value=*/45);
+    auction_worklet::mojom::PrivateAggregationRequest expected_request(
+        expected_contribution.Clone(),
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New(
+            /*is_enabled=*/true,
+            /*debug_key=*/content::mojom::DebugKey::New(
+                std::numeric_limits<uint64_t>::max())));
+
+    PrivateAggregationRequests pa_requests =
+        context_recycler.private_aggregation_bindings()
+            ->TakePrivateAggregationRequests();
+    ASSERT_EQ(pa_requests.size(), 1u);
+    EXPECT_EQ(pa_requests[0], expected_request.Clone());
+  }
+
+  // Negative debug key
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    Run(scope, script, "enableDebugMode", error_msgs, WrapDebugKey(-1));
+    EXPECT_THAT(
+        error_msgs,
+        ElementsAre("https://example.org/script.js:21 Uncaught TypeError: "
+                    "debug_key must be either a non-negative integer Number or "
+                    "BigInt."));
+
+    EXPECT_TRUE(context_recycler.private_aggregation_bindings()
+                    ->TakePrivateAggregationRequests()
+                    .empty());
+  }
+
+  // Non-integer debug key
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    Run(scope, script, "enableDebugMode", error_msgs, WrapDebugKey(1.5));
+    EXPECT_THAT(
+        error_msgs,
+        ElementsAre("https://example.org/script.js:21 Uncaught TypeError: "
+                    "debug_key must be either a non-negative integer Number or "
+                    "BigInt."));
+
+    EXPECT_TRUE(context_recycler.private_aggregation_bindings()
+                    ->TakePrivateAggregationRequests()
+                    .empty());
+  }
+
+  // Too large debug key
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    Run(scope, script, "enableDebugMode", error_msgs,
+        WrapDebugKey(std::string("18446744073709551616")));
+    EXPECT_THAT(error_msgs,
+                ElementsAre("https://example.org/script.js:21 Uncaught "
+                            "TypeError: BigInt is too large."));
+
+    EXPECT_TRUE(context_recycler.private_aggregation_bindings()
+                    ->TakePrivateAggregationRequests()
+                    .empty());
+  }
+
+  // Invalid enableDebugMode argument
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    // The debug key is not wrapped in a dictionary.
+    Run(scope, script, "enableDebugMode", error_msgs,
+        gin::ConvertToV8(helper_->isolate(), 1234));
+    EXPECT_THAT(
+        error_msgs,
+        ElementsAre("https://example.org/script.js:21 Uncaught TypeError: "
+                    "Invalid argument in enableDebugMode."));
+
+    EXPECT_TRUE(context_recycler.private_aggregation_bindings()
+                    ->TakePrivateAggregationRequests()
+                    .empty());
+  }
+
+  // enableDebugMode called twice: second call fails, first continues to apply
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    Run(scope, script, "enableDebugMode", error_msgs, WrapDebugKey(1234));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    Run(scope, script, "enableDebugMode", error_msgs);
+    EXPECT_THAT(
+        error_msgs,
+        ElementsAre("https://example.org/script.js:12 Uncaught TypeError: "
+                    "enableDebugMode may be called at most once."));
+    error_msgs.clear();
+
+    gin::Dictionary dict = gin::Dictionary::CreateEmpty(helper_->isolate());
+    dict.Set("bucket", 123);
+    dict.Set("value", 45);
+
+    Run(scope, script, "test", error_msgs,
+        gin::ConvertToV8(helper_->isolate(), dict));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    content::mojom::AggregatableReportHistogramContribution
+        expected_contribution(
+            /*bucket=*/123, /*value=*/45);
+    auction_worklet::mojom::PrivateAggregationRequest expected_request(
+        expected_contribution.Clone(),
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New(
+            /*is_enabled=*/true,
+            /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+
+    PrivateAggregationRequests pa_requests =
+        context_recycler.private_aggregation_bindings()
+            ->TakePrivateAggregationRequests();
+    ASSERT_EQ(pa_requests.size(), 1u);
+    EXPECT_EQ(pa_requests[0], expected_request.Clone());
+  }
+
+  // enableDebugMode called after report requested: debug details still applied
+  // Note that Shared Storage worklets have different behavior in this case.
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    gin::Dictionary dict = gin::Dictionary::CreateEmpty(helper_->isolate());
+    dict.Set("bucket", 123);
+    dict.Set("value", 45);
+
+    Run(scope, script, "test", error_msgs,
+        gin::ConvertToV8(helper_->isolate(), dict));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    Run(scope, script, "enableDebugMode", error_msgs, WrapDebugKey(1234));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    content::mojom::AggregatableReportHistogramContribution
+        expected_contribution(
+            /*bucket=*/123, /*value=*/45);
+    auction_worklet::mojom::PrivateAggregationRequest expected_request(
+        expected_contribution.Clone(),
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New(
+            /*is_enabled=*/true,
+            /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+
+    PrivateAggregationRequests pa_requests =
+        context_recycler.private_aggregation_bindings()
+            ->TakePrivateAggregationRequests();
+    ASSERT_EQ(pa_requests.size(), 1u);
+    EXPECT_EQ(pa_requests[0], expected_request.Clone());
+  }
+
+  // Multiple debug mode reports
+  {
+    ContextRecyclerScope scope(context_recycler);
+    std::vector<std::string> error_msgs;
+
+    Run(scope, script, "enableDebugMode", error_msgs, WrapDebugKey(1234));
+    EXPECT_THAT(error_msgs, ElementsAre());
+
+    {
+      gin::Dictionary dict_1 = gin::Dictionary::CreateEmpty(helper_->isolate());
+      dict_1.Set("bucket", 123);
+      dict_1.Set("value", 45);
+
+      Run(scope, script, "test", error_msgs,
+          gin::ConvertToV8(helper_->isolate(), dict_1));
+      EXPECT_THAT(error_msgs, ElementsAre());
+    }
+    {
+      gin::Dictionary dict_2 = gin::Dictionary::CreateEmpty(helper_->isolate());
+      dict_2.Set("bucket", 678);
+      dict_2.Set("value", 90);
+
+      Run(scope, script, "test", error_msgs,
+          gin::ConvertToV8(helper_->isolate(), dict_2));
+      EXPECT_THAT(error_msgs, ElementsAre());
+    }
+
+    content::mojom::AggregatableReportHistogramContribution
+        expected_contribution_1(/*bucket=*/123, /*value=*/45);
+    auction_worklet::mojom::PrivateAggregationRequest expected_request_1(
+        expected_contribution_1.Clone(),
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New(
+            /*is_enabled=*/true,
+            /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+
+    content::mojom::AggregatableReportHistogramContribution
+        expected_contribution_2(/*bucket=*/678, /*value=*/90);
+    auction_worklet::mojom::PrivateAggregationRequest expected_request_2(
+        expected_contribution_2.Clone(),
+        content::mojom::AggregationServiceMode::kDefault,
+        content::mojom::DebugModeDetails::New(
+            /*is_enabled=*/true,
+            /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+
+    PrivateAggregationRequests pa_requests =
+        context_recycler.private_aggregation_bindings()
+            ->TakePrivateAggregationRequests();
+    ASSERT_EQ(pa_requests.size(), 2u);
+    EXPECT_EQ(pa_requests[0], expected_request_1.Clone());
+    EXPECT_EQ(pa_requests[1], expected_request_2.Clone());
+  }
+
   // API not called
   {
     ContextRecyclerScope scope(context_recycler);
diff --git a/content/services/auction_worklet/private_aggregation_bindings.cc b/content/services/auction_worklet/private_aggregation_bindings.cc
index 86df334..dbdb2e2 100644
--- a/content/services/auction_worklet/private_aggregation_bindings.cc
+++ b/content/services/auction_worklet/private_aggregation_bindings.cc
@@ -6,11 +6,13 @@
 
 #include <stdint.h>
 
+#include <iterator>
 #include <memory>
 #include <string>
 #include <utility>
 
 #include "base/feature_list.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_util.h"
 #include "content/common/aggregatable_report.mojom.h"
 #include "content/common/private_aggregation_features.h"
@@ -70,6 +72,44 @@
   return absl::MakeUint128(words[1], words[0]);
 }
 
+// In case of failure, will return `absl::nullopt` and output an error to
+// `error_out`.
+absl::optional<uint64_t> ParseDebugKey(gin::Dictionary dict,
+                                       v8::Local<v8::Context>& context,
+                                       std::string* error_out) {
+  v8::Local<v8::Value> js_debug_key;
+
+  if (!dict.Get("debug_key", &js_debug_key) || js_debug_key.IsEmpty() ||
+      js_debug_key->IsNullOrUndefined()) {
+    return absl::nullopt;
+  }
+
+  if (js_debug_key->IsUint32()) {
+    v8::Maybe<uint32_t> maybe_debug_key = js_debug_key->Uint32Value(context);
+    if (maybe_debug_key.IsNothing()) {
+      *error_out = "Failed to interpret value as integer";
+    }
+    return maybe_debug_key.ToChecked();
+  }
+
+  if (js_debug_key->IsBigInt()) {
+    absl::optional<absl::uint128> maybe_debug_key =
+        ConvertBigIntToUint128(js_debug_key->ToBigInt(context), error_out);
+    if (!maybe_debug_key.has_value()) {
+      return absl::nullopt;
+    }
+    if (absl::Uint128High64(maybe_debug_key.value()) != 0) {
+      *error_out = "BigInt is too large";
+      return absl::nullopt;
+    }
+    return absl::Uint128Low64(maybe_debug_key.value());
+  }
+
+  *error_out =
+      "debug_key must be either a non-negative integer Number or BigInt";
+  return absl::nullopt;
+}
+
 }  // namespace
 
 PrivateAggregationBindings::PrivateAggregationBindings(
@@ -89,14 +129,24 @@
 
   v8::Local<v8::ObjectTemplate> private_aggregation_template =
       v8::ObjectTemplate::New(v8_helper_->isolate());
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(
-      v8_helper_->isolate(), &PrivateAggregationBindings::SendHistogramReport,
-      v8_this);
-  function_template->RemovePrototype();
 
+  v8::Local<v8::FunctionTemplate> send_histogram_report_template =
+      v8::FunctionTemplate::New(
+          v8_helper_->isolate(),
+          &PrivateAggregationBindings::SendHistogramReport, v8_this);
+  send_histogram_report_template->RemovePrototype();
   private_aggregation_template->Set(
       v8_helper_->CreateStringFromLiteral("sendHistogramReport"),
-      function_template);
+      send_histogram_report_template);
+
+  v8::Local<v8::FunctionTemplate> enable_debug_mode_template =
+      v8::FunctionTemplate::New(v8_helper_->isolate(),
+                                &PrivateAggregationBindings::EnableDebugMode,
+                                v8_this);
+  enable_debug_mode_template->RemovePrototype();
+  private_aggregation_template->Set(
+      v8_helper_->CreateStringFromLiteral("enableDebugMode"),
+      enable_debug_mode_template);
 
   global_template->Set(
       v8_helper_->CreateStringFromLiteral("privateAggregation"),
@@ -104,12 +154,29 @@
 }
 
 void PrivateAggregationBindings::Reset() {
-  private_aggregation_requests_.clear();
+  private_aggregation_contributions_.clear();
+  debug_mode_details_.is_enabled = false;
+  debug_mode_details_.debug_key = nullptr;
 }
 
 std::vector<auction_worklet::mojom::PrivateAggregationRequestPtr>
 PrivateAggregationBindings::TakePrivateAggregationRequests() {
-  return std::move(private_aggregation_requests_);
+  std::vector<auction_worklet::mojom::PrivateAggregationRequestPtr> requests;
+
+  requests.reserve(private_aggregation_contributions_.size());
+  base::ranges::transform(
+      private_aggregation_contributions_, std::back_inserter(requests),
+      [this](content::mojom::AggregatableReportHistogramContributionPtr&
+                 contribution) {
+        return auction_worklet::mojom::PrivateAggregationRequest::New(
+            std::move(contribution),
+            // TODO(alexmt): consider allowing this to be set
+            content::mojom::AggregationServiceMode::kDefault,
+            debug_mode_details_.Clone());
+      });
+  private_aggregation_contributions_.clear();
+
+  return requests;
 }
 
 void PrivateAggregationBindings::SendHistogramReport(
@@ -121,7 +188,8 @@
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
   AuctionV8Helper* v8_helper = bindings->v8_helper_;
 
-  if (args.Length() != 1 || args[0].IsEmpty() || !args[0]->IsObject()) {
+  // Any additional arguments are ignored.
+  if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsObject()) {
     isolate->ThrowException(
         v8::Exception::TypeError(v8_helper->CreateStringFromLiteral(
             "sendHistogramReport requires 1 object parameter")));
@@ -211,15 +279,53 @@
     return;
   }
 
-  content::mojom::AggregatableReportHistogramContributionPtr contribution =
+  bindings->private_aggregation_contributions_.push_back(
       content::mojom::AggregatableReportHistogramContribution::New(bucket,
-                                                                   value);
+                                                                   value));
+}
 
-  bindings->private_aggregation_requests_.push_back(
-      auction_worklet::mojom::PrivateAggregationRequest::New(
-          std::move(contribution),
-          // TODO(alexmt): consider allowing this to be set
-          content::mojom::AggregationServiceMode::kDefault));
+void PrivateAggregationBindings::EnableDebugMode(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  PrivateAggregationBindings* bindings =
+      static_cast<PrivateAggregationBindings*>(
+          v8::External::Cast(*args.Data())->Value());
+  v8::Isolate* isolate = args.GetIsolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  AuctionV8Helper* v8_helper = bindings->v8_helper_;
+
+  if (bindings->debug_mode_details_.is_enabled) {
+    isolate->ThrowException(
+        v8::Exception::TypeError(v8_helper->CreateStringFromLiteral(
+            "enableDebugMode may be called at most once")));
+    return;
+  }
+
+  // If no arguments are provided, no debug key is set.
+  if (args.Length() >= 1 && !args[0].IsEmpty()) {
+    gin::Dictionary dict(isolate);
+
+    if (!gin::ConvertFromV8(isolate, args[0], &dict)) {
+      isolate->ThrowException(
+          v8::Exception::TypeError(v8_helper->CreateStringFromLiteral(
+              "Invalid argument in enableDebugMode")));
+      return;
+    }
+
+    std::string error;
+    absl::optional<uint64_t> maybe_debug_key =
+        ParseDebugKey(dict, context, &error);
+    if (!maybe_debug_key.has_value()) {
+      DCHECK(base::IsStringUTF8(error));
+      isolate->ThrowException(v8::Exception::TypeError(
+          v8_helper->CreateUtf8String(error).ToLocalChecked()));
+      return;
+    }
+
+    bindings->debug_mode_details_.debug_key =
+        content::mojom::DebugKey::New(maybe_debug_key.value());
+  }
+
+  bindings->debug_mode_details_.is_enabled = true;
 }
 
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/private_aggregation_bindings.h b/content/services/auction_worklet/private_aggregation_bindings.h
index b04e844..ae9fd1c 100644
--- a/content/services/auction_worklet/private_aggregation_bindings.h
+++ b/content/services/auction_worklet/private_aggregation_bindings.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "content/common/content_export.h"
+#include "content/common/private_aggregation_host.mojom.h"
 #include "content/services/auction_worklet/context_recycler.h"
 #include "content/services/auction_worklet/public/mojom/private_aggregation_request.mojom-forward.h"
 #include "v8/include/v8-forward.h"
@@ -40,11 +41,15 @@
  private:
   static void SendHistogramReport(
       const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void EnableDebugMode(const v8::FunctionCallbackInfo<v8::Value>& args);
 
   const raw_ptr<AuctionV8Helper> v8_helper_;
 
-  std::vector<auction_worklet::mojom::PrivateAggregationRequestPtr>
-      private_aggregation_requests_;
+  // Defaults to debug mode being disabled.
+  content::mojom::DebugModeDetails debug_mode_details_;
+
+  std::vector<content::mojom::AggregatableReportHistogramContributionPtr>
+      private_aggregation_contributions_;
 };
 
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/public/mojom/private_aggregation_request.mojom b/content/services/auction_worklet/public/mojom/private_aggregation_request.mojom
index 6326a63a..62c326eb 100644
--- a/content/services/auction_worklet/public/mojom/private_aggregation_request.mojom
+++ b/content/services/auction_worklet/public/mojom/private_aggregation_request.mojom
@@ -5,10 +5,12 @@
 module auction_worklet.mojom;
 
 import "content/common/aggregatable_report.mojom";
+import "content/common/private_aggregation_host.mojom";
 
 // Represents a request made to the Private Aggregation API (e.g. via
 // `privateAggregation.sendHistogramReport()`).
 struct PrivateAggregationRequest {
   content.mojom.AggregatableReportHistogramContribution contribution;
   content.mojom.AggregationServiceMode aggregation_mode;
+  content.mojom.DebugModeDetails debug_mode_details;
 };
diff --git a/content/services/auction_worklet/seller_worklet_unittest.cc b/content/services/auction_worklet/seller_worklet_unittest.cc
index 00e1b985..7829272 100644
--- a/content/services/auction_worklet/seller_worklet_unittest.cc
+++ b/content/services/auction_worklet/seller_worklet_unittest.cc
@@ -3354,13 +3354,15 @@
           content::mojom::AggregatableReportHistogramContribution::New(
               /*bucket=*/123,
               /*value=*/45),
-          content::mojom::AggregationServiceMode::kDefault);
+          content::mojom::AggregationServiceMode::kDefault,
+          content::mojom::DebugModeDetails::New());
   auction_worklet::mojom::PrivateAggregationRequestPtr kExpectedRequest2 =
       auction_worklet::mojom::PrivateAggregationRequest::New(
           content::mojom::AggregatableReportHistogramContribution::New(
               /*bucket=*/absl::MakeInt128(/*high=*/1, /*low=*/0),
               /*value=*/1),
-          content::mojom::AggregationServiceMode::kDefault);
+          content::mojom::AggregationServiceMode::kDefault,
+          content::mojom::DebugModeDetails::New());
 
   {
     PrivateAggregationRequests expected_pa_requests;
@@ -3450,6 +3452,62 @@
         /*expected_debug_win_report_url=*/absl::nullopt,
         std::move(expected_pa_requests));
   }
+
+  // Debug mode enabled with debug key
+  {
+    PrivateAggregationRequests expected_pa_requests;
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest1->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, content::mojom::DebugKey::New(1234u))));
+
+    RunScoreAdWithJavascriptExpectingResult(
+        CreateScoreAdScript("5",
+                            R"(
+            privateAggregation.enableDebugMode({debug_key: 1234});
+            privateAggregation.sendHistogramReport({bucket: 123, value: 45});
+          )"),
+        5, /*expected_errors=*/{},
+        mojom::ComponentAuctionModifiedBidParamsPtr(),
+        /*expected_data_version=*/absl::nullopt,
+        /*expected_debug_loss_report_url=*/absl::nullopt,
+        /*expected_debug_win_report_url=*/absl::nullopt,
+        std::move(expected_pa_requests));
+  }
+
+  // Debug mode enabled without debug key, but with multiple requests
+  {
+    PrivateAggregationRequests expected_pa_requests;
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest1->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, /*debug_key=*/nullptr)));
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest2->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, /*debug_key=*/nullptr)));
+
+    RunScoreAdWithJavascriptExpectingResult(
+        CreateScoreAdScript("5",
+                            R"(
+            privateAggregation.enableDebugMode();
+            privateAggregation.sendHistogramReport({bucket: 123, value: 45});
+            privateAggregation.sendHistogramReport(
+                {bucket: 18446744073709551616n, value: 1});
+          )"),
+        5, /*expected_errors=*/{},
+        mojom::ComponentAuctionModifiedBidParamsPtr(),
+        /*expected_data_version=*/absl::nullopt,
+        /*expected_debug_loss_report_url=*/absl::nullopt,
+        /*expected_debug_win_report_url=*/absl::nullopt,
+        std::move(expected_pa_requests));
+  }
 }
 
 TEST_F(SellerWorkletPrivateAggregationEnabledTest, ReportResult) {
@@ -3458,13 +3516,15 @@
           content::mojom::AggregatableReportHistogramContribution::New(
               /*bucket=*/123,
               /*value=*/45),
-          content::mojom::AggregationServiceMode::kDefault);
+          content::mojom::AggregationServiceMode::kDefault,
+          content::mojom::DebugModeDetails::New());
   auction_worklet::mojom::PrivateAggregationRequestPtr kExpectedRequest2 =
       auction_worklet::mojom::PrivateAggregationRequest::New(
           content::mojom::AggregatableReportHistogramContribution::New(
               /*bucket=*/absl::MakeInt128(/*high=*/1, /*low=*/0),
               /*value=*/1),
-          content::mojom::AggregationServiceMode::kDefault);
+          content::mojom::AggregationServiceMode::kDefault,
+          content::mojom::DebugModeDetails::New());
 
   {
     PrivateAggregationRequests expected_pa_requests;
@@ -3545,6 +3605,74 @@
         {"https://url.test/:11 Uncaught ReferenceError: error is not "
          "defined."});
   }
+
+  // Debug mode enabled with debug key
+  {
+    PrivateAggregationRequests expected_pa_requests;
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest1->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, content::mojom::DebugKey::New(1234u))));
+
+    RunReportResultCreatedScriptExpectingResult(
+        "5",
+        R"(
+            privateAggregation.enableDebugMode({debug_key: 1234});
+            privateAggregation.sendHistogramReport({bucket: 123, value: 45});
+        )",
+        /*expected_signals_for_winner=*/"5",
+        /*expected_report_url=*/absl::nullopt, /*expected_ad_beacon_map=*/{},
+        std::move(expected_pa_requests),
+        /*expected_errors=*/{});
+  }
+
+  // Debug mode enabled without debug key, but with multiple requests
+  {
+    PrivateAggregationRequests expected_pa_requests;
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest1->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, /*debug_key=*/nullptr)));
+    expected_pa_requests.push_back(
+        auction_worklet::mojom::PrivateAggregationRequest::New(
+            kExpectedRequest2->contribution->Clone(),
+            content::mojom::AggregationServiceMode::kDefault,
+            content::mojom::DebugModeDetails::New(
+                /*is_enabled=*/true, /*debug_key=*/nullptr)));
+
+    RunReportResultCreatedScriptExpectingResult(
+        "5",
+        R"(
+            privateAggregation.enableDebugMode();
+            privateAggregation.sendHistogramReport({bucket: 123, value: 45});
+            privateAggregation.sendHistogramReport(
+                {bucket: 18446744073709551616n, value: 1});
+        )",
+        /*expected_signals_for_winner=*/"5",
+        /*expected_report_url=*/absl::nullopt, /*expected_ad_beacon_map=*/{},
+        std::move(expected_pa_requests),
+        /*expected_errors=*/{});
+  }
+
+  // Debug mode enabled twice
+  {
+    RunReportResultCreatedScriptExpectingResult(
+        "5",
+        R"(
+            privateAggregation.enableDebugMode();
+            privateAggregation.enableDebugMode();
+        )",
+        /*expected_signals_for_winner=*/absl::nullopt,
+        /*expected_report_url=*/absl::nullopt, /*expected_ad_beacon_map=*/{},
+        /*expected_pa_requests=*/{},
+        /*expected_errors=*/
+        {"https://url.test/:11 Uncaught TypeError: enableDebugMode may be "
+         "called at most once."});
+  }
 }
 
 class SellerWorkletPrivateAggregationDisabledTest : public SellerWorkletTest {
diff --git a/content/services/shared_storage_worklet/private_aggregation.cc b/content/services/shared_storage_worklet/private_aggregation.cc
index ef55901a..dbf319f 100644
--- a/content/services/shared_storage_worklet/private_aggregation.cc
+++ b/content/services/shared_storage_worklet/private_aggregation.cc
@@ -89,6 +89,44 @@
   return absl::MakeUint128(words[1], words[0]);
 }
 
+// In case of failure, will return `absl::nullopt` and output an error to
+// `error_out`.
+absl::optional<uint64_t> ParseDebugKey(gin::Dictionary dict,
+                                       v8::Local<v8::Context>& context,
+                                       std::string* error_out) {
+  v8::Local<v8::Value> js_debug_key;
+
+  if (!dict.Get("debug_key", &js_debug_key) || js_debug_key.IsEmpty() ||
+      js_debug_key->IsNullOrUndefined()) {
+    return absl::nullopt;
+  }
+
+  if (js_debug_key->IsUint32()) {
+    v8::Maybe<uint32_t> maybe_debug_key = js_debug_key->Uint32Value(context);
+    if (maybe_debug_key.IsNothing()) {
+      *error_out = "Failed to interpret value as integer";
+    }
+    return maybe_debug_key.ToChecked();
+  }
+
+  if (js_debug_key->IsBigInt()) {
+    absl::optional<absl::uint128> maybe_debug_key =
+        ConvertBigIntToUint128(js_debug_key->ToBigInt(context), error_out);
+    if (!maybe_debug_key.has_value()) {
+      return absl::nullopt;
+    }
+    if (absl::Uint128High64(maybe_debug_key.value()) != 0) {
+      *error_out = "BigInt is too large";
+      return absl::nullopt;
+    }
+    return absl::Uint128Low64(maybe_debug_key.value());
+  }
+
+  *error_out =
+      "debug_key must be either a non-negative integer Number or BigInt";
+  return absl::nullopt;
+}
+
 }  // namespace
 
 PrivateAggregation::PrivateAggregation(
@@ -104,7 +142,8 @@
     v8::Isolate* isolate) {
   return Wrappable<PrivateAggregation>::GetObjectTemplateBuilder(isolate)
       .SetMethod("sendHistogramReport",
-                 &PrivateAggregation::SendHistogramReport);
+                 &PrivateAggregation::SendHistogramReport)
+      .SetMethod("enableDebugMode", &PrivateAggregation::EnableDebugMode);
 }
 
 const char* PrivateAggregation::GetTypeName() {
@@ -112,19 +151,15 @@
 }
 
 void PrivateAggregation::SendHistogramReport(gin::Arguments* args) {
-  if (!has_recorded_use_counters_) {
-    has_recorded_use_counters_ = true;
-    client_->RecordUseCounters(
-        {blink::mojom::WebFeature::kPrivateAggregationApiAll,
-         blink::mojom::WebFeature::kPrivateAggregationApiSharedStorage});
-  }
+  EnsureUseCountersAreRecorded();
 
   v8::Isolate* isolate = args->isolate();
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   std::vector<v8::Local<v8::Value>> argument_list = args->GetAll();
 
-  if (argument_list.size() != 1 || argument_list[0].IsEmpty() ||
+  // Any additional arguments are ignored.
+  if (argument_list.size() == 0 || argument_list[0].IsEmpty() ||
       !argument_list[0]->IsObject()) {
     isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
         isolate, "sendHistogramReport requires 1 object parameter")));
@@ -217,7 +252,58 @@
   private_aggregation_host_->SendHistogramReport(
       std::move(contributions),
       // TODO(alexmt): consider allowing this to be set
-      content::mojom::AggregationServiceMode::kDefault);
+      content::mojom::AggregationServiceMode::kDefault,
+      debug_mode_details_.Clone());
+}
+
+void PrivateAggregation::EnableDebugMode(gin::Arguments* args) {
+  EnsureUseCountersAreRecorded();
+
+  v8::Isolate* isolate = args->isolate();
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+  std::vector<v8::Local<v8::Value>> argument_list = args->GetAll();
+
+  if (debug_mode_details_.is_enabled) {
+    isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
+        isolate, "enableDebugMode may be called at most once")));
+    return;
+  }
+
+  // If no arguments are provided, no debug key is set.
+  if (argument_list.size() >= 1 && !argument_list[0].IsEmpty()) {
+    gin::Dictionary dict(isolate);
+
+    if (!gin::ConvertFromV8(isolate, argument_list[0], &dict)) {
+      isolate->ThrowException(v8::Exception::TypeError(CreateStringFromLiteral(
+          isolate, "Invalid argument in enableDebugMode")));
+      return;
+    }
+
+    std::string error;
+    absl::optional<uint64_t> maybe_debug_key =
+        ParseDebugKey(dict, context, &error);
+    if (!maybe_debug_key.has_value()) {
+      DCHECK(base::IsStringUTF8(error));
+      isolate->ThrowException(v8::Exception::TypeError(
+          CreateUtf8String(isolate, error).ToLocalChecked()));
+      return;
+    }
+
+    debug_mode_details_.debug_key =
+        content::mojom::DebugKey::New(maybe_debug_key.value());
+  }
+
+  debug_mode_details_.is_enabled = true;
+}
+
+void PrivateAggregation::EnsureUseCountersAreRecorded() {
+  if (!has_recorded_use_counters_) {
+    has_recorded_use_counters_ = true;
+    client_->RecordUseCounters(
+        {blink::mojom::WebFeature::kPrivateAggregationApiAll,
+         blink::mojom::WebFeature::kPrivateAggregationApiSharedStorage});
+  }
 }
 
 }  // namespace shared_storage_worklet
diff --git a/content/services/shared_storage_worklet/private_aggregation.h b/content/services/shared_storage_worklet/private_aggregation.h
index e2eb952..2aef109 100644
--- a/content/services/shared_storage_worklet/private_aggregation.h
+++ b/content/services/shared_storage_worklet/private_aggregation.h
@@ -7,7 +7,7 @@
 
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
-#include "content/common/private_aggregation_host.mojom-forward.h"
+#include "content/common/private_aggregation_host.mojom.h"
 #include "content/common/shared_storage_worklet_service.mojom.h"
 #include "gin/object_template_builder.h"
 #include "gin/wrappable.h"
@@ -35,12 +35,18 @@
 
  private:
   void SendHistogramReport(gin::Arguments* args);
+  void EnableDebugMode(gin::Arguments* args);
+
+  void EnsureUseCountersAreRecorded();
 
   raw_ref<mojom::SharedStorageWorkletServiceClient> client_;
   raw_ref<content::mojom::PrivateAggregationHost> private_aggregation_host_;
 
   bool has_recorded_use_counters_ = false;
 
+  // Defaults to debug mode being disabled.
+  content::mojom::DebugModeDetails debug_mode_details_;
+
   base::WeakPtrFactory<PrivateAggregation> weak_ptr_factory_{this};
 };
 
diff --git a/content/services/shared_storage_worklet/shared_storage_worklet_global_scope_unittest.cc b/content/services/shared_storage_worklet/shared_storage_worklet_global_scope_unittest.cc
index 8a172da..bcd8714 100644
--- a/content/services/shared_storage_worklet/shared_storage_worklet_global_scope_unittest.cc
+++ b/content/services/shared_storage_worklet/shared_storage_worklet_global_scope_unittest.cc
@@ -249,7 +249,8 @@
       void,
       SendHistogramReport,
       (std::vector<content::mojom::AggregatableReportHistogramContributionPtr>,
-       content::mojom::AggregationServiceMode),
+       content::mojom::AggregationServiceMode,
+       content::mojom::DebugModeDetailsPtr),
       (override));
 };
 
@@ -1430,12 +1431,15 @@
           [](std::vector<
                  content::mojom::AggregatableReportHistogramContributionPtr>
                  contributions,
-             content::mojom::AggregationServiceMode aggregation_mode) {
+             content::mojom::AggregationServiceMode aggregation_mode,
+             content::mojom::DebugModeDetailsPtr debug_mode_details) {
             ASSERT_EQ(contributions.size(), 1u);
             EXPECT_EQ(contributions[0]->bucket, 1);
             EXPECT_EQ(contributions[0]->value, 2);
             EXPECT_EQ(aggregation_mode,
                       content::mojom::AggregationServiceMode::kDefault);
+            ASSERT_FALSE(debug_mode_details.is_null());
+            EXPECT_EQ(*debug_mode_details, content::mojom::DebugModeDetails());
           }));
 
   SimulateAddModule(R"(
@@ -2207,20 +2211,25 @@
     EXPECT_TRUE(error_message.empty());
   }
 
-  void ExecuteScriptAndValidateContribution(const std::string& script_body,
-                                            absl::uint128 expected_bucket,
-                                            int expected_value) {
+  void ExecuteScriptAndValidateContribution(
+      const std::string& script_body,
+      absl::uint128 expected_bucket,
+      int expected_value,
+      content::mojom::DebugModeDetailsPtr expected_debug_mode_details =
+          content::mojom::DebugModeDetails::New()) {
     EXPECT_CALL(*mock_private_aggregation_host(), SendHistogramReport)
         .WillOnce(testing::Invoke(
-            [=](std::vector<
+            [&](std::vector<
                     content::mojom::AggregatableReportHistogramContributionPtr>
                     contributions,
-                content::mojom::AggregationServiceMode aggregation_mode) {
+                content::mojom::AggregationServiceMode aggregation_mode,
+                content::mojom::DebugModeDetailsPtr debug_mode_details) {
               ASSERT_EQ(contributions.size(), 1u);
               EXPECT_EQ(contributions[0]->bucket, expected_bucket);
               EXPECT_EQ(contributions[0]->value, expected_value);
               EXPECT_EQ(aggregation_mode,
                         content::mojom::AggregationServiceMode::kDefault);
+              EXPECT_TRUE(debug_mode_details == expected_debug_mode_details);
             }));
 
     ExecuteScriptExpectNoError(script_body);
@@ -2367,23 +2376,29 @@
           [](std::vector<
                  content::mojom::AggregatableReportHistogramContributionPtr>
                  contributions,
-             content::mojom::AggregationServiceMode aggregation_mode) {
+             content::mojom::AggregationServiceMode aggregation_mode,
+             content::mojom::DebugModeDetailsPtr debug_mode_details) {
             ASSERT_EQ(contributions.size(), 1u);
             EXPECT_EQ(contributions[0]->bucket, 1);
             EXPECT_EQ(contributions[0]->value, 2);
             EXPECT_EQ(aggregation_mode,
                       content::mojom::AggregationServiceMode::kDefault);
+            ASSERT_FALSE(debug_mode_details.is_null());
+            EXPECT_EQ(*debug_mode_details, content::mojom::DebugModeDetails());
           }))
       .WillOnce(testing::Invoke(
           [](std::vector<
                  content::mojom::AggregatableReportHistogramContributionPtr>
                  contributions,
-             content::mojom::AggregationServiceMode aggregation_mode) {
+             content::mojom::AggregationServiceMode aggregation_mode,
+             content::mojom::DebugModeDetailsPtr debug_mode_details) {
             ASSERT_EQ(contributions.size(), 1u);
             EXPECT_EQ(contributions[0]->bucket, 3);
             EXPECT_EQ(contributions[0]->value, 4);
             EXPECT_EQ(aggregation_mode,
                       content::mojom::AggregationServiceMode::kDefault);
+            ASSERT_FALSE(debug_mode_details.is_null());
+            EXPECT_EQ(*debug_mode_details, content::mojom::DebugModeDetails());
           }));
 
   ExecuteScriptExpectNoError(
@@ -2393,4 +2408,163 @@
       )");
 }
 
+TEST_F(SharedStoragePrivateAggregationTest, DebugModeWithNoDebugKey) {
+  ExecuteScriptAndValidateContribution(
+      R"(
+        privateAggregation.enableDebugMode();
+        privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+      )",
+      /*expected_bucket=*/1,
+      /*expected_value=*/2,
+      /*expected_debug_mode_details=*/
+      content::mojom::DebugModeDetails::New(/*is_enabled=*/true,
+                                            /*debug_key=*/nullptr));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, DebugModeWithDebugKey) {
+  ExecuteScriptAndValidateContribution(
+      R"(
+        privateAggregation.enableDebugMode({debug_key: 1234});
+        privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+      )",
+      /*expected_bucket=*/1,
+      /*expected_value=*/2,
+      /*expected_debug_mode_details=*/
+      content::mojom::DebugModeDetails::New(
+          /*is_enabled=*/true,
+          /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, DebugModeWithBigIntDebugKey) {
+  ExecuteScriptAndValidateContribution(
+      R"(
+        privateAggregation.enableDebugMode({debug_key: 1234n});
+        privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+      )",
+      /*expected_bucket=*/1,
+      /*expected_value=*/2,
+      /*expected_debug_mode_details=*/
+      content::mojom::DebugModeDetails::New(
+          /*is_enabled=*/true,
+          /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NegativeDebugKey_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.enableDebugMode({debug_key: -1});");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: debug_key must be "
+            "either a non-negative integer Number or BigInt.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, NonIntegerDebugKey_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.enableDebugMode({debug_key: 1.5});");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: debug_key must be "
+            "either a non-negative integer Number or BigInt.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, TooLargeDebugKey_Rejected) {
+  std::string error_str = ExecuteScriptReturningError(
+      "privateAggregation.enableDebugMode({debug_key: "
+      "18446744073709551616n});");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: BigInt is too large.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest,
+       InvalidEnableDebugModeArgument_Rejected) {
+  // The debug key is not wrapped in a dictionary.
+  std::string error_str =
+      ExecuteScriptReturningError("privateAggregation.enableDebugMode(1234);");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:1 Uncaught TypeError: Invalid argument in "
+            "enableDebugMode.");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest,
+       EnableDebugModeCalledTwice_SecondCallFails) {
+  std::string error_str = ExecuteScriptReturningError(
+      R"(
+        privateAggregation.enableDebugMode({debug_key: 1234n});
+        privateAggregation.enableDebugMode();
+      )");
+
+  EXPECT_EQ(error_str,
+            "https://example.test/:3 Uncaught TypeError: enableDebugMode may "
+            "be called at most once.");
+
+  // Note that the first call still applies to future requests.
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 1, value: 2});",
+      /*expected_bucket=*/1,
+      /*expected_value=*/2,
+      /*expected_debug_mode_details=*/
+      content::mojom::DebugModeDetails::New(
+          /*is_enabled=*/true,
+          /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+}
+
+// Note that FLEDGE worklets have different behavior in this case.
+TEST_F(SharedStoragePrivateAggregationTest,
+       EnableDebugModeCalledAfterRequest_DoesntApply) {
+  ExecuteScriptAndValidateContribution(
+      "privateAggregation.sendHistogramReport({bucket: 1, value: 2});",
+      /*expected_bucket=*/1,
+      /*expected_value=*/2,
+      /*expected_debug_mode_details=*/
+      content::mojom::DebugModeDetails::New());
+
+  ExecuteScriptExpectNoError(
+      "privateAggregation.enableDebugMode({debug_key: 1234n});");
+}
+
+TEST_F(SharedStoragePrivateAggregationTest, MultipleDebugModeRequests) {
+  EXPECT_CALL(*mock_private_aggregation_host(), SendHistogramReport)
+      .WillOnce(testing::Invoke(
+          [](std::vector<
+                 content::mojom::AggregatableReportHistogramContributionPtr>
+                 contributions,
+             content::mojom::AggregationServiceMode aggregation_mode,
+             content::mojom::DebugModeDetailsPtr debug_mode_details) {
+            ASSERT_EQ(contributions.size(), 1u);
+            EXPECT_EQ(contributions[0]->bucket, 1);
+            EXPECT_EQ(contributions[0]->value, 2);
+            EXPECT_EQ(aggregation_mode,
+                      content::mojom::AggregationServiceMode::kDefault);
+            EXPECT_EQ(debug_mode_details,
+                      content::mojom::DebugModeDetails::New(
+                          /*is_enabled=*/true,
+                          /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+          }))
+      .WillOnce(testing::Invoke(
+          [](std::vector<
+                 content::mojom::AggregatableReportHistogramContributionPtr>
+                 contributions,
+             content::mojom::AggregationServiceMode aggregation_mode,
+             content::mojom::DebugModeDetailsPtr debug_mode_details) {
+            ASSERT_EQ(contributions.size(), 1u);
+            EXPECT_EQ(contributions[0]->bucket, 3);
+            EXPECT_EQ(contributions[0]->value, 4);
+            EXPECT_EQ(aggregation_mode,
+                      content::mojom::AggregationServiceMode::kDefault);
+            EXPECT_EQ(debug_mode_details,
+                      content::mojom::DebugModeDetails::New(
+                          /*is_enabled=*/true,
+                          /*debug_key=*/content::mojom::DebugKey::New(1234u)));
+          }));
+
+  ExecuteScriptExpectNoError(
+      R"(
+        privateAggregation.enableDebugMode({debug_key: 1234n});
+        privateAggregation.sendHistogramReport({bucket: 1, value: 2});
+        privateAggregation.sendHistogramReport({bucket: 3, value: 4});
+      )");
+}
+
 }  // namespace shared_storage_worklet
diff --git a/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt b/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt
index 8db1948..23c7814 100644
--- a/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt
@@ -4,4 +4,5 @@
 ++Button text:"Clicky button" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="Clicky"]
 ++TextView text:"foo" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
 ++TextView text:"bar" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
-++TextView text:"baz" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph", roleDescription="Texty"]
\ No newline at end of file
+++TextView text:"baz" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph", roleDescription="Texty"]
+++View text:"SuperContainer group" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group", roleDescription="SuperContainer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-roledescription-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-roledescription-expected-auralinux.txt
index 2f04931..0775bf42 100644
--- a/content/test/data/accessibility/aria/aria-roledescription-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-roledescription-expected-auralinux.txt
@@ -8,3 +8,5 @@
 ++++[static] name='bar'
 ++[paragraph] roledescription:Texty
 ++++[static] name='baz'
+++[panel] roledescription:SuperContainer
+++++[static] name='SuperContainer group'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-roledescription-expected-blink.txt b/content/test/data/accessibility/aria/aria-roledescription-expected-blink.txt
index 6ff8fbd..a66ad51 100644
--- a/content/test/data/accessibility/aria/aria-roledescription-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-roledescription-expected-blink.txt
@@ -19,3 +19,6 @@
 ++++++paragraph roleDescription='Texty'
 ++++++++staticText name='baz'
 ++++++++++inlineTextBox name='baz'
+++++++group roleDescription='SuperContainer'
+++++++++staticText name='SuperContainer group'
+++++++++++inlineTextBox name='SuperContainer group'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-roledescription-expected-mac.txt b/content/test/data/accessibility/aria/aria-roledescription-expected-mac.txt
index 2d3176da..d922ff0 100644
--- a/content/test/data/accessibility/aria/aria-roledescription-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-roledescription-expected-mac.txt
@@ -8,3 +8,5 @@
 ++++AXStaticText AXRoleDescription='text' AXValue='bar'
 ++AXGroup AXRoleDescription='texty'
 ++++AXStaticText AXRoleDescription='text' AXValue='baz'
+++AXGroup AXSubrole=AXApplicationGroup AXRoleDescription='supercontainer'
+++++AXStaticText AXRoleDescription='text' AXValue='SuperContainer group'
diff --git a/content/test/data/accessibility/aria/aria-roledescription-expected-uia-win.txt b/content/test/data/accessibility/aria/aria-roledescription-expected-uia-win.txt
new file mode 100644
index 0000000..b952fb5
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-roledescription-expected-uia-win.txt
@@ -0,0 +1,12 @@
+Document LocalizedControlType='document'
+++Button LocalizedControlType='button' Name='Native button'
+++Button LocalizedControlType='button' Name='ARIA button'
+++Button LocalizedControlType='Clicky' Name='Clicky button'
+++Group LocalizedControlType='group'
+++++Text LocalizedControlType='text' Name='foo'
+++Group LocalizedControlType='group' Name='bar'
+++++Text LocalizedControlType='text' Name='bar'
+++Group LocalizedControlType='Texty'
+++++Text LocalizedControlType='text' Name='baz'
+++Group LocalizedControlType='SuperContainer'
+++++Text LocalizedControlType='text' Name='SuperContainer group'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-roledescription-expected-win.txt b/content/test/data/accessibility/aria/aria-roledescription-expected-win.txt
index 69de453..8c09360 100644
--- a/content/test/data/accessibility/aria/aria-roledescription-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-roledescription-expected-win.txt
@@ -8,3 +8,5 @@
 ++++ROLE_SYSTEM_STATICTEXT name='bar'
 ++IA2_ROLE_PARAGRAPH localized_extended_role='Texty'
 ++++ROLE_SYSTEM_STATICTEXT name='baz'
+++ROLE_SYSTEM_GROUPING localized_extended_role='SuperContainer'
+++++ROLE_SYSTEM_STATICTEXT name='SuperContainer group'
diff --git a/content/test/data/accessibility/aria/aria-roledescription.html b/content/test/data/accessibility/aria/aria-roledescription.html
index 528163f..dfbe105 100644
--- a/content/test/data/accessibility/aria/aria-roledescription.html
+++ b/content/test/data/accessibility/aria/aria-roledescription.html
@@ -3,6 +3,7 @@
 @MAC-ALLOW:AXRoleDescription
 @WIN-ALLOW:localized_extended_role*
 @AURALINUX-ALLOW:roledescription*
+@UIA-WIN-ALLOW:LocalizedControlType=*
 -->
 <html>
 <body>
@@ -12,5 +13,6 @@
   <div aria-roledescription="Plain">foo</div>
   <div aria-roledescription="Focusable" tabindex="0">bar</div>
   <p aria-roledescription="Texty">baz</p>
+  <div role="group" aria-roledescription="SuperContainer">SuperContainer group</div>
 </body>
 </html>
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 578add75..3715b6c3 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -391,8 +391,6 @@
 crbug.com/1287280 [ android android-pixel-6 no-passthrough android-chromium ] Pixel_CanvasDisplaySRGBAccelerated2D [ Failure ]
 crbug.com/1288586 [ android android-pixel-6 no-passthrough renderer-skia-gl android-webview-instrumentation ] Pixel_OffscreenCanvasWebGLDefault [ Failure ]
 crbug.com/1289884 [ android android-pixel-6 renderer-skia-vulkan android-webview-instrumentation ] * [ Failure ]
-crbug.com/1356252 [ android android-pixel-6 passthrough angle-opengles ] Pixel_ScissorTestWithPreserveDrawingBuffer [ Failure ]
-crbug.com/1356252 [ android android-pixel-6 passthrough angle-opengles ] Pixel_WebGLGreenTriangle_AA_NoAlpha [ Failure ]
 
 crbug.com/1290953 [ android android-nexus-5x android-webview-instrumentation ] Pixel_CanvasLowLatency2D [ Failure ]
 crbug.com/1290953 [ android android-nexus-5x android-webview-instrumentation ] Pixel_CanvasLowLatency2DDrawImage [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index a611857..72b5dd1 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -655,8 +655,7 @@
 # This can be re-added once the general Android suppression due to DrDc (crbug.com/1289303) is removed.
 # crbug.com/1288595 [ android android-pixel-6 passthrough ] conformance/canvas/render-after-resize-test.html [ Failure ]
 crbug.com/1288603 [ android android-pixel-6 passthrough angle-opengles ] conformance/extensions/angle-instanced-arrays-out-of-bounds.html [ Failure ]
-crbug.com/1356252 [ android android-pixel-6 passthrough angle-opengles ] conformance/rendering/color-mask-preserved-during-implicit-clears.html [ Failure ]
-crbug.com/1356252 [ android android-pixel-6 passthrough angle-opengles ] conformance/state/gl-enable-enum-test.html [ Failure ]
+
 
 # Misc failures
 crbug.com/angleproject/2988 [ android angle-opengles ] conformance/context/context-size-change.html [ Failure ]
@@ -699,6 +698,7 @@
 crbug.com/1080380 [ chromeos chromeos-board-kevin ] conformance/offscreencanvas/context-lost-restored-worker.html [ Failure ]
 crbug.com/1080380 [ chromeos chromeos-board-kevin ] conformance/offscreencanvas/context-lost-restored.html [ Failure ]
 crbug.com/1108368 [ chromeos chromeos-board-kevin ] conformance/misc/shader-precision-format.html [ Failure ]
+crbug.com/1357064 [ chromeos chromeos-board-kevin no-passthrough ] conformance/rendering/blending.html [ Failure ]
 
 # Failing on chromeos-amd64-generic-rel.
 crbug.com/1232446 [ chromeos chromeos-board-amd64-generic ] conformance/rendering/gl-scissor-test.html [ Failure ]
diff --git a/content/web_test/renderer/test_runner.cc b/content/web_test/renderer/test_runner.cc
index 6691ca7..b55b8bfb 100644
--- a/content/web_test/renderer/test_runner.cc
+++ b/content/web_test/renderer/test_runner.cc
@@ -3270,6 +3270,7 @@
   // Clean out the lifecycle if needed before capturing the web tree
   // dump and pixels from the compositor.
   auto* web_frame = main_frame->GetWebFrame();
+  web_frame->FrameWidget()->PrepareForFinalLifecyclUpdateForTesting();
   web_frame->FrameWidget()->UpdateAllLifecyclePhases(
       blink::DocumentUpdateReason::kTest);
 
diff --git a/device/fido/authenticator_make_credential_response.cc b/device/fido/authenticator_make_credential_response.cc
index 852a487..3e2cd47f 100644
--- a/device/fido/authenticator_make_credential_response.cc
+++ b/device/fido/authenticator_make_credential_response.cc
@@ -63,10 +63,10 @@
 }
 
 AuthenticatorMakeCredentialResponse::AuthenticatorMakeCredentialResponse(
-    absl::optional<FidoTransportProtocol> transport_used,
-    AttestationObject attestation_object)
-    : attestation_object_(std::move(attestation_object)),
-      transport_used_(transport_used) {}
+    absl::optional<FidoTransportProtocol> in_transport_used,
+    AttestationObject in_attestation_object)
+    : attestation_object(std::move(in_attestation_object)),
+      transport_used(in_transport_used) {}
 
 AuthenticatorMakeCredentialResponse::AuthenticatorMakeCredentialResponse(
     AuthenticatorMakeCredentialResponse&& that) = default;
@@ -80,29 +80,14 @@
 
 std::vector<uint8_t>
 AuthenticatorMakeCredentialResponse::GetCBOREncodedAttestationObject() const {
-  return cbor::Writer::Write(AsCBOR(attestation_object_))
+  return cbor::Writer::Write(AsCBOR(attestation_object))
       .value_or(std::vector<uint8_t>());
 }
 
-void AuthenticatorMakeCredentialResponse::EraseAttestationStatement(
-    AttestationObject::AAGUID erase_aaguid) {
-  attestation_object_.EraseAttestationStatement(erase_aaguid);
-}
-
-bool AuthenticatorMakeCredentialResponse::IsSelfAttestation() {
-  return attestation_object_.IsSelfAttestation();
-}
-
-bool AuthenticatorMakeCredentialResponse::
-    IsAttestationCertificateInappropriatelyIdentifying() {
-  return attestation_object_
-      .IsAttestationCertificateInappropriatelyIdentifying();
-}
-
 absl::optional<device::DevicePublicKeyOutput>
 AuthenticatorMakeCredentialResponse::GetDevicePublicKeyResponse() const {
   const absl::optional<cbor::Value>& maybe_extensions =
-      attestation_object_.authenticator_data().extensions();
+      attestation_object.authenticator_data().extensions();
   if (!maybe_extensions) {
     return absl::nullopt;
   }
@@ -121,17 +106,12 @@
 
 const std::array<uint8_t, kRpIdHashLength>&
 AuthenticatorMakeCredentialResponse::GetRpIdHash() const {
-  return attestation_object_.rp_id_hash();
-}
-
-void AuthenticatorMakeCredentialResponse::set_large_blob_key(
-    const base::span<const uint8_t, kLargeBlobKeyLength> large_blob_key) {
-  large_blob_key_ = fido_parsing_utils::Materialize(large_blob_key);
+  return attestation_object.rp_id_hash();
 }
 
 std::vector<uint8_t> AsCTAPStyleCBORBytes(
     const AuthenticatorMakeCredentialResponse& response) {
-  const AttestationObject& object = response.attestation_object();
+  const AttestationObject& object = response.attestation_object;
   cbor::Value::MapValue map;
   map.emplace(1, object.attestation_statement().format_name());
   map.emplace(2, object.authenticator_data().SerializeToByteArray());
@@ -139,8 +119,8 @@
   if (response.enterprise_attestation_returned) {
     map.emplace(4, true);
   }
-  if (response.large_blob_key()) {
-    map.emplace(5, cbor::Value(*response.large_blob_key()));
+  if (response.large_blob_key) {
+    map.emplace(5, cbor::Value(*response.large_blob_key));
   }
   if (response.device_public_key_signature.has_value()) {
     cbor::Value::MapValue unsigned_extension_outputs;
diff --git a/device/fido/authenticator_make_credential_response.h b/device/fido/authenticator_make_credential_response.h
index df2706a5..66b0b08 100644
--- a/device/fido/authenticator_make_credential_response.h
+++ b/device/fido/authenticator_make_credential_response.h
@@ -51,41 +51,13 @@
 
   std::vector<uint8_t> GetCBOREncodedAttestationObject() const;
 
-  // Replaces the attestation statement with a “none” attestation, and removes
-  // AAGUID from authenticator data section unless |preserve_aaguid| is true.
-  // https://w3c.github.io/webauthn/#createCredential
-  void EraseAttestationStatement(AttestationObject::AAGUID erase_aaguid);
-
-  // Returns true if the attestation is a "self" attestation, i.e. is just the
-  // private key signing itself to show that it is fresh and the AAGUID is zero.
-  bool IsSelfAttestation();
-
-  // Returns true if the attestation certificate is known to be inappropriately
-  // identifying. Some tokens return unique attestation certificates even when
-  // the bit to request that is not set. (Normal attestation certificates are
-  // not intended to be trackable.)
-  bool IsAttestationCertificateInappropriatelyIdentifying();
-
   // Returns the output the the devicePubKey extension, if any.
   absl::optional<device::DevicePublicKeyOutput> GetDevicePublicKeyResponse()
       const;
 
   const std::array<uint8_t, kRpIdHashLength>& GetRpIdHash() const;
 
-  const AttestationObject& attestation_object() const {
-    return attestation_object_;
-  }
-
-  absl::optional<FidoTransportProtocol> transport_used() const {
-    return transport_used_;
-  }
-
-  absl::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key()
-      const {
-    return large_blob_key_;
-  }
-  void set_large_blob_key(
-      const base::span<const uint8_t, kLargeBlobKeyLength> large_blob_key);
+  AttestationObject attestation_object;
 
   // enterprise_attestation_returned is true if the authenticator indicated that
   // it returned an enterprise attestation. Note: U2F authenticators can
@@ -112,17 +84,14 @@
   // https://github.com/fido-alliance/fido-2-specs/pull/1346
   absl::optional<std::vector<uint8_t>> device_public_key_signature;
 
- private:
-  AttestationObject attestation_object_;
-
   // Contains the transport used to register the credential in this case. It is
   // nullopt for cases where we cannot determine the transport (Windows).
-  absl::optional<FidoTransportProtocol> transport_used_;
+  absl::optional<FidoTransportProtocol> transport_used;
 
   // The large blob key associated to the credential. This value is only
   // returned if the credential is created with the largeBlobKey extension on a
   // capable authenticator.
-  absl::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key_;
+  absl::optional<std::array<uint8_t, kLargeBlobKeyLength>> large_blob_key;
 };
 
 // Through cbor::Writer, produces a CTAP style CBOR-encoded byte array
diff --git a/device/fido/ctap_response_fuzzer.cc b/device/fido/ctap_response_fuzzer.cc
index 46aeb20a..3e1c6b0c 100644
--- a/device/fido/ctap_response_fuzzer.cc
+++ b/device/fido/ctap_response_fuzzer.cc
@@ -50,13 +50,15 @@
   auto response = ReadCTAPMakeCredentialResponse(
       FidoTransportProtocol::kUsbHumanInterfaceDevice, input_cbor);
   if (response)
-    response->EraseAttestationStatement(AttestationObject::AAGUID::kErase);
+    response->attestation_object.EraseAttestationStatement(
+        AttestationObject::AAGUID::kErase);
 
   response = AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
       FidoTransportProtocol::kUsbHumanInterfaceDevice, relying_party_id_hash,
       input);
   if (response)
-    response->EraseAttestationStatement(AttestationObject::AAGUID::kErase);
+    response->attestation_object.EraseAttestationStatement(
+        AttestationObject::AAGUID::kErase);
 
   ReadCTAPGetAssertionResponse(FidoTransportProtocol::kUsbHumanInterfaceDevice,
                                input_cbor);
diff --git a/device/fido/ctap_response_unittest.cc b/device/fido/ctap_response_unittest.cc
index f76de25..55d688e 100644
--- a/device/fido/ctap_response_unittest.cc
+++ b/device/fido/ctap_response_unittest.cc
@@ -500,7 +500,7 @@
       certificate.GetArray()[0].GetBytestring(),
       ::testing::ElementsAreArray(test_data::kCtap2MakeCredentialCertificate));
   EXPECT_THAT(
-      make_credential_response->attestation_object().GetCredentialId(),
+      make_credential_response->attestation_object.GetCredentialId(),
       ::testing::ElementsAreArray(test_data::kCtap2MakeCredentialCredentialId));
 }
 
@@ -509,7 +509,7 @@
       FidoTransportProtocol::kUsbHumanInterfaceDevice,
       DecodeCBOR(test_data::kTestMakeCredentialResponse));
   ASSERT_TRUE(make_credential_response);
-  make_credential_response->EraseAttestationStatement(
+  make_credential_response->attestation_object.EraseAttestationStatement(
       AttestationObject::AAGUID::kErase);
   EXPECT_THAT(make_credential_response->GetCBOREncodedAttestationObject(),
               ::testing::ElementsAreArray(test_data::kNoneAttestationResponse));
@@ -543,7 +543,7 @@
           test_data::kApplicationParameter,
           test_data::kTestU2fRegisterResponse);
   ASSERT_TRUE(response);
-  EXPECT_THAT(response->attestation_object().GetCredentialId(),
+  EXPECT_THAT(response->attestation_object.GetCredentialId(),
               ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
   EXPECT_EQ(GetTestAttestationObjectBytes(),
             response->GetCBOREncodedAttestationObject());
diff --git a/device/fido/device_response_converter.cc b/device/fido/device_response_converter.cc
index cdba2d71..4f73a5f9 100644
--- a/device/fido/device_response_converter.cc
+++ b/device/fido/device_response_converter.cc
@@ -22,6 +22,7 @@
 #include "device/fido/authenticator_supported_options.h"
 #include "device/fido/features.h"
 #include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_transport_protocol.h"
 #include "device/fido/opaque_attestation_statement.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -111,7 +112,7 @@
         it->second.GetBytestring().size() != kLargeBlobKeyLength) {
       return absl::nullopt;
     }
-    response.set_large_blob_key(
+    response.large_blob_key = fido_parsing_utils::Materialize(
         base::make_span<kLargeBlobKeyLength>(it->second.GetBytestring()));
   }
 
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index db44e2d9..2561806 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -343,7 +343,7 @@
   }
 
   const absl::optional<cbor::Value>& extensions =
-      response.attestation_object().authenticator_data().extensions();
+      response.attestation_object.authenticator_data().extensions();
   if (extensions && !ValidateResponseExtensions(request, options, authenticator,
                                                 *extensions)) {
     FIDO_LOG(ERROR) << "Invalid extensions block: "
@@ -370,7 +370,7 @@
     return false;
   }
 
-  if (request.large_blob_key && !response.large_blob_key()) {
+  if (request.large_blob_key && !response.large_blob_key) {
     FIDO_LOG(ERROR) << "Large blob key requested but not returned";
     return false;
   }
diff --git a/device/fido/make_credential_task_unittest.cc b/device/fido/make_credential_task_unittest.cc
index 40dd813..740e7a73 100644
--- a/device/fido/make_credential_task_unittest.cc
+++ b/device/fido/make_credential_task_unittest.cc
@@ -97,8 +97,7 @@
   ASSERT_TRUE(make_credential_callback_receiver().value());
   EXPECT_EQ(32u, make_credential_callback_receiver()
                      .value()
-                     ->attestation_object()
-                     .GetCredentialId()
+                     ->attestation_object.GetCredentialId()
                      .size());
 }
 
diff --git a/device/fido/u2f_register_operation_unittest.cc b/device/fido/u2f_register_operation_unittest.cc
index fe2df33..268069f 100644
--- a/device/fido/u2f_register_operation_unittest.cc
+++ b/device/fido/u2f_register_operation_unittest.cc
@@ -91,8 +91,7 @@
   ASSERT_TRUE(register_callback_receiver().value());
   EXPECT_THAT(register_callback_receiver()
                   .value()
-                  ->attestation_object()
-                  .GetCredentialId(),
+                  ->attestation_object.GetCredentialId(),
               ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
 }
 
@@ -112,8 +111,7 @@
   ASSERT_TRUE(register_callback_receiver().value());
   EXPECT_EQ(32ul, register_callback_receiver()
                       .value()
-                      ->attestation_object()
-                      .GetCredentialId()
+                      ->attestation_object.GetCredentialId()
                       .size());
 }
 
@@ -145,8 +143,7 @@
   ASSERT_TRUE(register_callback_receiver().value());
   EXPECT_THAT(register_callback_receiver()
                   .value()
-                  ->attestation_object()
-                  .GetCredentialId(),
+                  ->attestation_object.GetCredentialId(),
               ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
 }
 
@@ -193,8 +190,7 @@
             register_callback_receiver().status());
   EXPECT_THAT(register_callback_receiver()
                   .value()
-                  ->attestation_object()
-                  .GetCredentialId(),
+                  ->attestation_object.GetCredentialId(),
               ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
 }
 
@@ -276,7 +272,7 @@
 
     EXPECT_EQ(CtapDeviceResponseCode::kSuccess, cb.status());
     ASSERT_TRUE(cb.value());
-    EXPECT_THAT(cb.value()->attestation_object().GetCredentialId(),
+    EXPECT_THAT(cb.value()->attestation_object.GetCredentialId(),
                 ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
   }
 }
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index 9ff447c..f9c984d 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -182,7 +182,7 @@
   make_credential_response.enterprise_attestation_returned =
       enterprise_attestation_requested;
   if (large_blob_key) {
-    make_credential_response.set_large_blob_key(*large_blob_key);
+    make_credential_response.large_blob_key = *large_blob_key;
   }
   make_credential_response.device_public_key_signature =
       std::move(dpk_signature);
diff --git a/device/fido/virtual_ctap2_device_unittest.cc b/device/fido/virtual_ctap2_device_unittest.cc
index ad8192be2..36ba0abb 100644
--- a/device/fido/virtual_ctap2_device_unittest.cc
+++ b/device/fido/virtual_ctap2_device_unittest.cc
@@ -165,7 +165,7 @@
   ASSERT_TRUE(response);
 
   const AttestationStatement& attestation =
-      response->attestation_object().attestation_statement();
+      response->attestation_object.attestation_statement();
 
   EXPECT_FALSE(attestation.IsSelfAttestation());
   EXPECT_FALSE(
diff --git a/device/fido/win/type_conversions_unittest.cc b/device/fido/win/type_conversions_unittest.cc
index 40e8801..33e92a3 100644
--- a/device/fido/win/type_conversions_unittest.cc
+++ b/device/fido/win/type_conversions_unittest.cc
@@ -114,17 +114,16 @@
     if (!response)
       return;
 
-    EXPECT_EQ(response->attestation_object()
-                  .authenticator_data()
+    EXPECT_EQ(response->attestation_object.authenticator_data()
                   .SerializeToByteArray(),
               test.authenticator_data);
     EXPECT_EQ(
-        response->attestation_object().attestation_statement().format_name(),
+        response->attestation_object.attestation_statement().format_name(),
         base::WideToUTF8(test.format));
-    EXPECT_EQ(cbor::Writer::Write(AsCBOR(
-                  response->attestation_object().attestation_statement())),
+    EXPECT_EQ(cbor::Writer::Write(
+                  AsCBOR(response->attestation_object.attestation_statement())),
               test.cbor_attestation_statement);
-    EXPECT_EQ(response->transport_used(), test.expected_transport);
+    EXPECT_EQ(response->transport_used, test.expected_transport);
   }
 }
 
diff --git a/docs/webui_build_configuration.md b/docs/webui_build_configuration.md
index cfe6525..4c0d7f9 100644
--- a/docs/webui_build_configuration.md
+++ b/docs/webui_build_configuration.md
@@ -53,6 +53,9 @@
 minify: Whether to minify HTML/CSS with
         third_party/node/node_modules/html-minifier. Defaults to false.
 use_js: Whether to output .js files instead of .ts files. Defaults to false.
+scheme: One of ['chrome', 'relative']. Defaults to 'chrome'. Specifies whether
+        dependencies of the generated wrapper file should be imported with
+        "chrome://resources" or scheme-relative "//resources" URLs.
 ```
 
 #### **Example**
diff --git a/extensions/browser/api/alarms/alarm_manager.cc b/extensions/browser/api/alarms/alarm_manager.cc
index f2c2b471..5d881758 100644
--- a/extensions/browser/api/alarms/alarm_manager.cc
+++ b/extensions/browser/api/alarms/alarm_manager.cc
@@ -186,7 +186,7 @@
 void AlarmManager::GetAllAlarmsWhenReady(GetAllAlarmsCallback callback,
                                          const std::string& extension_id) {
   auto list = alarms_.find(extension_id);
-  std::move(callback).Run(list != alarms_.end() ? &list->second : NULL);
+  std::move(callback).Run(list != alarms_.end() ? &list->second : nullptr);
 }
 
 void AlarmManager::RemoveAlarmWhenReady(const std::string& name,
diff --git a/extensions/browser/api/alarms/alarms_api.cc b/extensions/browser/api/alarms/alarms_api.cc
index 9497724..3f9412d 100644
--- a/extensions/browser/api/alarms/alarms_api.cc
+++ b/extensions/browser/api/alarms/alarms_api.cc
@@ -94,8 +94,7 @@
   std::unique_ptr<alarms::Create::Params> params(
       alarms::Create::Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-  const std::string& alarm_name =
-      params->name.get() ? *params->name : kDefaultAlarmName;
+  const std::string& alarm_name = params->name.value_or(kDefaultAlarmName);
   std::vector<std::string> warnings;
   std::string error;
   if (!ValidateAlarmCreateInfo(alarm_name, params->alarm_info, extension(),
@@ -132,7 +131,7 @@
       alarms::Get::Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  std::string name = params->name.get() ? *params->name : kDefaultAlarmName;
+  std::string name = params->name.value_or(kDefaultAlarmName);
   AlarmManager::Get(browser_context())
       ->GetAlarm(extension_id(), name,
                  base::BindOnce(&AlarmsGetFunction::Callback, this, name));
@@ -172,7 +171,7 @@
       alarms::Clear::Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  std::string name = params->name.get() ? *params->name : kDefaultAlarmName;
+  std::string name = params->name.value_or(kDefaultAlarmName);
   AlarmManager::Get(browser_context())
       ->RemoveAlarm(extension_id(), name,
                     base::BindOnce(&AlarmsClearFunction::Callback, this, name));
diff --git a/extensions/browser/api/api_resource_manager_unittest.cc b/extensions/browser/api/api_resource_manager_unittest.cc
index 598cbf83..45eef71 100644
--- a/extensions/browser/api/api_resource_manager_unittest.cc
+++ b/extensions/browser/api/api_resource_manager_unittest.cc
@@ -45,15 +45,15 @@
   CHECK(resource_two_id);
 
   // Confirm each extension can get its own resource.
-  ASSERT_TRUE(manager->Get(extension_one_id, resource_one_id) != NULL);
-  ASSERT_TRUE(manager->Get(extension_two_id, resource_two_id) != NULL);
+  ASSERT_TRUE(manager->Get(extension_one_id, resource_one_id) != nullptr);
+  ASSERT_TRUE(manager->Get(extension_two_id, resource_two_id) != nullptr);
 
   // Confirm neither extension can get the other's resource.
-  ASSERT_TRUE(manager->Get(extension_one_id, resource_two_id) == NULL);
-  ASSERT_TRUE(manager->Get(extension_two_id, resource_one_id) == NULL);
+  ASSERT_TRUE(manager->Get(extension_one_id, resource_two_id) == nullptr);
+  ASSERT_TRUE(manager->Get(extension_two_id, resource_one_id) == nullptr);
 
   // And make sure we're not susceptible to any Jedi mind tricks.
-  ASSERT_TRUE(manager->Get(std::string(), resource_one_id) == NULL);
+  ASSERT_TRUE(manager->Get(std::string(), resource_one_id) == nullptr);
 }
 
 }  // namespace extensions
diff --git a/extensions/browser/api/app_runtime/app_runtime_api.cc b/extensions/browser/api/app_runtime/app_runtime_api.cc
index 218aba5..1141ca0 100644
--- a/extensions/browser/api/app_runtime/app_runtime_api.cc
+++ b/extensions/browser/api/app_runtime/app_runtime_api.cc
@@ -226,9 +226,9 @@
   app_runtime::LaunchData launch_data;
   app_runtime::LaunchSource source_enum =
       app_runtime::LAUNCH_SOURCE_URL_HANDLER;
-  launch_data.id = std::make_unique<std::string>(handler_id);
-  launch_data.url = std::make_unique<std::string>(url.spec());
-  launch_data.referrer_url = std::make_unique<std::string>(referrer_url.spec());
+  launch_data.id = handler_id;
+  launch_data.url = url.spec();
+  launch_data.referrer_url = referrer_url.spec();
   if (extensions::FeatureSwitch::trace_app_source()->IsEnabled()) {
     launch_data.source = source_enum;
   }
diff --git a/extensions/browser/api/app_window/app_window_api.cc b/extensions/browser/api/app_window/app_window_api.cc
index 6116d83..505a5e5a 100644
--- a/extensions/browser/api/app_window/app_window_api.cc
+++ b/extensions/browser/api/app_window/app_window_api.cc
@@ -103,7 +103,7 @@
     std::vector<std::string> subst;
     subst.push_back(property_name);
     *error = base::ReplaceStringPlaceholders(
-        app_window_constants::kConflictingBoundsOptions, subst, NULL);
+        app_window_constants::kConflictingBoundsOptions, subst, nullptr);
     return false;
   }
 
@@ -168,7 +168,7 @@
   AppWindow::CreateParams create_params;
   app_window::CreateWindowOptions* options = params->options.get();
   if (options) {
-    if (options->id.get()) {
+    if (options->id) {
       // TODO(mek): use URL if no id specified?
       // Limit length of id to 256 characters.
       if (options->id->length() > 256)
@@ -339,14 +339,14 @@
       }
     }
 
-    if (options->icon.get()) {
+    if (options->icon) {
       // First, check if the window icon URL is a valid global URL.
-      create_params.window_icon_url = GURL(*options->icon.get());
+      create_params.window_icon_url = GURL(*options->icon);
 
       // If the URL is not global, check for a valid extension local URL.
       if (!create_params.window_icon_url.is_valid()) {
         create_params.window_icon_url =
-            extension()->GetResourceURL(*options->icon.get());
+            extension()->GetResourceURL(*options->icon);
       }
     }
 
@@ -582,7 +582,7 @@
     create_params->frame =
         GetFrameFromString(*options.frame->as_frame_options->type);
 
-  if (options.frame->as_frame_options->color.get()) {
+  if (options.frame->as_frame_options->color) {
     if (create_params->frame != AppWindow::FRAME_CHROME) {
       *error = app_window_constants::kColorWithFrameNone;
       return false;
@@ -597,7 +597,7 @@
     create_params->has_frame_color = true;
     create_params->inactive_frame_color = create_params->active_frame_color;
 
-    if (options.frame->as_frame_options->inactive_color.get()) {
+    if (options.frame->as_frame_options->inactive_color) {
       if (!content::ParseHexColorString(
               *options.frame->as_frame_options->inactive_color,
               &create_params->inactive_frame_color)) {
@@ -609,7 +609,7 @@
     return true;
   }
 
-  if (options.frame->as_frame_options->inactive_color.get()) {
+  if (options.frame->as_frame_options->inactive_color) {
     *error = app_window_constants::kInactiveColorWithoutColor;
     return false;
   }
diff --git a/extensions/browser/api/audio/audio_service_chromeos.cc b/extensions/browser/api/audio/audio_service_chromeos.cc
index f9b3f85e..74e4cfe 100644
--- a/extensions/browser/api/audio/audio_service_chromeos.cc
+++ b/extensions/browser/api/audio/audio_service_chromeos.cc
@@ -318,8 +318,8 @@
       device.is_input
           ? cras_audio_handler_->GetInputGainPercentForDevice(device.id)
           : cras_audio_handler_->GetOutputVolumePercentForDevice(device.id);
-  info.stable_device_id = std::make_unique<std::string>(
-      id_calculator_->GetStableDeviceId(device.stable_device_id));
+  info.stable_device_id =
+      id_calculator_->GetStableDeviceId(device.stable_device_id);
 
   return info;
 }
diff --git a/extensions/browser/api/audio/audio_service_utils.cc b/extensions/browser/api/audio/audio_service_utils.cc
index 928bf33..af76703 100644
--- a/extensions/browser/api/audio/audio_service_utils.cc
+++ b/extensions/browser/api/audio/audio_service_utils.cc
@@ -190,10 +190,7 @@
   result.device_name = info->deviceName;
   result.is_active = info->isActive;
   result.level = info->level;
-  if (info->stableDeviceId) {
-    result.stable_device_id =
-        std::make_unique<std::string>(*(info->stableDeviceId));
-  }
+  result.stable_device_id = info->stableDeviceId;
   return result;
 }
 
diff --git a/extensions/browser/api/audio/audio_service_utils_unittest.cc b/extensions/browser/api/audio/audio_service_utils_unittest.cc
index 810d61f..b097bd31 100644
--- a/extensions/browser/api/audio/audio_service_utils_unittest.cc
+++ b/extensions/browser/api/audio/audio_service_utils_unittest.cc
@@ -201,7 +201,7 @@
   input.device_name = test_device_name;
   input.is_active = test_is_active;
   input.level = test_level;
-  input.stable_device_id = std::make_unique<std::string>(test_stable_device_id);
+  input.stable_device_id = test_stable_device_id;
 
   auto result = ConvertAudioDeviceInfoToMojom(input);
   ASSERT_TRUE(result);
diff --git a/extensions/browser/api/automation_internal/automation_internal_api.cc b/extensions/browser/api/automation_internal/automation_internal_api.cc
index 315e90c..97f949c7 100644
--- a/extensions/browser/api/automation_internal/automation_internal_api.cc
+++ b/extensions/browser/api/automation_internal/automation_internal_api.cc
@@ -329,7 +329,7 @@
   using api::automation_internal::EnableTab::Params;
   std::unique_ptr<Params> params(Params::Create(args()));
   EXTENSION_FUNCTION_VALIDATE(params.get());
-  content::WebContents* contents = NULL;
+  content::WebContents* contents = nullptr;
   AutomationInternalApiDelegate* automation_api_delegate =
       ExtensionsAPIClient::Get()->GetAutomationInternalApiDelegate();
   int tab_id = -1;
diff --git a/extensions/browser/api/bluetooth/bluetooth_api_pairing_delegate.cc b/extensions/browser/api/bluetooth/bluetooth_api_pairing_delegate.cc
index 7e0dca2..3ddbcf996 100644
--- a/extensions/browser/api/bluetooth/bluetooth_api_pairing_delegate.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_api_pairing_delegate.cc
@@ -58,7 +58,7 @@
   bt_private::PairingEvent event;
   PopulatePairingEvent(
       device, bt_private::PAIRING_EVENT_TYPE_DISPLAYPINCODE, &event);
-  event.pincode = std::make_unique<std::string>(pincode);
+  event.pincode = pincode;
   DispatchPairingEvent(event);
 }
 
diff --git a/extensions/browser/api/bluetooth/bluetooth_api_utils.cc b/extensions/browser/api/bluetooth/bluetooth_api_utils.cc
index 4a88e31..5887bd51 100644
--- a/extensions/browser/api/bluetooth/bluetooth_api_utils.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_api_utils.cc
@@ -125,8 +125,7 @@
 void BluetoothDeviceToApiDevice(const device::BluetoothDevice& device,
                                 Device* out) {
   out->address = device.GetAddress();
-  out->name = std::make_unique<std::string>(
-      base::UTF16ToUTF8(device.GetNameForDisplay()));
+  out->name = base::UTF16ToUTF8(device.GetNameForDisplay());
   out->device_class = device.GetBluetoothClass();
 
   // Only include the Device ID members when one exists for the device, and
diff --git a/extensions/browser/api/bluetooth/bluetooth_apitest.cc b/extensions/browser/api/bluetooth/bluetooth_apitest.cc
index fd37739..7b214b6 100644
--- a/extensions/browser/api/bluetooth/bluetooth_apitest.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_apitest.cc
@@ -152,7 +152,7 @@
 
   std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
       get_adapter_state.get(), "[]", browser()));
-  ASSERT_TRUE(result.get() != NULL);
+  ASSERT_TRUE(result.get() != nullptr);
   api::bluetooth::AdapterState state;
   ASSERT_TRUE(api::bluetooth::AdapterState::Populate(*result, &state));
 
@@ -382,7 +382,7 @@
       .WillOnce(testing::Return(device1_.get()));
   EXPECT_CALL(*mock_adapter_, GetDevice(device2_->GetAddress()))
       .Times(1)
-      .WillRepeatedly(testing::Return(static_cast<BluetoothDevice*>(NULL)));
+      .WillRepeatedly(testing::Return(static_cast<BluetoothDevice*>(nullptr)));
 
   // Load and wait for setup
   ExtensionTestMessageListener listener("ready", ReplyBehavior::kWillReply);
diff --git a/extensions/browser/api/bluetooth/bluetooth_event_router_unittest.cc b/extensions/browser/api/bluetooth/bluetooth_event_router_unittest.cc
index aabef58..26f44de 100644
--- a/extensions/browser/api/bluetooth/bluetooth_event_router_unittest.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_event_router_unittest.cc
@@ -55,7 +55,7 @@
   void TearDown() override {
     // It's important to destroy the router before the browser context keyed
     // services so it removes itself as an ExtensionRegistry observer.
-    router_.reset(NULL);
+    router_.reset();
     ExtensionsTest::TearDown();
   }
 
diff --git a/extensions/browser/api/bluetooth/bluetooth_private_api.cc b/extensions/browser/api/bluetooth/bluetooth_private_api.cc
index 82f9d3f..fe48701 100644
--- a/extensions/browser/api/bluetooth/bluetooth_private_api.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_private_api.cc
@@ -193,7 +193,7 @@
     const device::BluetoothDevice* device,
     const bt_private::SetPairingResponseOptions& options) {
   bool response = options.response != bt_private::PAIRING_RESPONSE_NONE;
-  bool pincode = options.pincode != nullptr;
+  bool pincode = options.pincode.has_value();
   bool passkey = options.passkey.has_value();
 
   if (!response && !pincode && !passkey)
@@ -247,7 +247,7 @@
   const bt_private::NewAdapterState& new_state = params_->adapter_state;
 
   // These properties are not owned.
-  std::string* name = new_state.name.get();
+  const auto& name = new_state.name;
   const auto& powered = new_state.powered;
   const auto& discoverable = new_state.discoverable;
 
@@ -371,7 +371,7 @@
     return;
   }
 
-  if (options.pincode.get()) {
+  if (options.pincode) {
     device->SetPinCode(*options.pincode);
   } else if (options.passkey) {
     device->SetPasskey(*options.passkey);
@@ -520,7 +520,7 @@
         std::make_unique<device::BluetoothDiscoveryFilter>(transport);
 
     if (df_param.uuids.get()) {
-      if (df_param.uuids->as_string.get()) {
+      if (df_param.uuids->as_string) {
         device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter;
         device_filter.uuids.insert(
             device::BluetoothUUID(*df_param.uuids->as_string));
diff --git a/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc b/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc
index 529cd96..601cc1f2 100644
--- a/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc
@@ -104,7 +104,7 @@
   void DispatchPairingEvent(bt_private::PairingEventType pairing_event_type) {
     bt_private::PairingEvent pairing_event;
     pairing_event.pairing = pairing_event_type;
-    pairing_event.device.name = std::make_unique<std::string>(kDeviceName);
+    pairing_event.device.name = kDeviceName;
     pairing_event.device.address = mock_device_->GetAddress();
     pairing_event.device.vendor_id_source = bt::VENDOR_ID_SOURCE_USB;
     pairing_event.device.type = bt::DEVICE_TYPE_PHONE;
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
index 0673806..99495db 100644
--- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
+++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
@@ -54,13 +54,12 @@
 
   out->uuid = service->GetUUID().canonical_value();
   out->is_primary = service->IsPrimary();
-  out->instance_id = std::make_unique<std::string>(service->GetIdentifier());
+  out->instance_id = service->GetIdentifier();
 
   if (!service->GetDevice())
     return;
 
-  out->device_address =
-      std::make_unique<std::string>(service->GetDevice()->GetAddress());
+  out->device_address = service->GetDevice()->GetAddress();
 }
 
 void PopulateCharacteristicProperties(
@@ -111,8 +110,7 @@
   DCHECK(out);
 
   out->uuid = characteristic->GetUUID().canonical_value();
-  out->instance_id =
-      std::make_unique<std::string>(characteristic->GetIdentifier());
+  out->instance_id = characteristic->GetIdentifier();
 
   out->service = std::make_unique<apibtle::Service>();
   PopulateService(characteristic->GetService(), out->service.get());
@@ -131,7 +129,7 @@
   DCHECK(out);
 
   out->uuid = descriptor->GetUUID().canonical_value();
-  out->instance_id = std::make_unique<std::string>(descriptor->GetIdentifier());
+  out->instance_id = descriptor->GetIdentifier();
 
   out->characteristic = std::make_unique<apibtle::Characteristic>();
   PopulateCharacteristic(descriptor->GetCharacteristic(),
@@ -149,8 +147,7 @@
   if (!device)
     return;
   request->device.address = device->GetAddress();
-  request->device.name = std::make_unique<std::string>(
-      base::UTF16ToUTF8(device->GetNameForDisplay()));
+  request->device.name = base::UTF16ToUTF8(device->GetNameForDisplay());
   request->device.device_class = device->GetBluetoothClass();
 }
 
@@ -322,7 +319,7 @@
 }
 
 bool BluetoothLowEnergyEventRouter::HasAdapter() const {
-  return (adapter_.get() != NULL);
+  return (adapter_.get() != nullptr);
 }
 
 void BluetoothLowEnergyEventRouter::Connect(bool persistent,
@@ -1547,7 +1544,7 @@
   auto iter = service_id_to_device_address_.find(instance_id);
   if (iter == service_id_to_device_address_.end()) {
     VLOG(1) << "GATT service identifier unknown: " << instance_id;
-    return NULL;
+    return nullptr;
   }
 
   const std::string& address = iter->second;
@@ -1555,14 +1552,14 @@
   BluetoothDevice* device = adapter_->GetDevice(address);
   if (!device) {
     VLOG(1) << "Bluetooth device not found: " << address;
-    return NULL;
+    return nullptr;
   }
 
   BluetoothRemoteGattService* service = device->GetGattService(instance_id);
   if (!service) {
     VLOG(1) << "GATT service with ID \"" << instance_id
             << "\" not found on device \"" << address << "\"";
-    return NULL;
+    return nullptr;
   }
 
   return service;
@@ -1574,7 +1571,7 @@
   auto iter = chrc_id_to_service_id_.find(instance_id);
   if (iter == chrc_id_to_service_id_.end()) {
     VLOG(1) << "GATT characteristic identifier unknown: " << instance_id;
-    return NULL;
+    return nullptr;
   }
 
   const std::string& service_id = iter->second;
@@ -1582,7 +1579,7 @@
   BluetoothRemoteGattService* service = FindServiceById(service_id);
   if (!service) {
     VLOG(1) << "Failed to obtain service for characteristic: " << instance_id;
-    return NULL;
+    return nullptr;
   }
 
   BluetoothRemoteGattCharacteristic* characteristic =
@@ -1590,7 +1587,7 @@
   if (!characteristic) {
     VLOG(1) << "GATT characteristic with ID \"" << instance_id
             << "\" not found on service \"" << service_id << "\"";
-    return NULL;
+    return nullptr;
   }
 
   return characteristic;
@@ -1602,7 +1599,7 @@
   auto iter = desc_id_to_chrc_id_.find(instance_id);
   if (iter == desc_id_to_chrc_id_.end()) {
     VLOG(1) << "GATT descriptor identifier unknown: " << instance_id;
-    return NULL;
+    return nullptr;
   }
 
   const std::string& chrc_id = iter->second;
@@ -1610,14 +1607,14 @@
   if (!chrc) {
     VLOG(1) << "Failed to obtain characteristic for descriptor: "
             << instance_id;
-    return NULL;
+    return nullptr;
   }
 
   BluetoothRemoteGattDescriptor* descriptor = chrc->GetDescriptor(instance_id);
   if (!descriptor) {
     VLOG(1) << "GATT descriptor with ID \"" << instance_id
             << "\" not found on characteristic \"" << chrc_id << "\"";
-    return NULL;
+    return nullptr;
   }
 
   return descriptor;
@@ -1786,7 +1783,7 @@
   std::unordered_set<int>* connection_ids =
       manager->GetResourceIds(extension_id);
   if (!connection_ids)
-    return NULL;
+    return nullptr;
 
   for (auto iter = connection_ids->cbegin(); iter != connection_ids->cend();
        ++iter) {
@@ -1799,7 +1796,7 @@
       return conn;
   }
 
-  return NULL;
+  return nullptr;
 }
 
 bool BluetoothLowEnergyEventRouter::RemoveConnection(
@@ -1836,7 +1833,7 @@
 
   std::unordered_set<int>* ids = manager->GetResourceIds(extension_id);
   if (!ids)
-    return NULL;
+    return nullptr;
 
   for (auto iter = ids->cbegin(); iter != ids->cend(); ++iter) {
     BluetoothLowEnergyNotifySession* session =
@@ -1849,7 +1846,7 @@
       return session;
   }
 
-  return NULL;
+  return nullptr;
 }
 
 bool BluetoothLowEnergyEventRouter::RemoveNotifySession(
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
index 3feaaf3f..8ae274e 100644
--- a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
+++ b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api.cc
@@ -48,7 +48,7 @@
   // to the system.
   socket_info.socket_id = socket_id;
   if (socket->name()) {
-    socket_info.name = std::make_unique<std::string>(*socket->name());
+    socket_info.name = *socket->name();
   }
   socket_info.persistent = socket->persistent();
   if (socket->buffer_size() > 0) {
@@ -58,18 +58,16 @@
   socket_info.connected = socket->IsConnected();
 
   if (socket->IsConnected()) {
-    socket_info.address =
-        std::make_unique<std::string>(socket->device_address());
+    socket_info.address = socket->device_address();
   }
-  socket_info.uuid =
-      std::make_unique<std::string>(socket->uuid().canonical_value());
+  socket_info.uuid = socket->uuid().canonical_value();
 
   return socket_info;
 }
 
 void SetSocketProperties(BluetoothApiSocket* socket,
                          SocketProperties* properties) {
-  if (properties->name.get()) {
+  if (properties->name) {
     socket->set_name(*properties->name);
   }
   if (properties->persistent) {
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_socket_apitest.cc b/extensions/browser/api/bluetooth_socket/bluetooth_socket_apitest.cc
index e5ddce3..14f7921 100644
--- a/extensions/browser/api/bluetooth_socket/bluetooth_socket_apitest.cc
+++ b/extensions/browser/api/bluetooth_socket/bluetooth_socket_apitest.cc
@@ -80,7 +80,7 @@
   EXPECT_CALL(*mock_adapter_, GetDevice(mock_device1_->GetAddress()))
       .WillRepeatedly(testing::Return(mock_device1_.get()));
   EXPECT_CALL(*mock_adapter_, GetDevice(std::string("aa:aa:aa:aa:aa:aa")))
-      .WillOnce(testing::Return(static_cast<BluetoothDevice*>(NULL)));
+      .WillOnce(testing::Return(static_cast<BluetoothDevice*>(nullptr)));
 
   // Return a mock socket object as a successful result to the connect() call.
   BluetoothUUID service_uuid("8e3ad063-db38-4289-aa8f-b30e4223cf40");
diff --git a/extensions/browser/api/declarative/rules_registry.cc b/extensions/browser/api/declarative/rules_registry.cc
index 5c734fab..62db2db 100644
--- a/extensions/browser/api/declarative/rules_registry.cc
+++ b/extensions/browser/api/declarative/rules_registry.cc
@@ -403,7 +403,7 @@
   // First we insert all rules with existing identifier, so that generated
   // identifiers cannot collide with identifiers passed by the caller.
   for (const auto& rule : *rules) {
-    if (rule.id.get()) {
+    if (rule.id) {
       std::string id = *(rule.id);
       if (!IsUniqueId(extension_id, id)) {
         RemoveUsedRuleIdentifiers(extension_id, rollback_log);
@@ -415,8 +415,8 @@
   // Now we generate IDs in case they were not specified in the rules. This
   // cannot fail so we do not need to keep track of a rollback log.
   for (auto& rule : *rules) {
-    if (!rule.id.get()) {
-      rule.id = std::make_unique<std::string>(GenerateUniqueId(extension_id));
+    if (!rule.id) {
+      rule.id = GenerateUniqueId(extension_id);
       used_rule_identifiers_[extension_id].insert(*(rule.id));
     }
   }
diff --git a/extensions/browser/api/declarative/rules_registry_unittest.cc b/extensions/browser/api/declarative/rules_registry_unittest.cc
index 47e65df..e3a228c 100644
--- a/extensions/browser/api/declarative/rules_registry_unittest.cc
+++ b/extensions/browser/api/declarative/rules_registry_unittest.cc
@@ -50,13 +50,13 @@
 
   ASSERT_EQ(2u, get_rules.size());
 
-  ASSERT_TRUE(get_rules[0]->id.get());
+  ASSERT_TRUE(get_rules[0]->id);
   // Make a copy of the id that this rule was assigned so that we can try to
   // reuse it later when the rule is gone.
   std::string id0 = *get_rules[0]->id;
   EXPECT_NE("", id0);
 
-  ASSERT_TRUE(get_rules[1]->id.get());
+  ASSERT_TRUE(get_rules[1]->id);
   EXPECT_NE("", *get_rules[1]->id);
 
   EXPECT_NE(id0, *get_rules[1]->id);
@@ -69,7 +69,7 @@
   {
     std::vector<api::events::Rule> add_rules;
     add_rules.emplace_back();
-    add_rules[0].id = std::make_unique<std::string>(id0);
+    add_rules[0].id = id0;
     error = registry->AddRules(kExtensionId, std::move(add_rules));
     EXPECT_FALSE(error.empty());
   }
@@ -97,7 +97,7 @@
   {
     std::vector<api::events::Rule> add_rules;
     add_rules.emplace_back();
-    add_rules[0].id = std::make_unique<std::string>(id0);
+    add_rules[0].id = id0;
     error = registry->AddRules(kExtensionId, std::move(add_rules));
     EXPECT_TRUE(error.empty()) << error;
     EXPECT_EQ(1u /*extensions*/ + 2u /*rules*/,
@@ -122,7 +122,7 @@
   {
     std::vector<api::events::Rule> add_rules;
     add_rules.emplace_back();
-    add_rules[0].id = std::make_unique<std::string>(kRuleId);
+    add_rules[0].id = kRuleId;
     error = registry->AddRules(kExtensionId, std::move(add_rules));
     EXPECT_TRUE(error.empty()) << error;
   }
@@ -135,7 +135,7 @@
 
   ASSERT_EQ(1u, get_rules_4b.size());
 
-  ASSERT_TRUE(get_rules_4b[0]->id.get());
+  ASSERT_TRUE(get_rules_4b[0]->id);
   EXPECT_EQ(kRuleId, *get_rules_4b[0]->id);
 
   // Create extension
@@ -306,9 +306,9 @@
     // Add some extra rules outside of the manifest.
     std::vector<api::events::Rule> add_rules;
     api::events::Rule rule_1;
-    rule_1.id = std::make_unique<std::string>("rule_1");
+    rule_1.id = "rule_1";
     api::events::Rule rule_2;
-    rule_2.id = std::make_unique<std::string>("rule_2");
+    rule_2.id = "rule_2";
     add_rules.push_back(std::move(rule_1));
     add_rules.push_back(std::move(rule_2));
     registry->AddRules(kExtensionId, std::move(add_rules));
diff --git a/extensions/browser/api/declarative/test_rules_registry.cc b/extensions/browser/api/declarative/test_rules_registry.cc
index 1aca74b2..b1dd215 100644
--- a/extensions/browser/api/declarative/test_rules_registry.cc
+++ b/extensions/browser/api/declarative/test_rules_registry.cc
@@ -11,12 +11,11 @@
 TestRulesRegistry::TestRulesRegistry(content::BrowserThread::ID owner_thread,
                                      const std::string& event_name,
                                      int rules_registry_id)
-    : RulesRegistry(NULL /*profile*/,
+    : RulesRegistry(nullptr /*profile*/,
                     event_name,
                     owner_thread,
-                    NULL,
-                    rules_registry_id) {
-}
+                    nullptr,
+                    rules_registry_id) {}
 
 TestRulesRegistry::TestRulesRegistry(content::BrowserContext* browser_context,
                                      const std::string& event_name,
diff --git a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc
index 660547a..dd28e3f 100644
--- a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc
+++ b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc
@@ -75,7 +75,7 @@
     flatbuffers::FlatBufferBuilder* builder,
     const dnr_api::URLTransform& transform) {
   auto create_string_offset =
-      [builder](const std::unique_ptr<std::string>& str) {
+      [builder](const absl::optional<std::string>& str) {
         if (!str)
           return FlatStringOffset();
 
@@ -83,7 +83,7 @@
       };
 
   auto skip_separator_and_create_string_offset =
-      [builder](const std::unique_ptr<std::string>& str, char separator) {
+      [builder](const absl::optional<std::string>& str, char separator) {
         if (!str)
           return FlatStringOffset();
 
@@ -93,7 +93,7 @@
         return builder->CreateSharedString(str->c_str() + 1, str->length() - 1);
       };
 
-  auto should_clear_component = [](const std::unique_ptr<std::string>& str) {
+  auto should_clear_component = [](const absl::optional<std::string>& str) {
     return str && str->empty();
   };
 
diff --git a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer_unittest.cc b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer_unittest.cc
index ecfa9100..b97ad6d4 100644
--- a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer_unittest.cc
@@ -65,12 +65,12 @@
       case flat::HeaderOperation_append:
         header_info.operation = dnr_api::HEADER_OPERATION_APPEND;
         DCHECK(flat_value);
-        header_info.value = std::make_unique<std::string>(ToString(flat_value));
+        header_info.value = ToString(flat_value);
         break;
       case flat::HeaderOperation_set:
         header_info.operation = dnr_api::HEADER_OPERATION_SET;
         DCHECK(flat_value);
-        header_info.value = std::make_unique<std::string>(ToString(flat_value));
+        header_info.value = ToString(flat_value);
         break;
       case flat::HeaderOperation_remove:
         header_info.operation = dnr_api::HEADER_OPERATION_REMOVE;
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule.cc b/extensions/browser/api/declarative_net_request/indexed_rule.cc
index 66d69a63..8620fe1 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule.cc
@@ -49,7 +49,7 @@
 
   // This sets the |url_pattern_type|, |anchor_left|, |anchor_right| and
   // |url_pattern| fields on the |indexed_rule_|.
-  static void Parse(std::unique_ptr<std::string> url_filter,
+  static void Parse(absl::optional<std::string> url_filter,
                     IndexedRule* indexed_rule) {
     DCHECK(indexed_rule);
     UrlFilterParser(url_filter ? std::move(*url_filter) : std::string(),
@@ -292,7 +292,7 @@
   return !redirect_url.empty() && redirect_url[0] == '/';
 }
 
-bool IsValidTransformScheme(const std::unique_ptr<std::string>& scheme) {
+bool IsValidTransformScheme(const absl::optional<std::string>& scheme) {
   if (!scheme)
     return true;
 
@@ -303,7 +303,7 @@
   return false;
 }
 
-bool IsValidPort(const std::unique_ptr<std::string>& port) {
+bool IsValidPort(const absl::optional<std::string>& port) {
   if (!port || port->empty())
     return true;
 
@@ -311,7 +311,7 @@
   return base::StringToUint(*port, &port_num) && port_num <= 65535;
 }
 
-bool IsEmptyOrStartsWith(const std::unique_ptr<std::string>& str,
+bool IsEmptyOrStartsWith(const absl::optional<std::string>& str,
                          char starts_with) {
   return !str || str->empty() || str->at(0) == starts_with;
 }
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc b/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
index 0749bb91..501216c 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
@@ -35,7 +35,7 @@
 
 std::unique_ptr<dnr_api::Redirect> MakeRedirectUrl(const char* redirect_url) {
   auto redirect = std::make_unique<dnr_api::Redirect>();
-  redirect->url = std::make_unique<std::string>(redirect_url);
+  redirect->url = redirect_url;
   return redirect;
 }
 
@@ -43,7 +43,7 @@
   dnr_api::Rule rule;
   rule.priority = kMinValidPriority;
   rule.id = kMinValidID;
-  rule.condition.url_filter = std::make_unique<std::string>("filter");
+  rule.condition.url_filter = "filter";
   rule.action.type = dnr_api::RULE_ACTION_TYPE_BLOCK;
   return rule;
 }
@@ -227,7 +227,7 @@
 
 TEST_F(IndexedRuleTest, UrlFilterParsing) {
   struct {
-    std::unique_ptr<std::string> input_url_filter;
+    absl::optional<std::string> input_url_filter;
 
     // Only valid if |expected_result| is SUCCESS.
     const flat_rule::UrlPatternType expected_url_pattern_type;
@@ -237,50 +237,48 @@
 
     const ParseResult expected_result;
   } cases[] = {
-      {nullptr, flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_NONE,
-       flat_rule::AnchorType_NONE, "", ParseResult::SUCCESS},
-      {std::make_unique<std::string>(""), flat_rule::UrlPatternType_SUBSTRING,
+      {absl::nullopt, flat_rule::UrlPatternType_SUBSTRING,
        flat_rule::AnchorType_NONE, flat_rule::AnchorType_NONE, "",
-       ParseResult::ERROR_EMPTY_URL_FILTER},
-      {std::make_unique<std::string>("|"), flat_rule::UrlPatternType_SUBSTRING,
-       flat_rule::AnchorType_BOUNDARY, flat_rule::AnchorType_NONE, "",
        ParseResult::SUCCESS},
-      {std::make_unique<std::string>("||"), flat_rule::UrlPatternType_SUBSTRING,
+      {"", flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_NONE,
+       flat_rule::AnchorType_NONE, "", ParseResult::ERROR_EMPTY_URL_FILTER},
+      {"|", flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_BOUNDARY,
+       flat_rule::AnchorType_NONE, "", ParseResult::SUCCESS},
+      {"||", flat_rule::UrlPatternType_SUBSTRING,
        flat_rule::AnchorType_SUBDOMAIN, flat_rule::AnchorType_NONE, "",
        ParseResult::SUCCESS},
-      {std::make_unique<std::string>("|||"),
-       flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_SUBDOMAIN,
-       flat_rule::AnchorType_BOUNDARY, "", ParseResult::SUCCESS},
-      {std::make_unique<std::string>("|*|||"),
-       flat_rule::UrlPatternType_WILDCARDED, flat_rule::AnchorType_BOUNDARY,
-       flat_rule::AnchorType_BOUNDARY, "*||", ParseResult::SUCCESS},
-      {std::make_unique<std::string>("|xyz|"),
-       flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_BOUNDARY,
-       flat_rule::AnchorType_BOUNDARY, "xyz", ParseResult::SUCCESS},
-      {std::make_unique<std::string>("||x^yz"),
-       flat_rule::UrlPatternType_WILDCARDED, flat_rule::AnchorType_SUBDOMAIN,
-       flat_rule::AnchorType_NONE, "x^yz", ParseResult::SUCCESS},
-      {std::make_unique<std::string>("||xyz|"),
-       flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_SUBDOMAIN,
-       flat_rule::AnchorType_BOUNDARY, "xyz", ParseResult::SUCCESS},
-      {std::make_unique<std::string>("x*y|z"),
-       flat_rule::UrlPatternType_WILDCARDED, flat_rule::AnchorType_NONE,
-       flat_rule::AnchorType_NONE, "x*y|z", ParseResult::SUCCESS},
-      {std::make_unique<std::string>("**^"),
-       flat_rule::UrlPatternType_WILDCARDED, flat_rule::AnchorType_NONE,
+      {"|||", flat_rule::UrlPatternType_SUBSTRING,
+       flat_rule::AnchorType_SUBDOMAIN, flat_rule::AnchorType_BOUNDARY, "",
+       ParseResult::SUCCESS},
+      {"|*|||", flat_rule::UrlPatternType_WILDCARDED,
+       flat_rule::AnchorType_BOUNDARY, flat_rule::AnchorType_BOUNDARY, "*||",
+       ParseResult::SUCCESS},
+      {"|xyz|", flat_rule::UrlPatternType_SUBSTRING,
+       flat_rule::AnchorType_BOUNDARY, flat_rule::AnchorType_BOUNDARY, "xyz",
+       ParseResult::SUCCESS},
+      {"||x^yz", flat_rule::UrlPatternType_WILDCARDED,
+       flat_rule::AnchorType_SUBDOMAIN, flat_rule::AnchorType_NONE, "x^yz",
+       ParseResult::SUCCESS},
+      {"||xyz|", flat_rule::UrlPatternType_SUBSTRING,
+       flat_rule::AnchorType_SUBDOMAIN, flat_rule::AnchorType_BOUNDARY, "xyz",
+       ParseResult::SUCCESS},
+      {"x*y|z", flat_rule::UrlPatternType_WILDCARDED,
+       flat_rule::AnchorType_NONE, flat_rule::AnchorType_NONE, "x*y|z",
+       ParseResult::SUCCESS},
+      {"**^", flat_rule::UrlPatternType_WILDCARDED, flat_rule::AnchorType_NONE,
        flat_rule::AnchorType_NONE, "**^", ParseResult::SUCCESS},
-      {std::make_unique<std::string>("||google.com"),
-       flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_SUBDOMAIN,
-       flat_rule::AnchorType_NONE, "google.com", ParseResult::SUCCESS},
+      {"||google.com", flat_rule::UrlPatternType_SUBSTRING,
+       flat_rule::AnchorType_SUBDOMAIN, flat_rule::AnchorType_NONE,
+       "google.com", ParseResult::SUCCESS},
       // Url pattern with non-ascii characters -â±´ase.com.
-      {std::make_unique<std::string>(base::WideToUTF8(L"\x2c74"
-                                                      L"ase.com")),
+      {base::WideToUTF8(L"\x2c74"
+                        L"ase.com"),
        flat_rule::UrlPatternType_SUBSTRING, flat_rule::AnchorType_NONE,
        flat_rule::AnchorType_NONE, "", ParseResult::ERROR_NON_ASCII_URL_FILTER},
       // Url pattern starting with the domain anchor followed by a wildcard.
-      {std::make_unique<std::string>("||*xyz"),
-       flat_rule::UrlPatternType_WILDCARDED, flat_rule::AnchorType_SUBDOMAIN,
-       flat_rule::AnchorType_NONE, "", ParseResult::ERROR_INVALID_URL_FILTER}};
+      {"||*xyz", flat_rule::UrlPatternType_WILDCARDED,
+       flat_rule::AnchorType_SUBDOMAIN, flat_rule::AnchorType_NONE, "",
+       ParseResult::ERROR_INVALID_URL_FILTER}};
 
   for (size_t i = 0; i < std::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
@@ -317,7 +315,7 @@
 
   for (auto& test_case : test_cases) {
     dnr_api::Rule rule = CreateGenericParsedRule();
-    rule.condition.url_filter = std::make_unique<std::string>(kPattern);
+    rule.condition.url_filter = kPattern;
     rule.condition.is_url_filter_case_sensitive =
         std::move(test_case.is_url_filter_case_sensitive);
     IndexedRule indexed_rule;
@@ -687,8 +685,7 @@
     SCOPED_TRACE(test_case.regex_filter);
     dnr_api::Rule rule = CreateGenericParsedRule();
     rule.condition.url_filter.reset();
-    rule.condition.regex_filter =
-        std::make_unique<std::string>(test_case.regex_filter);
+    rule.condition.regex_filter = test_case.regex_filter;
 
     IndexedRule indexed_rule;
     ParseResult result = IndexedRule::CreateIndexedRule(
@@ -705,8 +702,8 @@
 
 TEST_F(IndexedRuleTest, MultipleFiltersSpecified) {
   dnr_api::Rule rule = CreateGenericParsedRule();
-  rule.condition.url_filter = std::make_unique<std::string>("google");
-  rule.condition.regex_filter = std::make_unique<std::string>("example");
+  rule.condition.url_filter = "google";
+  rule.condition.regex_filter = "example";
 
   IndexedRule indexed_rule;
   ParseResult result = IndexedRule::CreateIndexedRule(
@@ -746,15 +743,13 @@
     dnr_api::Rule rule = CreateGenericParsedRule();
     rule.condition.url_filter.reset();
     if (test_case.regex_filter) {
-      rule.condition.regex_filter =
-          std::make_unique<std::string>(test_case.regex_filter);
+      rule.condition.regex_filter = test_case.regex_filter;
     }
 
     rule.priority = kMinValidPriority;
     rule.action.type = dnr_api::RULE_ACTION_TYPE_REDIRECT;
     rule.action.redirect = std::make_unique<dnr_api::Redirect>();
-    rule.action.redirect->regex_substitution =
-        std::make_unique<std::string>(test_case.regex_substitution);
+    rule.action.redirect->regex_substitution = test_case.regex_substitution;
 
     IndexedRule indexed_rule;
     ParseResult result = IndexedRule::CreateIndexedRule(
@@ -776,16 +771,15 @@
   dnr_api::Rule rule = CreateGenericParsedRule();
   rule.priority = kMinValidPriority;
   rule.condition.url_filter.reset();
-  rule.condition.regex_filter = std::make_unique<std::string>("\\.*");
+  rule.condition.regex_filter = "\\.*";
   rule.action.type = dnr_api::RULE_ACTION_TYPE_REDIRECT;
   rule.action.redirect = std::make_unique<dnr_api::Redirect>();
 
   dnr_api::Redirect& redirect = *rule.action.redirect;
-  redirect.url = std::make_unique<std::string>("http://google.com");
-  redirect.regex_substitution =
-      std::make_unique<std::string>("http://example.com");
+  redirect.url = "http://google.com";
+  redirect.regex_substitution = "http://example.com";
   redirect.transform = std::make_unique<dnr_api::URLTransform>();
-  redirect.transform->scheme = std::make_unique<std::string>("https");
+  redirect.transform->scheme = "https";
 
   IndexedRule indexed_rule;
   ParseResult result = IndexedRule::CreateIndexedRule(
diff --git a/extensions/browser/api/declarative_net_request/test_utils.cc b/extensions/browser/api/declarative_net_request/test_utils.cc
index cf8e690..d58d5c2 100644
--- a/extensions/browser/api/declarative_net_request/test_utils.cc
+++ b/extensions/browser/api/declarative_net_request/test_utils.cc
@@ -435,9 +435,7 @@
 
   header_info.operation = operation;
   header_info.header = header;
-
-  if (value)
-    header_info.value = std::make_unique<std::string>(*value);
+  header_info.value = value;
 
   return header_info;
 }
diff --git a/extensions/browser/api/declarative_net_request/utils.cc b/extensions/browser/api/declarative_net_request/utils.cc
index 600203e..7ee7c9d 100644
--- a/extensions/browser/api/declarative_net_request/utils.cc
+++ b/extensions/browser/api/declarative_net_request/utils.cc
@@ -275,20 +275,18 @@
   details.url = request.url.spec();
 
   if (request.initiator) {
-    details.initiator =
-        std::make_unique<std::string>(request.initiator->Serialize());
+    details.initiator = request.initiator->Serialize();
   }
 
   details.method = request.method;
   details.frame_id = request.frame_data.frame_id;
   if (request.frame_data.document_id) {
-    details.document_id = std::make_unique<std::string>(
-        request.frame_data.document_id.ToString());
+    details.document_id = request.frame_data.document_id.ToString();
   }
   details.parent_frame_id = request.frame_data.parent_frame_id;
   if (request.frame_data.parent_document_id) {
-    details.parent_document_id = std::make_unique<std::string>(
-        request.frame_data.parent_document_id.ToString());
+    details.parent_document_id =
+        request.frame_data.parent_document_id.ToString();
   }
   details.tab_id = request.frame_data.tab_id;
   details.type = GetDNRResourceType(request.web_request_type);
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc b/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc
index 4d784dd..62e1437d 100644
--- a/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc
+++ b/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc
@@ -198,7 +198,7 @@
                                              &single_stage_list,
                                              &error));
     EXPECT_EQ("", error);
-    ASSERT_TRUE(one_stage_attributes.back().get() != NULL);
+    ASSERT_TRUE(one_stage_attributes.back().get() != nullptr);
   }
 
   WebRequestInfo request_info(WebRequestInfoInitParams{});
@@ -355,7 +355,7 @@
   EXPECT_TRUE(result);
 
   // Third set of test data, corner case -- empty disjunction.
-  GetArrayAsVector(NULL, NULL, 0u, &tests);
+  GetArrayAsVector(nullptr, nullptr, 0u, &tests);
   // Positive filter, failing (no test to pass).
   MatchAndCheck(tests, keys::kRequestHeadersKey, stage, request_info, &result);
   EXPECT_FALSE(result);
@@ -366,7 +366,7 @@
 
   // Fourth set of test data, corner case -- empty conjunction.
   const size_t kEmptyConjunctionSizes[] = { 0u };
-  GetArrayAsVector(NULL, kEmptyConjunctionSizes, 1u, &tests);
+  GetArrayAsVector(nullptr, kEmptyConjunctionSizes, 1u, &tests);
   // Positive filter, passing (trivial test).
   MatchAndCheck(tests, keys::kRequestHeadersKey, stage, request_info, &result);
   EXPECT_TRUE(result);
diff --git a/extensions/browser/api/device_permissions_manager.cc b/extensions/browser/api/device_permissions_manager.cc
index 3f47f8d..3549b99 100644
--- a/extensions/browser/api/device_permissions_manager.cc
+++ b/extensions/browser/api/device_permissions_manager.cc
@@ -234,7 +234,7 @@
     ExtensionPrefs* prefs,
     const std::string& extension_id) {
   std::set<scoped_refptr<DevicePermissionEntry>> result;
-  const base::ListValue* devices = NULL;
+  const base::ListValue* devices = nullptr;
   if (!prefs->ReadPrefAsList(extension_id, kDevices, &devices)) {
     return result;
   }
@@ -635,7 +635,7 @@
     return it->second;
   }
 
-  return NULL;
+  return nullptr;
 }
 
 void DevicePermissionsManager::RemoveEntryByDeviceGUID(
diff --git a/extensions/browser/api/dns/dns_api.cc b/extensions/browser/api/dns/dns_api.cc
index 1d15f3b..7152145 100644
--- a/extensions/browser/api/dns/dns_api.cc
+++ b/extensions/browser/api/dns/dns_api.cc
@@ -64,8 +64,7 @@
   resolve_info.result_code = resolve_error_info.error;
   if (result == net::OK) {
     DCHECK(resolved_addresses.has_value() && !resolved_addresses->empty());
-    resolve_info.address = std::make_unique<std::string>(
-        resolved_addresses->front().ToStringWithoutPort());
+    resolve_info.address = resolved_addresses->front().ToStringWithoutPort();
   }
 
   Respond(ArgumentList(Resolve::Results::Create(resolve_info)));
diff --git a/extensions/browser/api/dns/dns_apitest.cc b/extensions/browser/api/dns/dns_apitest.cc
index 538c83b2..13d6aee 100644
--- a/extensions/browser/api/dns/dns_apitest.cc
+++ b/extensions/browser/api/dns/dns_apitest.cc
@@ -65,7 +65,7 @@
 
   std::unique_ptr<base::Value> result(RunFunctionAndReturnSingleResult(
       resolve_function.get(), "[\"127.0.0.1\"]", browser_context()));
-  base::DictionaryValue* dict = NULL;
+  base::DictionaryValue* dict = nullptr;
   ASSERT_TRUE(result->GetAsDictionary(&dict));
 
   EXPECT_EQ(net::OK, dict->FindIntKey("resultCode"));
@@ -88,7 +88,7 @@
   std::string function_arguments = base::StringPrintf(R"(["%s"])", kHostname);
   std::unique_ptr<base::Value> result(RunFunctionAndReturnSingleResult(
       resolve_function.get(), function_arguments, browser_context()));
-  base::DictionaryValue* dict = NULL;
+  base::DictionaryValue* dict = nullptr;
   ASSERT_TRUE(result->GetAsDictionary(&dict));
 
   EXPECT_EQ(net::OK, dict->FindIntKey("resultCode"));
diff --git a/extensions/browser/api/extensions_api_client.cc b/extensions/browser/api/extensions_api_client.cc
index e066835..d61078a7 100644
--- a/extensions/browser/api/extensions_api_client.cc
+++ b/extensions/browser/api/extensions_api_client.cc
@@ -17,12 +17,14 @@
 class AppViewGuestDelegate;
 
 namespace {
-ExtensionsAPIClient* g_instance = NULL;
+ExtensionsAPIClient* g_instance = nullptr;
 }  // namespace
 
 ExtensionsAPIClient::ExtensionsAPIClient() { g_instance = this; }
 
-ExtensionsAPIClient::~ExtensionsAPIClient() { g_instance = NULL; }
+ExtensionsAPIClient::~ExtensionsAPIClient() {
+  g_instance = nullptr;
+}
 
 // static
 ExtensionsAPIClient* ExtensionsAPIClient::Get() { return g_instance; }
@@ -64,13 +66,13 @@
                                            const Extension& extension) {}
 
 AppViewGuestDelegate* ExtensionsAPIClient::CreateAppViewGuestDelegate() const {
-  return NULL;
+  return nullptr;
 }
 
 ExtensionOptionsGuestDelegate*
 ExtensionsAPIClient::CreateExtensionOptionsGuestDelegate(
     ExtensionOptionsGuest* guest) const {
-  return NULL;
+  return nullptr;
 }
 
 std::unique_ptr<guest_view::GuestViewManagerDelegate>
@@ -87,7 +89,7 @@
 
 WebViewGuestDelegate* ExtensionsAPIClient::CreateWebViewGuestDelegate(
     WebViewGuest* web_view_guest) const {
-  return NULL;
+  return nullptr;
 }
 
 WebViewPermissionHelperDelegate* ExtensionsAPIClient::
diff --git a/extensions/browser/api/feedback_private/feedback_private_api.cc b/extensions/browser/api/feedback_private/feedback_private_api.cc
index fc4a6f68..d2404af 100644
--- a/extensions/browser/api/feedback_private/feedback_private_api.cc
+++ b/extensions/browser/api/feedback_private/feedback_private_api.cc
@@ -220,10 +220,9 @@
   auto info = std::make_unique<FeedbackInfo>();
 
   info->description = description_template;
-  info->description_placeholder =
-      std::make_unique<std::string>(description_placeholder_text);
-  info->category_tag = std::make_unique<std::string>(category_tag);
-  info->page_url = std::make_unique<std::string>(page_url.spec());
+  info->description_placeholder = description_placeholder_text;
+  info->category_tag = category_tag;
+  info->page_url = page_url.spec();
   info->system_information = std::make_unique<SystemInformationList>();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   info->from_assistant = from_assistant;
diff --git a/extensions/browser/api/file_handlers/app_file_handler_util.cc b/extensions/browser/api/file_handlers/app_file_handler_util.cc
index a787fb2..596a074 100644
--- a/extensions/browser/api/file_handlers/app_file_handler_util.cc
+++ b/extensions/browser/api/file_handlers/app_file_handler_util.cc
@@ -296,13 +296,13 @@
                                               const std::string& handler_id) {
   const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
   if (!file_handlers)
-    return NULL;
+    return nullptr;
 
   for (auto i = file_handlers->cbegin(); i != file_handlers->cend(); i++) {
     if (i->id == handler_id)
       return &*i;
   }
-  return NULL;
+  return nullptr;
 }
 
 std::vector<FileHandlerMatch> FindFileHandlerMatchesForEntries(
diff --git a/extensions/browser/api/file_system/file_system_api.cc b/extensions/browser/api/file_system/file_system_api.cc
index 360996a..3b6f40e 100644
--- a/extensions/browser/api/file_system/file_system_api.cc
+++ b/extensions/browser/api/file_system/file_system_api.cc
@@ -172,7 +172,7 @@
   if (extensions->empty())
     return false;
 
-  if (accept_option.description.get())
+  if (accept_option.description)
     *description = base::UTF8ToUTF16(*accept_option.description);
   else if (description_id)
     *description = l10n_util::GetStringUTF16(description_id);
@@ -202,7 +202,7 @@
                             const base::File::Info& info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   std::unique_ptr<base::File::Info> file_info(
-      result == base::File::FILE_OK ? new base::File::Info(info) : NULL);
+      result == base::File::FILE_OK ? new base::File::Info(info) : nullptr);
   content::GetUIThreadTaskRunner({})->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), std::move(file_info)));
 }
@@ -722,7 +722,7 @@
 }
 
 void FileSystemChooseEntryFunction::BuildSuggestion(
-    const std::string* opt_name,
+    const absl::optional<std::string>& opt_name,
     base::FilePath* suggested_name,
     base::FilePath::StringType* suggested_extension) {
   if (opt_name) {
@@ -820,7 +820,7 @@
     }
 
     base::FilePath::StringType suggested_extension;
-    BuildSuggestion(options->suggested_name.get(), &suggested_name,
+    BuildSuggestion(options->suggested_name, &suggested_name,
                     &suggested_extension);
 
     BuildFileTypeInfo(&file_type_info, suggested_extension,
diff --git a/extensions/browser/api/file_system/file_system_api.h b/extensions/browser/api/file_system/file_system_api.h
index 04899ea..45c3de2 100644
--- a/extensions/browser/api/file_system/file_system_api.h
+++ b/extensions/browser/api/file_system/file_system_api.h
@@ -177,7 +177,7 @@
       const base::FilePath::StringType& suggested_extension,
       const AcceptOptions* accepts,
       const absl::optional<bool>& accepts_all_types);
-  static void BuildSuggestion(const std::string* opt_name,
+  static void BuildSuggestion(const absl::optional<std::string>& opt_name,
                               base::FilePath* suggested_name,
                               base::FilePath::StringType* suggested_extension);
 
diff --git a/extensions/browser/api/idle/idle_api_unittest.cc b/extensions/browser/api/idle/idle_api_unittest.cc
index d309064..0df3f7dc 100644
--- a/extensions/browser/api/idle/idle_api_unittest.cc
+++ b/extensions/browser/api/idle/idle_api_unittest.cc
@@ -56,13 +56,13 @@
                            const std::string& extension_id)
     : idle_manager_(idle_manager), extension_id_(extension_id) {
   const EventListenerInfo details(idle::OnStateChanged::kEventName,
-                                  extension_id_, GURL(), NULL);
+                                  extension_id_, GURL(), nullptr);
   idle_manager_->OnListenerAdded(details);
 }
 
 ScopedListen::~ScopedListen() {
   const EventListenerInfo details(idle::OnStateChanged::kEventName,
-                                  extension_id_, GURL(), NULL);
+                                  extension_id_, GURL(), nullptr);
   idle_manager_->OnListenerRemoved(details);
 }
 
diff --git a/extensions/browser/api/management/management_api.cc b/extensions/browser/api/management/management_api.cc
index d221f0be..c0a39a1 100644
--- a/extensions/browser/api/management/management_api.cc
+++ b/extensions/browser/api/management/management_api.cc
@@ -108,11 +108,10 @@
   info.offline_enabled = OfflineEnabledInfo::IsOfflineEnabled(&extension);
   info.version = extension.VersionString();
   if (!extension.version_name().empty())
-    info.version_name = std::make_unique<std::string>(extension.version_name());
+    info.version_name = extension.version_name();
   info.description = extension.description();
   info.options_url = OptionsPageInfo::GetOptionsPage(&extension).spec();
-  info.homepage_url = std::make_unique<std::string>(
-      ManifestURL::GetHomepageURL(&extension).spec());
+  info.homepage_url = ManifestURL::GetHomepageURL(&extension).spec();
   info.may_disable =
       !system->management_policy()->MustRemainEnabled(&extension, nullptr);
   info.is_app = extension.is_app();
@@ -149,11 +148,10 @@
   }
   const GURL update_url = delegate->GetEffectiveUpdateURL(extension, context);
   if (!update_url.is_empty())
-    info.update_url = std::make_unique<std::string>(update_url.spec());
+    info.update_url = update_url.spec();
 
   if (extension.is_app()) {
-    info.app_launch_url = std::make_unique<std::string>(
-        delegate->GetFullLaunchURL(&extension).spec());
+    info.app_launch_url = delegate->GetFullLaunchURL(&extension).spec();
   }
 
   const ExtensionIconSet::IconMap& icons =
diff --git a/extensions/browser/api/media_perception_private/conversion_utils.cc b/extensions/browser/api/media_perception_private/conversion_utils.cc
index ecf34c8..f2650be 100644
--- a/extensions/browser/api/media_perception_private/conversion_utils.cc
+++ b/extensions/browser/api/media_perception_private/conversion_utils.cc
@@ -15,8 +15,7 @@
   std::unique_ptr<Metadata> metadata_result = std::make_unique<Metadata>();
   if (metadata.has_visual_experience_controller_version()) {
     metadata_result->visual_experience_controller_version =
-        std::make_unique<std::string>(
-            metadata.visual_experience_controller_version());
+        metadata.visual_experience_controller_version();
   }
 
   return metadata_result;
@@ -340,7 +339,7 @@
     entity_result.depth = DistanceProtoToIdl(entity.depth());
 
   if (entity.has_label())
-    entity_result.entity_label = std::make_unique<std::string>(entity.label());
+    entity_result.entity_label = entity.label();
 
   return entity_result;
 }
@@ -350,8 +349,7 @@
   PacketLatency packet_latency_result;
 
   if (packet_latency.has_label()) {
-    packet_latency_result.packet_label =
-        std::make_unique<std::string>(packet_latency.label());
+    packet_latency_result.packet_label = packet_latency.label();
   }
 
   if (packet_latency.has_latency_usec()) {
@@ -573,8 +571,7 @@
 bool NamedTemplateArgumentProtoToIdl(
     const mri::State::NamedTemplateArgument named_template_argument_proto,
     NamedTemplateArgument* named_template_argument) {
-  named_template_argument->name =
-      std::make_unique<std::string>(named_template_argument_proto.name());
+  named_template_argument->name = named_template_argument_proto.name();
 
   base::Value value =
       NamedTemplateArgumentValueProtoToValue(named_template_argument_proto);
@@ -689,12 +686,10 @@
     state_result.status = StateStatusProtoToIdl(state);
   }
   if (state.has_device_context()) {
-    state_result.device_context =
-        std::make_unique<std::string>(state.device_context());
+    state_result.device_context = state.device_context();
   }
   if (state.has_configuration()) {
-    state_result.configuration =
-        std::make_unique<std::string>(state.configuration());
+    state_result.configuration = state.configuration();
   }
   if (state.has_whiteboard())
     state_result.whiteboard = WhiteboardProtoToIdl(state.whiteboard());
diff --git a/extensions/browser/api/media_perception_private/conversion_utils_unittest.cc b/extensions/browser/api/media_perception_private/conversion_utils_unittest.cc
index 2c35868..db8a0b97 100644
--- a/extensions/browser/api/media_perception_private/conversion_utils_unittest.cc
+++ b/extensions/browser/api/media_perception_private/conversion_utils_unittest.cc
@@ -57,7 +57,7 @@
                                 int width,
                                 int height,
                                 int frame_rate) {
-  param.id = std::make_unique<std::string>(id);
+  param.id = id;
   param.width = width;
   param.height = height;
   param.frame_rate = frame_rate;
@@ -591,13 +591,11 @@
             kNumericalTemplateArgumentName);
   EXPECT_NEAR(*state_result.named_template_arguments->at(0).value->as_number,
               kNumericalTemplateArgumentValue, kDoubleTolerance);
-  EXPECT_EQ(state_result.named_template_arguments->at(0).value->as_string,
-            nullptr);
+  EXPECT_FALSE(state_result.named_template_arguments->at(0).value->as_string);
 
   // Empty.
   EXPECT_EQ(*state_result.named_template_arguments->at(1).name, "");
-  EXPECT_EQ(state_result.named_template_arguments->at(1).value->as_string,
-            nullptr);
+  EXPECT_FALSE(state_result.named_template_arguments->at(1).value->as_string);
   EXPECT_THAT(state_result.named_template_arguments->at(1).value->as_number,
               Eq(absl::nullopt));
 
@@ -633,7 +631,7 @@
   EXPECT_FALSE(state_proto.has_device_context());
 
   state.status = media_perception::STATUS_RUNNING;
-  state.configuration = std::make_unique<std::string>(kTestConfiguration);
+  state.configuration = kTestConfiguration;
   state.whiteboard = std::make_unique<media_perception::Whiteboard>();
   state.whiteboard->top_left =
       MakePointIdl(kWhiteboardTopLeftX, kWhiteboardTopLeftY);
@@ -659,8 +657,7 @@
       std::make_unique<std::vector<media_perception::NamedTemplateArgument>>(
           kNamedTemplateArgumentsSize);
 
-  state.named_template_arguments->at(0).name =
-      std::make_unique<std::string>(kNumericalTemplateArgumentName);
+  state.named_template_arguments->at(0).name = kNumericalTemplateArgumentName;
 
   state.named_template_arguments->at(0).value =
       std::make_unique<media_perception::NamedTemplateArgument::Value>();
@@ -668,8 +665,7 @@
       base::Value(kNumericalTemplateArgumentValue),
       state.named_template_arguments->at(0).value.get());
 
-  state.named_template_arguments->at(2).name =
-      std::make_unique<std::string>(kStringTemplateArgumentName);
+  state.named_template_arguments->at(2).name = kStringTemplateArgumentName;
   state.named_template_arguments->at(2).value =
       std::make_unique<media_perception::NamedTemplateArgument::Value>();
   media_perception::NamedTemplateArgument::Value::Populate(
@@ -722,7 +718,7 @@
             kStringTemplateArgumentValue);
 
   state.status = media_perception::STATUS_SUSPENDED;
-  state.device_context = std::make_unique<std::string>(kTestDeviceContext);
+  state.device_context = kTestDeviceContext;
   state_proto = StateIdlToProto(state);
   EXPECT_EQ(state_proto.status(), mri::State::SUSPENDED);
   EXPECT_EQ(state_proto.device_context(), kTestDeviceContext);
diff --git a/extensions/browser/api/media_perception_private/media_perception_api_manager.cc b/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
index 3bf1185..a58537c 100644
--- a/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
+++ b/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
@@ -73,10 +73,8 @@
 // Pulls out the version number from a mount_point location for the media
 // perception component. Mount points look like
 // /run/imageloader/rtanalytics-light/1.0, where 1.0 is the version string.
-std::unique_ptr<std::string> ExtractVersionFromMountPoint(
-    const std::string& mount_point) {
-  return std::make_unique<std::string>(
-      base::FilePath(mount_point).BaseName().value());
+std::string ExtractVersionFromMountPoint(const std::string& mount_point) {
+  return base::FilePath(mount_point).BaseName().value();
 }
 
 }  // namespace
diff --git a/extensions/browser/api/media_perception_private/media_perception_private_api.cc b/extensions/browser/api/media_perception_private/media_perception_private_api.cc
index f590246..073faff 100644
--- a/extensions/browser/api/media_perception_private/media_perception_private_api.cc
+++ b/extensions/browser/api/media_perception_private/media_perception_private_api.cc
@@ -56,7 +56,7 @@
   // Check that device context is only provided with SetState RUNNING.
   if (params->state.status !=
           extensions::api::media_perception_private::STATUS_RUNNING &&
-      params->state.device_context.get() != nullptr) {
+      params->state.device_context) {
     return RespondNow(
         Error("Only provide deviceContext with SetState RUNNING."));
   }
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.cc b/extensions/browser/api/networking_private/networking_private_chromeos.cc
index 55e77d79..71ae0f0d 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.cc
@@ -226,9 +226,9 @@
   api_cert.hardware_backed = cert.hardware_backed;
   api_cert.device_wide = cert.device_wide;
   if (!cert.pem.empty())
-    api_cert.pem = std::make_unique<std::string>(cert.pem);
+    api_cert.pem = cert.pem;
   if (!cert.pkcs11_id.empty())
-    api_cert.pkcs11_id = std::make_unique<std::string>(cert.pkcs11_id);
+    api_cert.pkcs11_id = cert.pkcs11_id;
   return api_cert;
 }
 
diff --git a/extensions/browser/api/serial/serial_api.cc b/extensions/browser/api/serial/serial_api.cc
index 2bca82be..e935af96 100644
--- a/extensions/browser/api/serial/serial_api.cc
+++ b/extensions/browser/api/serial/serial_api.cc
@@ -84,26 +84,19 @@
   for (const auto& device : devices) {
     extensions::api::serial::DeviceInfo info;
     info.path = device->path.AsUTF8Unsafe();
-    if (device->has_vendor_id)
-      info.vendor_id = device->vendor_id;
-    if (device->has_product_id)
-      info.product_id = device->product_id;
-    if (device->display_name)
-      info.display_name = std::make_unique<std::string>(*device->display_name);
+    info.vendor_id = device->vendor_id;
+    info.product_id = device->product_id;
+    info.display_name = device->display_name;
     results.push_back(std::move(info));
 
 #if BUILDFLAG(IS_MAC)
     if (device->alternate_path) {
       extensions::api::serial::DeviceInfo alternate_info;
       alternate_info.path = device->alternate_path->AsUTF8Unsafe();
-      if (device->has_vendor_id)
-        alternate_info.vendor_id = device->vendor_id;
-      if (device->has_product_id)
-        alternate_info.product_id = device->product_id;
-      if (device->display_name) {
-        alternate_info.display_name =
-            std::make_unique<std::string>(*device->display_name);
-      }
+      alternate_info.vendor_id = device->vendor_id;
+      alternate_info.product_id = device->product_id;
+      alternate_info.display_name = device->display_name;
+
       results.push_back(std::move(alternate_info));
     }
 #endif  // BUILDFLAG(IS_MAC)
diff --git a/extensions/browser/api/serial/serial_connection.cc b/extensions/browser/api/serial/serial_connection.cc
index 4b4081f..00e76cf 100644
--- a/extensions/browser/api/serial/serial_connection.cc
+++ b/extensions/browser/api/serial/serial_connection.cc
@@ -234,7 +234,7 @@
 
   if (options.persistent)
     set_persistent(*options.persistent);
-  if (options.name.get())
+  if (options.name)
     set_name(*options.name);
   if (options.buffer_size)
     set_buffer_size(*options.buffer_size);
@@ -437,7 +437,7 @@
   DCHECK(serial_port_);
   if (options.persistent)
     set_persistent(*options.persistent);
-  if (options.name.get())
+  if (options.name)
     set_name(*options.name);
   if (options.buffer_size)
     set_buffer_size(*options.buffer_size);
diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc
index 04ba475..690705f5 100644
--- a/extensions/browser/api/socket/socket_api.cc
+++ b/extensions/browser/api/socket/socket_api.cc
@@ -797,16 +797,14 @@
   // that it should be closed locally.
   net::IPEndPoint peerAddress;
   if (socket->GetPeerAddress(&peerAddress)) {
-    info.peer_address =
-        std::make_unique<std::string>(peerAddress.ToStringWithoutPort());
+    info.peer_address = peerAddress.ToStringWithoutPort();
     info.peer_port = peerAddress.port();
   }
 
   // Grab the local address as known by the OS.
   net::IPEndPoint localAddress;
   if (socket->GetLocalAddress(&localAddress)) {
-    info.local_address =
-        std::make_unique<std::string>(localAddress.ToStringWithoutPort());
+    info.local_address = localAddress.ToStringWithoutPort();
     info.local_port = localAddress.port();
   }
 
diff --git a/extensions/browser/api/socket/socket_apitest.cc b/extensions/browser/api/socket/socket_apitest.cc
index 7243e4b..807431bc 100644
--- a/extensions/browser/api/socket/socket_apitest.cc
+++ b/extensions/browser/api/socket/socket_apitest.cc
@@ -25,7 +25,7 @@
 
   std::unique_ptr<base::Value> result(RunFunctionAndReturnSingleResult(
       socket_create_function.get(), "[\"udp\"]", browser_context()));
-  base::DictionaryValue* value = NULL;
+  base::DictionaryValue* value = nullptr;
   ASSERT_TRUE(result->GetAsDictionary(&value));
   absl::optional<int> socket_id = value->FindIntKey("socketId");
   ASSERT_TRUE(socket_id);
@@ -43,7 +43,7 @@
 
   std::unique_ptr<base::Value> result(RunFunctionAndReturnSingleResult(
       socket_create_function.get(), "[\"tcp\"]", browser_context()));
-  base::DictionaryValue* value = NULL;
+  base::DictionaryValue* value = nullptr;
   ASSERT_TRUE(result->GetAsDictionary(&value));
   absl::optional<int> socket_id = value->FindIntKey("socketId");
   ASSERT_TRUE(socket_id);
diff --git a/extensions/browser/api/socket/tcp_socket.cc b/extensions/browser/api/socket/tcp_socket.cc
index 7493cfe..9c49e45 100644
--- a/extensions/browser/api/socket/tcp_socket.cc
+++ b/extensions/browser/api/socket/tcp_socket.cc
@@ -457,11 +457,11 @@
     bool has_version_min = false;
     bool has_version_max = false;
     api::socket::TLSVersionConstraints* versions = options->tls_version.get();
-    if (versions->min.get()) {
+    if (versions->min) {
       has_version_min =
           SSLProtocolVersionFromString(*versions->min, &version_min);
     }
-    if (versions->max.get()) {
+    if (versions->max) {
       has_version_max =
           SSLProtocolVersionFromString(*versions->max, &version_max);
     }
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc
index a9ed4c7..6bbfe18 100644
--- a/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc
+++ b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc
@@ -39,7 +39,7 @@
   // to the system.
   socket_info.socket_id = socket_id;
   if (!socket->name().empty()) {
-    socket_info.name = std::make_unique<std::string>(socket->name());
+    socket_info.name = socket->name();
   }
   socket_info.persistent = socket->persistent();
   if (socket->buffer_size() > 0) {
@@ -51,8 +51,7 @@
   // Grab the local address as known by the OS.
   net::IPEndPoint localAddress;
   if (socket->GetLocalAddress(&localAddress)) {
-    socket_info.local_address =
-        std::make_unique<std::string>(localAddress.ToStringWithoutPort());
+    socket_info.local_address = localAddress.ToStringWithoutPort();
     socket_info.local_port = localAddress.port();
   }
 
@@ -62,8 +61,7 @@
   // that it should be closed locally.
   net::IPEndPoint peerAddress;
   if (socket->GetPeerAddress(&peerAddress)) {
-    socket_info.peer_address =
-        std::make_unique<std::string>(peerAddress.ToStringWithoutPort());
+    socket_info.peer_address = peerAddress.ToStringWithoutPort();
     socket_info.peer_port = peerAddress.port();
   }
 
@@ -72,7 +70,7 @@
 
 void SetSocketProperties(ResumableTCPSocket* socket,
                          SocketProperties* properties) {
-  if (properties->name.get()) {
+  if (properties->name) {
     socket->set_name(*properties->name);
   }
   if (properties->persistent) {
@@ -492,14 +490,8 @@
   if (params_->options.get() && params_->options->tls_version.get()) {
     legacy_params.tls_version =
         std::make_unique<api::socket::TLSVersionConstraints>();
-    if (params_->options->tls_version->min.get()) {
-      legacy_params.tls_version->min =
-          std::make_unique<std::string>(*params_->options->tls_version->min);
-    }
-    if (params_->options->tls_version->max.get()) {
-      legacy_params.tls_version->max =
-          std::make_unique<std::string>(*params_->options->tls_version->max);
-    }
+    legacy_params.tls_version->min = params_->options->tls_version->min;
+    legacy_params.tls_version->max = params_->options->tls_version->max;
   }
 
   socket->UpgradeToTLS(
diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc
index 27a799b..77243c7 100644
--- a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc
+++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc
@@ -35,7 +35,7 @@
   // to the system.
   socket_info.socket_id = socket_id;
   if (!socket->name().empty()) {
-    socket_info.name = std::make_unique<std::string>(socket->name());
+    socket_info.name = socket->name();
   }
   socket_info.persistent = socket->persistent();
   socket_info.paused = socket->paused();
@@ -43,8 +43,7 @@
   // Grab the local address as known by the OS.
   net::IPEndPoint localAddress;
   if (socket->GetLocalAddress(&localAddress)) {
-    socket_info.local_address =
-        std::make_unique<std::string>(localAddress.ToStringWithoutPort());
+    socket_info.local_address = localAddress.ToStringWithoutPort();
     socket_info.local_port = localAddress.port();
   }
 
@@ -53,7 +52,7 @@
 
 void SetSocketProperties(ResumableTCPServerSocket* socket,
                          SocketProperties* properties) {
-  if (properties->name.get()) {
+  if (properties->name) {
     socket->set_name(*properties->name);
   }
   if (properties->persistent) {
diff --git a/extensions/browser/api/sockets_udp/sockets_udp_api.cc b/extensions/browser/api/sockets_udp/sockets_udp_api.cc
index 859d5a9..0ff53ca 100644
--- a/extensions/browser/api/sockets_udp/sockets_udp_api.cc
+++ b/extensions/browser/api/sockets_udp/sockets_udp_api.cc
@@ -59,7 +59,7 @@
   // to the system.
   socket_info.socket_id = socket_id;
   if (!socket->name().empty()) {
-    socket_info.name = std::make_unique<std::string>(socket->name());
+    socket_info.name = socket->name();
   }
   socket_info.persistent = socket->persistent();
   if (socket->buffer_size() > 0) {
@@ -70,8 +70,7 @@
   // Grab the local address as known by the OS.
   net::IPEndPoint localAddress;
   if (socket->GetLocalAddress(&localAddress)) {
-    socket_info.local_address =
-        std::make_unique<std::string>(localAddress.ToStringWithoutPort());
+    socket_info.local_address = localAddress.ToStringWithoutPort();
     socket_info.local_port = localAddress.port();
   }
 
@@ -80,7 +79,7 @@
 
 void SetSocketProperties(ResumableUDPSocket* socket,
                          sockets_udp::SocketProperties* properties) {
-  if (properties->name.get()) {
+  if (properties->name) {
     socket->set_name(*properties->name);
   }
   if (properties->persistent) {
diff --git a/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc b/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc
index 7e46c9d..700a269 100644
--- a/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc
+++ b/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc
@@ -42,7 +42,7 @@
       api_test_utils::RunFunctionAndReturnSingleResult(
           socket_create_function.get(), "[]", browser_context()));
 
-  base::DictionaryValue* value = NULL;
+  base::DictionaryValue* value = nullptr;
   ASSERT_TRUE(result->GetAsDictionary(&value));
   absl::optional<int> socketId = value->FindIntKey("socketId");
   EXPECT_TRUE(socketId);
diff --git a/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc
index 7f330d2..29219f8 100644
--- a/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc
+++ b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc
@@ -85,7 +85,7 @@
 
   ResumableUDPSocket* socket =
       params.sockets->Get(params.extension_id, params.socket_id);
-  if (socket == NULL) {
+  if (socket == nullptr) {
     // This can happen if the socket is closed while our callback is active.
     return;
   }
diff --git a/extensions/browser/api/storage/settings_quota_unittest.cc b/extensions/browser/api/storage/settings_quota_unittest.cc
index 4044e60b..dd33d8de 100644
--- a/extensions/browser/api/storage/settings_quota_unittest.cc
+++ b/extensions/browser/api/storage/settings_quota_unittest.cc
@@ -48,13 +48,13 @@
     ASSERT_EQ(256u, validate_sizes.size());
   }
 
-  void TearDown() override { ASSERT_TRUE(storage_.get() != NULL); }
+  void TearDown() override { ASSERT_TRUE(storage_.get() != nullptr); }
 
  protected:
   // Creates |storage_|.  Must only be called once.
   void CreateStorage(
       size_t quota_bytes, size_t quota_bytes_per_item, size_t max_items) {
-    ASSERT_TRUE(storage_.get() == NULL);
+    ASSERT_TRUE(storage_.get() == nullptr);
     SettingsStorageQuotaEnforcer::Limits limits =
         { quota_bytes, quota_bytes_per_item, max_items };
     storage_ = std::make_unique<SettingsStorageQuotaEnforcer>(
diff --git a/extensions/browser/api/storage/settings_storage_quota_enforcer.cc b/extensions/browser/api/storage/settings_storage_quota_enforcer.cc
index 777fab9c..e2815ff5 100644
--- a/extensions/browser/api/storage/settings_storage_quota_enforcer.cc
+++ b/extensions/browser/api/storage/settings_storage_quota_enforcer.cc
@@ -45,7 +45,7 @@
 }
 
 ValueStore::Status QuotaExceededError(Resource resource) {
-  const char* name = NULL;
+  const char* name = nullptr;
   switch (resource) {
     case QUOTA_BYTES:
       name = "QUOTA_BYTES";
diff --git a/extensions/browser/api/storage/storage_api_unittest.cc b/extensions/browser/api/storage/storage_api_unittest.cc
index db92e75..309b6ed1 100644
--- a/extensions/browser/api/storage/storage_api_unittest.cc
+++ b/extensions/browser/api/storage/storage_api_unittest.cc
@@ -88,7 +88,7 @@
         base::StringPrintf("[\"local\", \"%s\"]", key.c_str()));
     if (!result.get())
       return testing::AssertionFailure() << "No result";
-    base::DictionaryValue* dict = NULL;
+    base::DictionaryValue* dict = nullptr;
     if (!result->GetAsDictionary(&dict))
       return testing::AssertionFailure() << result.get()
                                          << " was not a dictionary.";
diff --git a/extensions/browser/api/storage/storage_frontend.cc b/extensions/browser/api/storage/storage_frontend.cc
index 8a5f9408..3a19ab9 100644
--- a/extensions/browser/api/storage/storage_frontend.cc
+++ b/extensions/browser/api/storage/storage_frontend.cc
@@ -113,7 +113,7 @@
   auto it = caches_.find(settings_namespace);
   if (it != caches_.end())
     return it->second;
-  return NULL;
+  return nullptr;
 }
 
 bool StorageFrontend::IsStorageEnabled(
diff --git a/extensions/browser/api/system_cpu/cpu_info_provider.cc b/extensions/browser/api/system_cpu/cpu_info_provider.cc
index 60f2289..071535d 100644
--- a/extensions/browser/api/system_cpu/cpu_info_provider.cc
+++ b/extensions/browser/api/system_cpu/cpu_info_provider.cc
@@ -27,7 +27,7 @@
 
 void CpuInfoProvider::InitializeForTesting(
     scoped_refptr<CpuInfoProvider> provider) {
-  DCHECK(provider.get() != NULL);
+  DCHECK(provider.get() != nullptr);
   provider_.Get() = provider;
 }
 
@@ -84,7 +84,7 @@
 
 // static
 CpuInfoProvider* CpuInfoProvider::Get() {
-  if (provider_.Get().get() == NULL)
+  if (provider_.Get().get() == nullptr)
     provider_.Get() = new CpuInfoProvider();
   return provider_.Get().get();
 }
diff --git a/extensions/browser/api/system_memory/memory_info_provider.cc b/extensions/browser/api/system_memory/memory_info_provider.cc
index 4ddc8eb..70cdeef 100644
--- a/extensions/browser/api/system_memory/memory_info_provider.cc
+++ b/extensions/browser/api/system_memory/memory_info_provider.cc
@@ -22,7 +22,7 @@
 
 void MemoryInfoProvider::InitializeForTesting(
     scoped_refptr<MemoryInfoProvider> provider) {
-  DCHECK(provider.get() != NULL);
+  DCHECK(provider.get() != nullptr);
   provider_.Get() = provider;
 }
 
@@ -35,7 +35,7 @@
 
 // static
 MemoryInfoProvider* MemoryInfoProvider::Get() {
-  if (provider_.Get().get() == NULL)
+  if (provider_.Get().get() == nullptr)
     provider_.Get() = new MemoryInfoProvider();
   return provider_.Get().get();
 }
diff --git a/extensions/browser/api/system_storage/storage_info_provider.cc b/extensions/browser/api/system_storage/storage_info_provider.cc
index 8faa97d..53b5606 100644
--- a/extensions/browser/api/system_storage/storage_info_provider.cc
+++ b/extensions/browser/api/system_storage/storage_info_provider.cc
@@ -29,7 +29,7 @@
 
 void StorageInfoProvider::InitializeForTesting(
     scoped_refptr<StorageInfoProvider> provider) {
-  DCHECK(provider.get() != NULL);
+  DCHECK(provider.get() != nullptr);
   provider_.Get() = provider;
 }
 
@@ -90,7 +90,7 @@
 
 // static
 StorageInfoProvider* StorageInfoProvider::Get() {
-  if (provider_.Get().get() == NULL)
+  if (provider_.Get().get() == nullptr)
     provider_.Get() = new StorageInfoProvider();
   return provider_.Get().get();
 }
diff --git a/extensions/browser/api/web_request/form_data_parser.cc b/extensions/browser/api/web_request/form_data_parser.cc
index ea5313f1..cf8f438 100644
--- a/extensions/browser/api/web_request/form_data_parser.cc
+++ b/extensions/browser/api/web_request/form_data_parser.cc
@@ -323,7 +323,7 @@
   std::string value;
   const bool found =
       request_headers.GetHeader(net::HttpRequestHeaders::kContentType, &value);
-  return CreateFromContentTypeHeader(found ? &value : NULL);
+  return CreateFromContentTypeHeader(found ? &value : nullptr);
 }
 
 // static
@@ -333,7 +333,7 @@
   ParserChoice choice = ERROR_CHOICE;
   std::string boundary;
 
-  if (content_type_header == NULL) {
+  if (content_type_header == nullptr) {
     choice = URL_ENCODED;
   } else {
     const std::string content_type(
@@ -375,7 +375,7 @@
 FormDataParser::FormDataParser() {}
 
 FormDataParserUrlEncoded::FormDataParserUrlEncoded()
-    : source_(NULL),
+    : source_(nullptr),
       source_set_(false),
       source_malformed_(false),
       arg_name_(&name_),
@@ -466,7 +466,7 @@
 // static
 bool FormDataParserMultipart::StartsWithPattern(const re2::StringPiece& input,
                                                 const RE2& pattern) {
-  return pattern.Match(input, 0, input.size(), RE2::ANCHOR_START, NULL, 0);
+  return pattern.Match(input, 0, input.size(), RE2::ANCHOR_START, nullptr, 0);
 }
 
 FormDataParserMultipart::FormDataParserMultipart(
@@ -491,7 +491,7 @@
       return false;
     }
   }
-  if (data != NULL) {
+  if (data != nullptr) {
     if (source_.data() == data_start) {
       // No data in this body part.
       state_ = STATE_ERROR;
@@ -568,7 +568,7 @@
 }
 
 bool FormDataParserMultipart::SetSource(base::StringPiece source) {
-  if (source.data() == NULL || !source_.empty())
+  if (source.data() == nullptr || !source_.empty())
     return false;
   source_.set(source.data(), source.size());
 
diff --git a/extensions/browser/api/web_request/form_data_parser_unittest.cc b/extensions/browser/api/web_request/form_data_parser_unittest.cc
index 84c86110..9817bdef 100644
--- a/extensions/browser/api/web_request/form_data_parser_unittest.cc
+++ b/extensions/browser/api/web_request/form_data_parser_unittest.cc
@@ -20,7 +20,7 @@
     const std::string& content_type_header) {
   std::unique_ptr<FormDataParser> parser(
       FormDataParser::CreateFromContentTypeHeader(&content_type_header));
-  if (parser.get() == NULL)
+  if (parser.get() == nullptr)
     return nullptr;
   return parser;
 }
diff --git a/extensions/browser/api/web_request/upload_data_presenter.cc b/extensions/browser/api/web_request/upload_data_presenter.cc
index ae142a7c..0b01280c 100644
--- a/extensions/browser/api/web_request/upload_data_presenter.cc
+++ b/extensions/browser/api/web_request/upload_data_presenter.cc
@@ -140,10 +140,9 @@
 }
 
 ParsedDataPresenter::ParsedDataPresenter(const std::string& form_type)
-  : parser_(FormDataParser::CreateFromContentTypeHeader(&form_type)),
-    success_(parser_.get() != NULL),
-    dictionary_(success_ ? new base::DictionaryValue() : NULL) {
-}
+    : parser_(FormDataParser::CreateFromContentTypeHeader(&form_type)),
+      success_(parser_.get() != nullptr),
+      dictionary_(success_ ? new base::DictionaryValue() : nullptr) {}
 
 void ParsedDataPresenter::Abort() {
   success_ = false;
diff --git a/extensions/browser/api/web_request/upload_data_presenter_unittest.cc b/extensions/browser/api/web_request/upload_data_presenter_unittest.cc
index 9b68ce8..ed9f402 100644
--- a/extensions/browser/api/web_request/upload_data_presenter_unittest.cc
+++ b/extensions/browser/api/web_request/upload_data_presenter_unittest.cc
@@ -34,12 +34,12 @@
   // Real output.
   std::unique_ptr<ParsedDataPresenter> parsed_data_presenter(
       ParsedDataPresenter::CreateForTests());
-  ASSERT_TRUE(parsed_data_presenter.get() != NULL);
+  ASSERT_TRUE(parsed_data_presenter.get() != nullptr);
   parsed_data_presenter->FeedBytes(
       base::StringPiece(element.bytes(), element.length()));
   EXPECT_TRUE(parsed_data_presenter->Succeeded());
   std::unique_ptr<base::Value> result = parsed_data_presenter->Result();
-  ASSERT_TRUE(result.get() != NULL);
+  ASSERT_TRUE(result.get() != nullptr);
 
   EXPECT_EQ(*result, expected_form);
 }
diff --git a/extensions/browser/api/web_request/web_request_api_helpers.cc b/extensions/browser/api/web_request/web_request_api_helpers.cc
index 0597582..80985db 100644
--- a/extensions/browser/api/web_request/web_request_api_helpers.cc
+++ b/extensions/browser/api/web_request/web_request_api_helpers.cc
@@ -1462,7 +1462,7 @@
     return;
 
   // Only create a copy if we really want to modify the response headers.
-  if (override_response_headers->get() == NULL) {
+  if (override_response_headers->get() == nullptr) {
     *override_response_headers = base::MakeRefCounted<net::HttpResponseHeaders>(
         original_response_headers->raw_headers());
   }
@@ -1605,7 +1605,7 @@
   MergeRedirectUrlOfResponses(request.url, deltas, &new_url, ignored_actions);
   if (new_url.is_valid()) {
     // Only create a copy if we really want to modify the response headers.
-    if (override_response_headers->get() == NULL) {
+    if (override_response_headers->get() == nullptr) {
       *override_response_headers =
           base::MakeRefCounted<net::HttpResponseHeaders>(
               original_response_headers->raw_headers());
diff --git a/extensions/browser/api_unittest.cc b/extensions/browser/api_unittest.cc
index fd010741..cc98f31 100644
--- a/extensions/browser/api_unittest.cc
+++ b/extensions/browser/api_unittest.cc
@@ -71,7 +71,7 @@
 ApiUnitTest::RunFunctionAndReturnDictionary(ExtensionFunction* function,
                                             const std::string& args) {
   base::Value* value = RunFunctionAndReturnValue(function, args).release();
-  base::DictionaryValue* dict = NULL;
+  base::DictionaryValue* dict = nullptr;
 
   if (value && !value->GetAsDictionary(&dict))
     delete value;
diff --git a/extensions/browser/app_window/app_web_contents_helper.cc b/extensions/browser/app_window/app_web_contents_helper.cc
index 825c86da..96ab5dc81 100644
--- a/extensions/browser/app_window/app_web_contents_helper.cc
+++ b/extensions/browser/app_window/app_web_contents_helper.cc
@@ -61,13 +61,13 @@
         base::StringPrintf(
             "Can't open same-window link to \"%s\"; try target=\"_blank\".",
             params.url.spec().c_str()));
-    return NULL;
+    return nullptr;
   }
 
   // These dispositions aren't really navigations.
   if (disposition == WindowOpenDisposition::SAVE_TO_DISK ||
       disposition == WindowOpenDisposition::IGNORE_ACTION)
-    return NULL;
+    return nullptr;
 
   content::WebContents* contents =
       app_delegate_->OpenURLFromTab(browser_context_, web_contents_, params);
diff --git a/extensions/browser/app_window/app_window_geometry_cache_unittest.cc b/extensions/browser/app_window/app_window_geometry_cache_unittest.cc
index 1de13d4..66f2320 100644
--- a/extensions/browser/app_window/app_window_geometry_cache_unittest.cc
+++ b/extensions/browser/app_window/app_window_geometry_cache_unittest.cc
@@ -138,7 +138,8 @@
 // Test getting geometry from an empty store.
 TEST_F(AppWindowGeometryCacheTest, GetGeometryEmptyStore) {
   const std::string extension_id = AddExtensionWithPrefs("ext1");
-  ASSERT_FALSE(cache_->GetGeometry(extension_id, kWindowId, NULL, NULL, NULL));
+  ASSERT_FALSE(
+      cache_->GetGeometry(extension_id, kWindowId, nullptr, nullptr, nullptr));
 }
 
 // Test getting geometry for an unknown extension.
@@ -150,7 +151,8 @@
                               gfx::Rect(4, 5, 31, 43),
                               gfx::Rect(0, 0, 1600, 900),
                               ui::SHOW_STATE_NORMAL);
-  ASSERT_FALSE(cache_->GetGeometry(extension_id2, kWindowId, NULL, NULL, NULL));
+  ASSERT_FALSE(
+      cache_->GetGeometry(extension_id2, kWindowId, nullptr, nullptr, nullptr));
 }
 
 // Test getting geometry for an unknown window in a known extension.
@@ -161,7 +163,8 @@
                               gfx::Rect(4, 5, 31, 43),
                               gfx::Rect(0, 0, 1600, 900),
                               ui::SHOW_STATE_NORMAL);
-  ASSERT_FALSE(cache_->GetGeometry(extension_id, kWindowId2, NULL, NULL, NULL));
+  ASSERT_FALSE(
+      cache_->GetGeometry(extension_id, kWindowId2, nullptr, nullptr, nullptr));
 }
 
 // Test that loading geometry, screen_bounds and state from the store works
@@ -374,11 +377,13 @@
   }
 
   // The first added window should no longer have cached geometry.
-  EXPECT_FALSE(cache_->GetGeometry(extension_id, "window_0", NULL, NULL, NULL));
+  EXPECT_FALSE(
+      cache_->GetGeometry(extension_id, "window_0", nullptr, nullptr, nullptr));
   // All other windows should still exist.
   for (size_t i = 1; i < AppWindowGeometryCache::kMaxCachedWindows + 1; ++i) {
     std::string window_id = "window_" + base::NumberToString(i);
-    EXPECT_TRUE(cache_->GetGeometry(extension_id, window_id, NULL, NULL, NULL));
+    EXPECT_TRUE(cache_->GetGeometry(extension_id, window_id, nullptr, nullptr,
+                                    nullptr));
   }
 }
 
diff --git a/extensions/browser/app_window/app_window_registry.cc b/extensions/browser/app_window/app_window_registry.cc
index 45c06362..d9746382 100644
--- a/extensions/browser/app_window/app_window_registry.cc
+++ b/extensions/browser/app_window/app_window_registry.cc
@@ -124,12 +124,12 @@
       return *i;
   }
 
-  return NULL;
+  return nullptr;
 }
 
 AppWindow* AppWindowRegistry::GetCurrentAppWindowForApp(
     const std::string& app_id) const {
-  AppWindow* result = NULL;
+  AppWindow* result = nullptr;
   for (auto i = app_windows_.cbegin(); i != app_windows_.cend(); ++i) {
     if ((*i)->extension_id() == app_id) {
       result = *i;
@@ -144,7 +144,7 @@
 AppWindow* AppWindowRegistry::GetAppWindowForAppAndKey(
     const std::string& app_id,
     const std::string& window_key) const {
-  AppWindow* result = NULL;
+  AppWindow* result = nullptr;
   for (auto i = app_windows_.cbegin(); i != app_windows_.cend(); ++i) {
     if ((*i)->extension_id() == app_id && (*i)->window_key() == window_key) {
       result = *i;
diff --git a/extensions/browser/content_verifier_io_data.cc b/extensions/browser/content_verifier_io_data.cc
index ed44ec55..52d5199 100644
--- a/extensions/browser/content_verifier_io_data.cc
+++ b/extensions/browser/content_verifier_io_data.cc
@@ -60,7 +60,7 @@
   if (found != data_map_.end())
     return found->second.get();
   else
-    return NULL;
+    return nullptr;
 }
 
 }  // namespace extensions
diff --git a/extensions/browser/content_verify_job.cc b/extensions/browser/content_verify_job.cc
index a7bef9e..d1f747c5 100644
--- a/extensions/browser/content_verify_job.cc
+++ b/extensions/browser/content_verify_job.cc
@@ -204,7 +204,7 @@
 
   int block = current_block_++;
 
-  const std::string* expected_hash = NULL;
+  const std::string* expected_hash = nullptr;
   if (!hash_reader_->GetHashForBlock(block, &expected_hash) ||
       *expected_hash != final) {
     return false;
diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc
index 096e5cc..95c2abe 100644
--- a/extensions/browser/event_router.cc
+++ b/extensions/browser/event_router.cc
@@ -719,7 +719,7 @@
     const std::string& extension_id,
     RegisteredEventType type) const {
   std::set<std::string> events;
-  const ListValue* events_value = NULL;
+  const ListValue* events_value = nullptr;
 
   const char* pref_key = type == RegisteredEventType::kLazy
                              ? kRegisteredLazyEvents
diff --git a/extensions/browser/extension_pref_value_map.cc b/extensions/browser/extension_pref_value_map.cc
index 6eaa9b7..2274ec5 100644
--- a/extensions/browser/extension_pref_value_map.cc
+++ b/extensions/browser/extension_pref_value_map.cc
@@ -83,7 +83,7 @@
   if (incognito && !ext->second->incognito_enabled)
     return false;
 
-  auto winner = GetEffectivePrefValueController(pref_key, incognito, NULL);
+  auto winner = GetEffectivePrefValueController(pref_key, incognito, nullptr);
   if (winner == entries_.end())
     return true;
 
@@ -110,7 +110,7 @@
     const std::string& extension_id,
     const std::string& pref_key,
     bool* from_incognito) const {
-  bool incognito = (from_incognito != NULL);
+  bool incognito = (from_incognito != nullptr);
   auto winner =
       GetEffectivePrefValueController(pref_key, incognito, from_incognito);
   if (winner == entries_.end())
@@ -192,7 +192,7 @@
       return &(i->second->incognito_profile_preferences_session_only);
   }
   NOTREACHED();
-  return NULL;
+  return nullptr;
 }
 
 const PrefValueMap* ExtensionPrefValueMap::GetExtensionPrefValueMap(
@@ -211,7 +211,7 @@
       return &(i->second->incognito_profile_preferences_session_only);
   }
   NOTREACHED();
-  return NULL;
+  return nullptr;
 }
 
 void ExtensionPrefValueMap::GetExtensionControlledKeys(
@@ -245,9 +245,9 @@
     bool* from_incognito) const {
   auto winner = GetEffectivePrefValueController(key, incognito, from_incognito);
   if (winner == entries_.end())
-    return NULL;
+    return nullptr;
 
-  const base::Value* value = NULL;
+  const base::Value* value = nullptr;
   const std::string& ext_id = winner->first;
 
   // First search for incognito session only preferences.
@@ -304,7 +304,7 @@
     if (incognito && !incognito_enabled)
       continue;
 
-    const base::Value* value = NULL;
+    const base::Value* value = nullptr;
     const PrefValueMap* prefs = GetExtensionPrefValueMap(
         ext_id, extensions::kExtensionPrefsScopeRegular);
     if (prefs->GetValue(key, &value)) {
@@ -370,7 +370,7 @@
 
 std::string ExtensionPrefValueMap::GetExtensionControllingPref(
     const std::string& pref_key) const {
-  auto winner = GetEffectivePrefValueController(pref_key, false, NULL);
+  auto winner = GetEffectivePrefValueController(pref_key, false, nullptr);
   if (winner == entries_.end())
     return std::string();
   return winner->first;
diff --git a/extensions/browser/extension_pref_value_map_unittest.cc b/extensions/browser/extension_pref_value_map_unittest.cc
index 87a1a72..f204d607 100644
--- a/extensions/browser/extension_pref_value_map_unittest.cc
+++ b/extensions/browser/extension_pref_value_map_unittest.cc
@@ -49,7 +49,7 @@
   // Returns an empty string if the key is not set.
   std::string GetValue(const char * key, bool incognito) const {
     const base::Value* value =
-        epvm_.GetEffectivePrefValue(key, incognito, NULL);
+        epvm_.GetEffectivePrefValue(key, incognito, nullptr);
     return (value && value->is_string()) ? value->GetString() : std::string();
   }
 
@@ -124,18 +124,18 @@
   RegisterExtension(kExt2, CreateTime(20));
   RegisterExtension(kExt3, CreateTime(30));
 
-  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, NULL));
-  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt2, kPref1, NULL));
-  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, NULL));
+  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, nullptr));
+  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt2, kPref1, nullptr));
+  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, nullptr));
   EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt1, kPref1, false));
   EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt2, kPref1, false));
   EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt3, kPref1, false));
 
   epvm_.SetExtensionPref(kExt2, kPref1, kRegular, CreateVal("val1"));
 
-  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, NULL));
-  EXPECT_TRUE(epvm_.DoesExtensionControlPref(kExt2, kPref1, NULL));
-  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, NULL));
+  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, nullptr));
+  EXPECT_TRUE(epvm_.DoesExtensionControlPref(kExt2, kPref1, nullptr));
+  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, nullptr));
   EXPECT_FALSE(epvm_.CanExtensionControlPref(kExt1, kPref1, false));
   EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt2, kPref1, false));
   EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt3, kPref1, false));
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 397fbb1..c2d667e7 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -716,7 +716,7 @@
                                     base::StringPiece pref_key,
                                     const base::ListValue** out_value) const {
   const base::DictionaryValue* ext = GetExtensionPref(extension_id);
-  const base::ListValue* out = NULL;
+  const base::ListValue* out = nullptr;
   if (!ext || !ext->GetList(pref_key, &out))
     return false;
   if (out_value)
@@ -754,14 +754,14 @@
 
 bool ExtensionPrefs::HasPrefForExtension(
     const std::string& extension_id) const {
-  return GetExtensionPref(extension_id) != NULL;
+  return GetExtensionPref(extension_id) != nullptr;
 }
 
 bool ExtensionPrefs::ReadPrefAsURLPatternSet(const std::string& extension_id,
                                              base::StringPiece pref_key,
                                              URLPatternSet* result,
                                              int valid_schemes) const {
-  const base::ListValue* value = NULL;
+  const base::ListValue* value = nullptr;
   if (!ReadPrefAsList(extension_id, pref_key, &value))
     return false;
   const base::DictionaryValue* extension = GetExtensionPref(extension_id);
@@ -774,7 +774,7 @@
   }
 
   bool allow_file_access = AllowFileAccess(extension_id);
-  return result->Populate(*value, valid_schemes, allow_file_access, NULL);
+  return result->Populate(*value, valid_schemes, allow_file_access, nullptr);
 }
 
 void ExtensionPrefs::SetExtensionPrefURLPatternSet(
@@ -802,24 +802,24 @@
   // Retrieve the API permissions. Please refer SetExtensionPrefPermissionSet()
   // for api_values format.
   APIPermissionSet apis;
-  const base::ListValue* api_values = NULL;
+  const base::ListValue* api_values = nullptr;
   std::string api_pref = JoinPrefs({pref_key, kPrefAPIs});
   if (ReadPrefAsList(extension_id, api_pref, &api_values)) {
     APIPermissionSet::ParseFromJSON(api_values,
                                     APIPermissionSet::kAllowInternalPermissions,
-                                    &apis, NULL, NULL);
+                                    &apis, nullptr, nullptr);
   }
 
   // Retrieve the Manifest Keys permissions. Please refer to
   // |SetExtensionPrefPermissionSet| for manifest_permissions_values format.
   ManifestPermissionSet manifest_permissions;
-  const base::ListValue* manifest_permissions_values = NULL;
+  const base::ListValue* manifest_permissions_values = nullptr;
   std::string manifest_permission_pref =
       JoinPrefs({pref_key, kPrefManifestPermissions});
   if (ReadPrefAsList(extension_id, manifest_permission_pref,
                      &manifest_permissions_values)) {
     ManifestPermissionSet::ParseFromJSON(
-        manifest_permissions_values, &manifest_permissions, NULL, NULL);
+        manifest_permissions_values, &manifest_permissions, nullptr, nullptr);
   }
 
   // Retrieve the explicit host permissions.
@@ -1733,7 +1733,7 @@
 int ExtensionPrefs::GetDelayedInstallCreationFlags(
     const std::string& extension_id) const {
   int creation_flags = Extension::NO_FLAGS;
-  const base::DictionaryValue* delayed_info = NULL;
+  const base::DictionaryValue* delayed_info = nullptr;
   if (ReadPrefAsDictionary(extension_id, kDelayedInstallInfo, &delayed_info)) {
     if (absl::optional<int> flags =
             delayed_info->FindIntKey(kPrefCreationFlags)) {
diff --git a/extensions/browser/extension_registry.cc b/extensions/browser/extension_registry.cc
index 27c7442..d1b4295 100644
--- a/extensions/browser/extension_registry.cc
+++ b/extensions/browser/extension_registry.cc
@@ -144,7 +144,7 @@
     if (extension)
       return extension;
   }
-  return NULL;
+  return nullptr;
 }
 
 const Extension* ExtensionRegistry::GetInstalledExtension(
diff --git a/extensions/browser/extension_registry_unittest.cc b/extensions/browser/extension_registry_unittest.cc
index 6f119a5c..2267d8cb 100644
--- a/extensions/browser/extension_registry_unittest.cc
+++ b/extensions/browser/extension_registry_unittest.cc
@@ -88,7 +88,7 @@
 };
 
 TEST_F(ExtensionRegistryTest, FillAndClearRegistry) {
-  ExtensionRegistry registry(NULL);
+  ExtensionRegistry registry(nullptr);
   scoped_refptr<const Extension> extension1 = ExtensionBuilder("one").Build();
   scoped_refptr<const Extension> extension2 = ExtensionBuilder("two").Build();
   scoped_refptr<const Extension> extension3 = ExtensionBuilder("three").Build();
@@ -122,7 +122,7 @@
 
 // A simple test of adding and removing things from sets.
 TEST_F(ExtensionRegistryTest, AddAndRemoveExtensionFromRegistry) {
-  ExtensionRegistry registry(NULL);
+  ExtensionRegistry registry(nullptr);
 
   // Adding an extension works.
   scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build();
@@ -143,7 +143,7 @@
 }
 
 TEST_F(ExtensionRegistryTest, AddExtensionToRegistryTwice) {
-  ExtensionRegistry registry(NULL);
+  ExtensionRegistry registry(nullptr);
   scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build();
 
   // An extension can exist in two sets at once. It would be nice to eliminate
@@ -158,7 +158,7 @@
 }
 
 TEST_F(ExtensionRegistryTest, GetExtensionById) {
-  ExtensionRegistry registry(NULL);
+  ExtensionRegistry registry(nullptr);
 
   // Trying to get an extension fails cleanly when the sets are empty.
   EXPECT_FALSE(
@@ -238,7 +238,7 @@
 }
 
 TEST_F(ExtensionRegistryTest, Observer) {
-  ExtensionRegistry registry(NULL);
+  ExtensionRegistry registry(nullptr);
   TestObserver observer;
   registry.AddObserver(&observer);
 
diff --git a/extensions/browser/extensions_browser_client.cc b/extensions/browser/extensions_browser_client.cc
index 87df226..f8a6ad36 100644
--- a/extensions/browser/extensions_browser_client.cc
+++ b/extensions/browser/extensions_browser_client.cc
@@ -19,7 +19,7 @@
 
 namespace {
 
-ExtensionsBrowserClient* g_extension_browser_client = NULL;
+ExtensionsBrowserClient* g_extension_browser_client = nullptr;
 
 }  // namespace
 
diff --git a/extensions/browser/lazy_background_task_queue_unittest.cc b/extensions/browser/lazy_background_task_queue_unittest.cc
index d0b32d13..72c6baeb 100644
--- a/extensions/browser/lazy_background_task_queue_unittest.cc
+++ b/extensions/browser/lazy_background_task_queue_unittest.cc
@@ -196,7 +196,7 @@
 
   // ProcessPendingTasks is a no-op if there are no tasks.
   scoped_refptr<const Extension> extension = CreateSimpleExtension();
-  queue.ProcessPendingTasks(NULL, browser_context(), extension.get());
+  queue.ProcessPendingTasks(nullptr, browser_context(), extension.get());
   EXPECT_EQ(0, task_run_count());
 
   // Schedule a task to run.
@@ -209,13 +209,13 @@
 
   // Trying to run tasks for an unrelated BrowserContext should do nothing.
   content::TestBrowserContext unrelated_context;
-  queue.ProcessPendingTasks(NULL, &unrelated_context, extension.get());
+  queue.ProcessPendingTasks(nullptr, &unrelated_context, extension.get());
   EXPECT_EQ(0, task_run_count());
   EXPECT_EQ(1u, queue.pending_tasks_.size());
 
   // Processing tasks when there is one pending runs the task and removes the
   // extension from the list of extensions with pending tasks.
-  queue.ProcessPendingTasks(NULL, browser_context(), extension.get());
+  queue.ProcessPendingTasks(nullptr, browser_context(), extension.get());
   EXPECT_EQ(1, task_run_count());
   EXPECT_EQ(0u, queue.pending_tasks_.size());
 }
diff --git a/extensions/browser/management_policy_unittest.cc b/extensions/browser/management_policy_unittest.cc
index c057dd0..771c3de 100644
--- a/extensions/browser/management_policy_unittest.cc
+++ b/extensions/browser/management_policy_unittest.cc
@@ -70,142 +70,142 @@
   std::u16string error;
   // The extension and location are irrelevant to the
   // TestManagementPolicyProviders.
-  EXPECT_TRUE(policy_.UserMayLoad(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayLoad(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // One provider, no relevant restriction.
   policy_.RegisterProvider(&no_modify_status_);
-  EXPECT_TRUE(policy_.UserMayLoad(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayLoad(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Two providers, no relevant restrictions.
   policy_.RegisterProvider(&must_remain_enabled_);
-  EXPECT_TRUE(policy_.UserMayLoad(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayLoad(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Three providers, one with a relevant restriction.
   policy_.RegisterProvider(&no_load_);
-  EXPECT_FALSE(policy_.UserMayLoad(NULL, &error));
+  EXPECT_FALSE(policy_.UserMayLoad(nullptr, &error));
   EXPECT_FALSE(error.empty());
 
   // Remove the restriction.
   policy_.UnregisterProvider(&no_load_);
   error.clear();
-  EXPECT_TRUE(policy_.UserMayLoad(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayLoad(nullptr, &error));
   EXPECT_TRUE(error.empty());
 }
 TEST_F(ManagementPolicyTest, UserMayModifySettings) {
   // No providers registered.
   std::u16string error;
-  EXPECT_TRUE(policy_.UserMayModifySettings(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayModifySettings(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // One provider, no relevant restriction.
   policy_.RegisterProvider(&allow_all_);
-  EXPECT_TRUE(policy_.UserMayModifySettings(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayModifySettings(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Two providers, no relevant restrictions.
   policy_.RegisterProvider(&no_load_);
-  EXPECT_TRUE(policy_.UserMayModifySettings(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayModifySettings(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Three providers, one with a relevant restriction.
   policy_.RegisterProvider(&no_modify_status_);
-  EXPECT_FALSE(policy_.UserMayModifySettings(NULL, &error));
+  EXPECT_FALSE(policy_.UserMayModifySettings(nullptr, &error));
   EXPECT_FALSE(error.empty());
 
   // Remove the restriction.
   policy_.UnregisterProvider(&no_modify_status_);
   error.clear();
-  EXPECT_TRUE(policy_.UserMayModifySettings(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayModifySettings(nullptr, &error));
   EXPECT_TRUE(error.empty());
 }
 
 TEST_F(ManagementPolicyTest, MustRemainEnabled) {
   // No providers registered.
   std::u16string error;
-  EXPECT_FALSE(policy_.MustRemainEnabled(NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainEnabled(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // One provider, no relevant restriction.
   policy_.RegisterProvider(&allow_all_);
-  EXPECT_FALSE(policy_.MustRemainEnabled(NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainEnabled(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Two providers, no relevant restrictions.
   policy_.RegisterProvider(&no_modify_status_);
-  EXPECT_FALSE(policy_.MustRemainEnabled(NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainEnabled(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Three providers, one with a relevant restriction.
   policy_.RegisterProvider(&must_remain_enabled_);
-  EXPECT_TRUE(policy_.MustRemainEnabled(NULL, &error));
+  EXPECT_TRUE(policy_.MustRemainEnabled(nullptr, &error));
   EXPECT_FALSE(error.empty());
 
   // Remove the restriction.
   policy_.UnregisterProvider(&must_remain_enabled_);
   error.clear();
-  EXPECT_FALSE(policy_.MustRemainEnabled(NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainEnabled(nullptr, &error));
   EXPECT_TRUE(error.empty());
 }
 
 TEST_F(ManagementPolicyTest, MustRemainDisabled) {
   // No providers registered.
   std::u16string error;
-  EXPECT_FALSE(policy_.MustRemainDisabled(NULL, NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainDisabled(nullptr, nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // One provider, no relevant restriction.
   policy_.RegisterProvider(&allow_all_);
-  EXPECT_FALSE(policy_.MustRemainDisabled(NULL, NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainDisabled(nullptr, nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Two providers, no relevant restrictions.
   policy_.RegisterProvider(&no_modify_status_);
-  EXPECT_FALSE(policy_.MustRemainDisabled(NULL, NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainDisabled(nullptr, nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Three providers, one with a relevant restriction.
   extensions::disable_reason::DisableReason reason =
       extensions::disable_reason::DISABLE_NONE;
   policy_.RegisterProvider(&must_remain_disabled_);
-  EXPECT_TRUE(policy_.MustRemainDisabled(NULL, &reason, &error));
+  EXPECT_TRUE(policy_.MustRemainDisabled(nullptr, &reason, &error));
   EXPECT_FALSE(error.empty());
   EXPECT_EQ(extensions::disable_reason::DISABLE_SIDELOAD_WIPEOUT, reason);
 
   // Remove the restriction.
   policy_.UnregisterProvider(&must_remain_disabled_);
   error.clear();
-  EXPECT_FALSE(policy_.MustRemainDisabled(NULL, NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainDisabled(nullptr, nullptr, &error));
   EXPECT_TRUE(error.empty());
 }
 
 TEST_F(ManagementPolicyTest, MustRemainInstalled) {
   // No providers registered.
   std::u16string error;
-  EXPECT_FALSE(policy_.MustRemainInstalled(NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainInstalled(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // One provider, no relevant restriction.
   policy_.RegisterProvider(&allow_all_);
-  EXPECT_FALSE(policy_.MustRemainInstalled(NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainInstalled(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Two providers, no relevant restrictions.
   policy_.RegisterProvider(&no_modify_status_);
-  EXPECT_FALSE(policy_.MustRemainInstalled(NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainInstalled(nullptr, &error));
   EXPECT_TRUE(error.empty());
 
   // Three providers, one with a relevant restriction.
   policy_.RegisterProvider(&must_remain_installed_);
-  EXPECT_TRUE(policy_.MustRemainInstalled(NULL, &error));
+  EXPECT_TRUE(policy_.MustRemainInstalled(nullptr, &error));
   EXPECT_FALSE(error.empty());
 
   // Remove the restriction.
   policy_.UnregisterProvider(&must_remain_installed_);
   error.clear();
-  EXPECT_FALSE(policy_.MustRemainInstalled(NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainInstalled(nullptr, &error));
   EXPECT_TRUE(error.empty());
 }
 
@@ -215,30 +215,30 @@
   std::string original_error = "Ceci est en effet une erreur.";
   std::u16string original_error16 = base::UTF8ToUTF16(original_error);
   std::u16string error = original_error16;
-  EXPECT_TRUE(policy_.UserMayLoad(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayLoad(nullptr, &error));
   EXPECT_EQ(original_error, base::UTF16ToUTF8(error));
-  EXPECT_TRUE(policy_.UserMayModifySettings(NULL, &error));
+  EXPECT_TRUE(policy_.UserMayModifySettings(nullptr, &error));
   EXPECT_EQ(original_error, base::UTF16ToUTF8(error));
-  EXPECT_FALSE(policy_.MustRemainEnabled(NULL, &error));
+  EXPECT_FALSE(policy_.MustRemainEnabled(nullptr, &error));
   EXPECT_EQ(original_error, base::UTF16ToUTF8(error));
 
   // Ensure no crashes if no error message was requested.
-  EXPECT_TRUE(policy_.UserMayLoad(NULL, NULL));
-  EXPECT_TRUE(policy_.UserMayModifySettings(NULL, NULL));
-  EXPECT_FALSE(policy_.MustRemainEnabled(NULL, NULL));
+  EXPECT_TRUE(policy_.UserMayLoad(nullptr, nullptr));
+  EXPECT_TRUE(policy_.UserMayModifySettings(nullptr, nullptr));
+  EXPECT_FALSE(policy_.MustRemainEnabled(nullptr, nullptr));
   policy_.RegisterProvider(&restrict_all_);
-  EXPECT_FALSE(policy_.UserMayLoad(NULL, NULL));
-  EXPECT_FALSE(policy_.UserMayModifySettings(NULL, NULL));
-  EXPECT_TRUE(policy_.MustRemainEnabled(NULL, NULL));
+  EXPECT_FALSE(policy_.UserMayLoad(nullptr, nullptr));
+  EXPECT_FALSE(policy_.UserMayModifySettings(nullptr, nullptr));
+  EXPECT_TRUE(policy_.MustRemainEnabled(nullptr, nullptr));
 
   // Make sure returned error is correct.
   error = original_error16;
-  EXPECT_FALSE(policy_.UserMayLoad(NULL, &error));
+  EXPECT_FALSE(policy_.UserMayLoad(nullptr, &error));
   EXPECT_EQ(base::UTF8ToUTF16(TestProvider::expected_error()), error);
   error = original_error16;
-  EXPECT_FALSE(policy_.UserMayModifySettings(NULL, &error));
+  EXPECT_FALSE(policy_.UserMayModifySettings(nullptr, &error));
   EXPECT_EQ(base::UTF8ToUTF16(TestProvider::expected_error()), error);
   error = original_error16;
-  EXPECT_TRUE(policy_.MustRemainEnabled(NULL, &error));
+  EXPECT_TRUE(policy_.MustRemainEnabled(nullptr, &error));
   EXPECT_EQ(base::UTF8ToUTF16(TestProvider::expected_error()), error);
 }
diff --git a/extensions/browser/quota_service.cc b/extensions/browser/quota_service.cc
index 19bca3d93..ba316d9 100644
--- a/extensions/browser/quota_service.cc
+++ b/extensions/browser/quota_service.cc
@@ -56,7 +56,7 @@
   if (heuristics.empty())
     return std::string();  // No heuristic implies no limit.
 
-  QuotaLimitHeuristic* failed_heuristic = NULL;
+  QuotaLimitHeuristic* failed_heuristic = nullptr;
   for (const auto& heuristic : heuristics) {
     // Apply heuristic to each item (bucket).
     if (!heuristic->ApplyToArgs(args, event_time)) {
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index f415d34..1e514e9 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -75,7 +75,6 @@
     mediaStoppedPlaying,
     menuEnd,
     menuItemSelected,
-    menuListItemSelected,
     menuListValueChanged,
     menuPopupEnd,
     menuPopupStart,
diff --git a/extensions/common/api/bluetooth/bluetooth_manifest_handler.cc b/extensions/common/api/bluetooth/bluetooth_manifest_handler.cc
index 6abd8c3e..68b09617 100644
--- a/extensions/common/api/bluetooth/bluetooth_manifest_handler.cc
+++ b/extensions/common/api/bluetooth/bluetooth_manifest_handler.cc
@@ -38,7 +38,7 @@
   BluetoothManifestData* data = BluetoothManifestData::Get(extension);
   if (data)
     return data->permission()->Clone().release();
-  return NULL;
+  return nullptr;
 }
 
 base::span<const char* const> BluetoothManifestHandler::Keys() const {
diff --git a/extensions/common/api/commands/commands_handler.cc b/extensions/common/api/commands/commands_handler.cc
index 68b2a692..14968b18 100644
--- a/extensions/common/api/commands/commands_handler.cc
+++ b/extensions/common/api/commands/commands_handler.cc
@@ -34,14 +34,14 @@
     const Extension* extension) {
   auto* info =
       static_cast<CommandsInfo*>(extension->GetManifestData(keys::kCommands));
-  return info ? info->browser_action_command.get() : NULL;
+  return info ? info->browser_action_command.get() : nullptr;
 }
 
 // static
 const Command* CommandsInfo::GetPageActionCommand(const Extension* extension) {
   auto* info =
       static_cast<CommandsInfo*>(extension->GetManifestData(keys::kCommands));
-  return info ? info->page_action_command.get() : NULL;
+  return info ? info->page_action_command.get() : nullptr;
 }
 
 // static
@@ -55,7 +55,7 @@
 const CommandMap* CommandsInfo::GetNamedCommands(const Extension* extension) {
   auto* info =
       static_cast<CommandsInfo*>(extension->GetManifestData(keys::kCommands));
-  return info ? &info->named_commands : NULL;
+  return info ? &info->named_commands : nullptr;
 }
 
 CommandsHandler::CommandsHandler() = default;
@@ -69,7 +69,7 @@
     return true;
   }
 
-  const base::DictionaryValue* dict = NULL;
+  const base::DictionaryValue* dict = nullptr;
   if (!extension->manifest()->GetDictionary(keys::kCommands, &dict)) {
     *error = manifest_errors::kInvalidCommandsKey;
     return false;
@@ -83,7 +83,7 @@
        iter.Advance()) {
     ++command_index;
 
-    const base::DictionaryValue* command = NULL;
+    const base::DictionaryValue* command = nullptr;
     if (!iter.value().GetAsDictionary(&command)) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           manifest_errors::kInvalidKeyBindingDictionary,
diff --git a/extensions/common/api/commands/commands_manifest_unittest.cc b/extensions/common/api/commands/commands_manifest_unittest.cc
index 3faa1878..edab4e6 100644
--- a/extensions/common/api/commands/commands_manifest_unittest.cc
+++ b/extensions/common/api/commands/commands_manifest_unittest.cc
@@ -48,7 +48,7 @@
 
   const Command* browser_action =
       CommandsInfo::GetBrowserActionCommand(extension.get());
-  ASSERT_TRUE(NULL != browser_action);
+  ASSERT_TRUE(nullptr != browser_action);
   ASSERT_STREQ("_execute_browser_action",
                browser_action->command_name().c_str());
   ASSERT_STREQ("", base::UTF16ToASCII(browser_action->description()).c_str());
@@ -56,7 +56,7 @@
 
   const Command* page_action =
       CommandsInfo::GetPageActionCommand(extension.get());
-  ASSERT_TRUE(NULL != page_action);
+  ASSERT_TRUE(nullptr != page_action);
   ASSERT_STREQ("_execute_page_action", page_action->command_name().c_str());
   ASSERT_STREQ("", base::UTF16ToASCII(page_action->description()).c_str());
   ASSERT_EQ(ctrl_f, page_action->accelerator());
@@ -89,7 +89,7 @@
   // should get a command assigned to it.
   const extensions::Command* command =
       CommandsInfo::GetBrowserActionCommand(extension.get());
-  ASSERT_TRUE(command != NULL);
+  ASSERT_TRUE(command != nullptr);
   ASSERT_EQ(ui::VKEY_UNKNOWN, command->accelerator().key_code());
 }
 
diff --git a/extensions/common/api/sockets/sockets_manifest_handler.cc b/extensions/common/api/sockets/sockets_manifest_handler.cc
index 9642632..f75a2a1b 100644
--- a/extensions/common/api/sockets/sockets_manifest_handler.cc
+++ b/extensions/common/api/sockets/sockets_manifest_handler.cc
@@ -38,7 +38,7 @@
   SocketsManifestData* data = SocketsManifestData::Get(extension);
   if (data)
     return data->permission()->Clone().release();
-  return NULL;
+  return nullptr;
 }
 
 base::span<const char* const> SocketsManifestHandler::Keys() const {
diff --git a/extensions/common/api/sockets/sockets_manifest_permission.cc b/extensions/common/api/sockets/sockets_manifest_permission.cc
index 64b8cbd0..097cdb6 100644
--- a/extensions/common/api/sockets/sockets_manifest_permission.cc
+++ b/extensions/common/api/sockets/sockets_manifest_permission.cc
@@ -247,21 +247,21 @@
   if (sockets.udp->bind->as_strings->size() == 0 &&
       sockets.udp->send->as_strings->size() == 0 &&
       sockets.udp->multicast_membership->as_strings->size() == 0) {
-    sockets.udp.reset(NULL);
+    sockets.udp.reset();
   }
 
   sockets.tcp = std::make_unique<Sockets::Tcp>();
   SetHostPatterns(
       sockets.tcp->connect, this, SocketPermissionRequest::TCP_CONNECT);
   if (sockets.tcp->connect->as_strings->size() == 0) {
-    sockets.tcp.reset(NULL);
+    sockets.tcp.reset();
   }
 
   sockets.tcp_server = std::make_unique<Sockets::TcpServer>();
   SetHostPatterns(
       sockets.tcp_server->listen, this, SocketPermissionRequest::TCP_LISTEN);
   if (sockets.tcp_server->listen->as_strings->size() == 0) {
-    sockets.tcp_server.reset(NULL);
+    sockets.tcp_server.reset();
   }
 
   return std::unique_ptr<base::Value>(sockets.ToValue().release());
diff --git a/extensions/common/extension_api.cc b/extensions/common/extension_api.cc
index 7200132..e29fc30 100644
--- a/extensions/common/extension_api.cc
+++ b/extensions/common/extension_api.cc
@@ -61,7 +61,7 @@
       return item;
   }
 
-  return NULL;
+  return nullptr;
 }
 
 const base::Value::Dict* GetSchemaChild(const base::Value::Dict& schema_node,
@@ -76,7 +76,7 @@
       return child_node;
   }
 
-  return NULL;
+  return nullptr;
 }
 
 struct ExtensionAPIStatic {
@@ -88,7 +88,7 @@
     LAZY_INSTANCE_INITIALIZER;
 
 // May override |g_extension_api_static| for a test.
-ExtensionAPI* g_shared_instance_for_test = NULL;
+ExtensionAPI* g_shared_instance_for_test = nullptr;
 
 }  // namespace
 
@@ -258,7 +258,7 @@
 
   auto provider = dependency_providers_.find(feature_type);
   if (provider == dependency_providers_.end())
-    return NULL;
+    return nullptr;
 
   const Feature* feature = provider->second->GetFeature(feature_name);
   // Try getting the feature for the parent API, if this was a child.
diff --git a/extensions/common/extension_set.cc b/extensions/common/extension_set.cc
index 8bf2917..ca73f8e 100644
--- a/extensions/common/extension_set.cc
+++ b/extensions/common/extension_set.cc
@@ -112,7 +112,7 @@
 
 const Extension* ExtensionSet::GetAppByURL(const GURL& url) const {
   const Extension* extension = GetExtensionOrAppByURL(url);
-  return (extension && extension->is_app()) ? extension : NULL;
+  return (extension && extension->is_app()) ? extension : nullptr;
 }
 
 const Extension* ExtensionSet::GetHostedAppByURL(const GURL& url) const {
@@ -121,7 +121,7 @@
       return iter->second.get();
   }
 
-  return NULL;
+  return nullptr;
 }
 
 const Extension* ExtensionSet::GetHostedAppByOverlappingWebExtent(
@@ -131,7 +131,7 @@
       return iter->second.get();
   }
 
-  return NULL;
+  return nullptr;
 }
 
 bool ExtensionSet::InSameExtent(const GURL& old_url,
diff --git a/extensions/common/extensions_client.cc b/extensions/common/extensions_client.cc
index 7fb9e421..f9f5ea2 100644
--- a/extensions/common/extensions_client.cc
+++ b/extensions/common/extensions_client.cc
@@ -20,7 +20,7 @@
 
 namespace {
 
-ExtensionsClient* g_client = NULL;
+ExtensionsClient* g_client = nullptr;
 
 }  // namespace
 
diff --git a/extensions/common/manifest_handler.cc b/extensions/common/manifest_handler.cc
index 6c7290c..86907dc 100644
--- a/extensions/common/manifest_handler.cc
+++ b/extensions/common/manifest_handler.cc
@@ -21,7 +21,7 @@
 
 static base::LazyInstance<ManifestHandlerRegistry>::DestructorAtExit
     g_registry = LAZY_INSTANCE_INITIALIZER;
-static ManifestHandlerRegistry* g_registry_override = NULL;
+static ManifestHandlerRegistry* g_registry_override = nullptr;
 
 }  // namespace
 
@@ -50,12 +50,12 @@
 }
 
 ManifestPermission* ManifestHandler::CreatePermission() {
-  return NULL;
+  return nullptr;
 }
 
 ManifestPermission* ManifestHandler::CreateInitialRequiredPermission(
     const Extension* extension) {
-  return NULL;
+  return nullptr;
 }
 
 // static
@@ -172,7 +172,7 @@
     const std::string& name) {
   ManifestHandlerMap::const_iterator it = handlers_.find(name);
   if (it == handlers_.end())
-    return NULL;
+    return nullptr;
 
   return it->second->CreatePermission();
 }
@@ -203,7 +203,7 @@
   if (new_registry != g_registry.Pointer())
     g_registry_override = new_registry;
   else
-    g_registry_override = NULL;
+    g_registry_override = nullptr;
   return old_registry;
 }
 
diff --git a/extensions/common/manifest_handlers/automation.cc b/extensions/common/manifest_handlers/automation.cc
index fc7bc5cb..b334606 100644
--- a/extensions/common/manifest_handlers/automation.cc
+++ b/extensions/common/manifest_handlers/automation.cc
@@ -110,7 +110,7 @@
 bool AutomationManifestPermission::FromValue(const base::Value* value) {
   std::u16string error;
   automation_info_.reset(
-      AutomationInfo::FromValue(*value, NULL /* install_warnings */, &error)
+      AutomationInfo::FromValue(*value, nullptr /* install_warnings */, &error)
           .release());
   return error.empty();
 }
@@ -207,7 +207,7 @@
         base::WrapUnique(new const AutomationInfo(info->desktop, info->matches,
                                                   info->interact)));
   }
-  return NULL;
+  return nullptr;
 }
 
 // static
diff --git a/extensions/common/manifest_handlers/file_handler_info.cc b/extensions/common/manifest_handlers/file_handler_info.cc
index 23444476..d181847 100644
--- a/extensions/common/manifest_handlers/file_handler_info.cc
+++ b/extensions/common/manifest_handlers/file_handler_info.cc
@@ -46,7 +46,7 @@
     const Extension* extension) {
   FileHandlers* info = static_cast<FileHandlers*>(
       extension->GetManifestData(keys::kFileHandlers));
-  return info ? &info->file_handlers : NULL;
+  return info ? &info->file_handlers : nullptr;
 }
 
 FileHandlersParser::FileHandlersParser() {
diff --git a/extensions/common/manifest_handlers/file_handler_manifest_unittest.cc b/extensions/common/manifest_handlers/file_handler_manifest_unittest.cc
index 1d6b56a..d77a044 100644
--- a/extensions/common/manifest_handlers/file_handler_manifest_unittest.cc
+++ b/extensions/common/manifest_handlers/file_handler_manifest_unittest.cc
@@ -45,7 +45,7 @@
   ASSERT_TRUE(extension.get());
   const FileHandlersInfo* handlers =
       FileHandlers::GetFileHandlers(extension.get());
-  ASSERT_TRUE(handlers != NULL);
+  ASSERT_TRUE(handlers != nullptr);
   ASSERT_EQ(3U, handlers->size());
 
   apps::FileHandlerInfo handler = handlers->at(0);
@@ -79,7 +79,7 @@
   ASSERT_TRUE(extension.get());
   const FileHandlersInfo* handlers =
       FileHandlers::GetFileHandlers(extension.get());
-  ASSERT_TRUE(handlers == NULL);
+  ASSERT_TRUE(handlers == nullptr);
 }
 
 }  // namespace extensions
diff --git a/extensions/common/manifest_handlers/mime_types_handler.cc b/extensions/common/manifest_handlers/mime_types_handler.cc
index 26ebb6f7..f3054f4 100644
--- a/extensions/common/manifest_handlers/mime_types_handler.cc
+++ b/extensions/common/manifest_handlers/mime_types_handler.cc
@@ -127,7 +127,7 @@
       extension->GetManifestData(keys::kMimeTypesHandler));
   if (info)
     return &info->handler_;
-  return NULL;
+  return nullptr;
 }
 
 MimeTypesHandlerParser::MimeTypesHandlerParser() {
diff --git a/extensions/common/message_bundle.cc b/extensions/common/message_bundle.cc
index c4fb529..3e6f7e87 100644
--- a/extensions/common/message_bundle.cc
+++ b/extensions/common/message_bundle.cc
@@ -60,7 +60,7 @@
                                      std::string* error) {
   std::unique_ptr<MessageBundle> message_bundle(new MessageBundle);
   if (!message_bundle->Init(locale_catalogs, error))
-    return NULL;
+    return nullptr;
 
   return message_bundle.release();
 }
diff --git a/extensions/common/message_bundle_unittest.cc b/extensions/common/message_bundle_unittest.cc
index 74315c7..154376b 100644
--- a/extensions/common/message_bundle_unittest.cc
+++ b/extensions/common/message_bundle_unittest.cc
@@ -169,7 +169,7 @@
 
 TEST_F(MessageBundleTest, InitEmptyDictionaries) {
   CreateMessageBundle();
-  EXPECT_TRUE(handler_.get() != NULL);
+  EXPECT_TRUE(handler_.get() != nullptr);
   EXPECT_EQ(0U + ReservedMessagesCount(), handler_->size());
   CheckReservedMessages(handler_.get());
 }
@@ -178,7 +178,7 @@
   catalogs_.push_back(std::move(CreateGoodDictionary()->GetDict()));
   CreateMessageBundle();
 
-  EXPECT_TRUE(handler_.get() != NULL);
+  EXPECT_TRUE(handler_.get() != nullptr);
   EXPECT_EQ(3U + ReservedMessagesCount(), handler_->size());
 
   EXPECT_EQ("message1 A B", handler_->GetL10nMessage("n1"));
@@ -202,7 +202,7 @@
 
   CreateMessageBundle();
 
-  EXPECT_TRUE(handler_.get() != NULL);
+  EXPECT_TRUE(handler_.get() != nullptr);
   EXPECT_EQ(3U + ReservedMessagesCount(), handler_->size());
 
   EXPECT_EQ("message1 B A", handler_->GetL10nMessage("n1"));
@@ -217,46 +217,46 @@
 
   std::string error = CreateMessageBundle();
 
-  EXPECT_TRUE(handler_.get() == NULL);
+  EXPECT_TRUE(handler_.get() == nullptr);
   EXPECT_EQ("Name of a key \"n 5\" is invalid. Only ASCII [a-z], "
             "[A-Z], [0-9] and \"_\" are allowed.", error);
 
   catalogs_[0] = std::move(CreateBadDictionary(NAME_NOT_A_TREE)->GetDict());
   handler_.reset(MessageBundle::Create(catalogs_, &error));
-  EXPECT_TRUE(handler_.get() == NULL);
+  EXPECT_TRUE(handler_.get() == nullptr);
   EXPECT_EQ("Not a valid tree for key n4.", error);
 
   catalogs_[0] = std::move(CreateBadDictionary(EMPTY_NAME_TREE)->GetDict());
   handler_.reset(MessageBundle::Create(catalogs_, &error));
-  EXPECT_TRUE(handler_.get() == NULL);
+  EXPECT_TRUE(handler_.get() == nullptr);
   EXPECT_EQ("There is no \"message\" element for key n4.", error);
 
   catalogs_[0] = std::move(CreateBadDictionary(MISSING_MESSAGE)->GetDict());
   handler_.reset(MessageBundle::Create(catalogs_, &error));
-  EXPECT_TRUE(handler_.get() == NULL);
+  EXPECT_TRUE(handler_.get() == nullptr);
   EXPECT_EQ("There is no \"message\" element for key n1.", error);
 
   catalogs_[0] =
       std::move(CreateBadDictionary(PLACEHOLDER_NOT_A_TREE)->GetDict());
   handler_.reset(MessageBundle::Create(catalogs_, &error));
-  EXPECT_TRUE(handler_.get() == NULL);
+  EXPECT_TRUE(handler_.get() == nullptr);
   EXPECT_EQ("Not a valid \"placeholders\" element for key n1.", error);
 
   catalogs_[0] =
       std::move(CreateBadDictionary(EMPTY_PLACEHOLDER_TREE)->GetDict());
   handler_.reset(MessageBundle::Create(catalogs_, &error));
-  EXPECT_TRUE(handler_.get() == NULL);
+  EXPECT_TRUE(handler_.get() == nullptr);
   EXPECT_EQ("Variable $a$ used but not defined.", error);
 
   catalogs_[0] = std::move(CreateBadDictionary(CONTENT_MISSING)->GetDict());
   handler_.reset(MessageBundle::Create(catalogs_, &error));
-  EXPECT_TRUE(handler_.get() == NULL);
+  EXPECT_TRUE(handler_.get() == nullptr);
   EXPECT_EQ("Invalid \"content\" element for key n1.", error);
 
   catalogs_[0] = std::move(
       CreateBadDictionary(MESSAGE_PLACEHOLDER_DOESNT_MATCH)->GetDict());
   handler_.reset(MessageBundle::Create(catalogs_, &error));
-  EXPECT_TRUE(handler_.get() == NULL);
+  EXPECT_TRUE(handler_.get() == nullptr);
   EXPECT_EQ("Variable $a$ used but not defined.", error);
 }
 
@@ -268,7 +268,7 @@
 
   std::string error = CreateMessageBundle();
 
-  EXPECT_TRUE(handler_.get() == NULL);
+  EXPECT_TRUE(handler_.get() == nullptr);
   std::string expected_error = ErrorUtils::FormatErrorMessage(
       errors::kReservedMessageFound, MessageBundle::kUILocaleKey);
   EXPECT_EQ(expected_error, error);
@@ -277,7 +277,7 @@
 TEST_F(MessageBundleTest, AppendReservedMessagesForLTR) {
   CreateMessageBundle();
 
-  ASSERT_TRUE(handler_.get() != NULL);
+  ASSERT_TRUE(handler_.get() != nullptr);
   ClearDictionary();
   ASSERT_TRUE(AppendReservedMessages("en_US"));
 
@@ -296,7 +296,7 @@
 TEST_F(MessageBundleTest, AppendReservedMessagesForRTL) {
   CreateMessageBundle();
 
-  ASSERT_TRUE(handler_.get() != NULL);
+  ASSERT_TRUE(handler_.get() != nullptr);
   ClearDictionary();
   ASSERT_TRUE(AppendReservedMessages("he"));
 
diff --git a/extensions/common/permissions/api_permission.cc b/extensions/common/permissions/api_permission.cc
index 3c17385a..2c2023d0 100644
--- a/extensions/common/permissions/api_permission.cc
+++ b/extensions/common/permissions/api_permission.cc
@@ -44,7 +44,7 @@
   bool FromValue(const base::Value* value,
                  std::string* /*error*/,
                  std::vector<std::string>* /*unhandled_permissions*/) override {
-    return (value == NULL);
+    return (value == nullptr);
   }
 
   std::unique_ptr<base::Value> ToValue() const override { return nullptr; }
diff --git a/extensions/common/permissions/permission_set.cc b/extensions/common/permissions/permission_set.cc
index eddc976b..4a2ca93 100644
--- a/extensions/common/permissions/permission_set.cc
+++ b/extensions/common/permissions/permission_set.cc
@@ -160,7 +160,7 @@
 }
 
 bool PermissionSet::CheckAPIPermission(APIPermissionID permission) const {
-  return CheckAPIPermissionWithParam(permission, NULL);
+  return CheckAPIPermissionWithParam(permission, nullptr);
 }
 
 bool PermissionSet::CheckAPIPermissionWithParam(
diff --git a/extensions/common/permissions/settings_override_permission.cc b/extensions/common/permissions/settings_override_permission.cc
index 045efdd..4be981a4 100644
--- a/extensions/common/permissions/settings_override_permission.cc
+++ b/extensions/common/permissions/settings_override_permission.cc
@@ -32,7 +32,7 @@
 
 bool SettingsOverrideAPIPermission::Check(
     const APIPermission::CheckParam* param) const {
-  return (param == NULL);
+  return (param == nullptr);
 }
 
 bool SettingsOverrideAPIPermission::Contains(const APIPermission* rhs) const {
diff --git a/extensions/common/permissions/socket_permission_unittest.cc b/extensions/common/permissions/socket_permission_unittest.cc
index afc3e39..e8fc8ec 100644
--- a/extensions/common/permissions/socket_permission_unittest.cc
+++ b/extensions/common/permissions/socket_permission_unittest.cc
@@ -275,13 +275,13 @@
   value.Append("tcp-connect:*.example.com:80");
   value.Append("udp-bind::8080");
   value.Append("udp-send-to::8888");
-  ASSERT_TRUE(permission1->FromValue(&value, NULL, NULL));
+  ASSERT_TRUE(permission1->FromValue(&value, nullptr, nullptr));
 
   EXPECT_FALSE(permission1->Equal(permission2.get()));
 
   std::unique_ptr<base::Value> vtmp(permission1->ToValue());
   ASSERT_TRUE(vtmp);
-  ASSERT_TRUE(permission2->FromValue(vtmp.get(), NULL, NULL));
+  ASSERT_TRUE(permission2->FromValue(vtmp.get(), nullptr, nullptr));
   EXPECT_TRUE(permission1->Equal(permission2.get()));
 }
 
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
index 4033391..1cfbcd8 100644
--- a/extensions/common/url_pattern.cc
+++ b/extensions/common/url_pattern.cc
@@ -451,7 +451,7 @@
 
 bool URLPattern::MatchesSecurityOrigin(const GURL& test) const {
   const GURL* test_url = &test;
-  bool has_inner_url = test.inner_url() != NULL;
+  bool has_inner_url = test.inner_url() != nullptr;
 
   if (has_inner_url) {
     if (!test.SchemeIsFileSystem())
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 02999c2e..63f1826 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -1295,7 +1295,7 @@
   DCHECK_EQ(kMainThreadId, worker_thread_id);
   bindings_system_->messaging_service()->DeliverMessage(
       script_context_set_.get(), target_port_id, message,
-      NULL);  // All render frames.
+      nullptr);  // All render frames.
 }
 
 void Dispatcher::OnDispatchOnConnect(
@@ -1309,7 +1309,7 @@
 
   bindings_system_->messaging_service()->DispatchOnConnect(
       script_context_set_.get(), target_port_id, channel_name, source, info,
-      NULL);  // All render frames.
+      nullptr);  // All render frames.
 }
 
 void Dispatcher::OnDispatchOnDisconnect(int worker_thread_id,
@@ -1318,7 +1318,7 @@
   DCHECK_EQ(kMainThreadId, worker_thread_id);
   bindings_system_->messaging_service()->DispatchOnDisconnect(
       script_context_set_.get(), port_id, error_message,
-      NULL);  // All render frames.
+      nullptr);  // All render frames.
 }
 
 void Dispatcher::DispatchEvent(mojom::DispatchEventParamsPtr params,
diff --git a/extensions/renderer/extensions_renderer_client.cc b/extensions/renderer/extensions_renderer_client.cc
index e011405..ff1b498 100644
--- a/extensions/renderer/extensions_renderer_client.cc
+++ b/extensions/renderer/extensions_renderer_client.cc
@@ -10,7 +10,7 @@
 
 namespace {
 
-ExtensionsRendererClient* g_client = NULL;
+ExtensionsRendererClient* g_client = nullptr;
 
 }  // namespace
 
diff --git a/extensions/renderer/localization_peer_unittest.cc b/extensions/renderer/localization_peer_unittest.cc
index ee443038..3dd7731 100644
--- a/extensions/renderer/localization_peer_unittest.cc
+++ b/extensions/renderer/localization_peer_unittest.cc
@@ -191,7 +191,7 @@
 
 TEST_F(ExtensionLocalizationPeerTest, CreateWithValidInput) {
   SetUpExtensionLocalizationPeer("text/css", GURL(kExtensionUrl_1));
-  EXPECT_TRUE(NULL != filter_peer_.get());
+  EXPECT_TRUE(nullptr != filter_peer_.get());
 }
 
 MATCHER_P(IsURLRequestEqual, status, "") {
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc
index b6ea1359..3b40fd7 100644
--- a/extensions/renderer/module_system.cc
+++ b/extensions/renderer/module_system.cc
@@ -518,7 +518,7 @@
                      ToV8StringUnsafe(GetIsolate(), module_field.c_str()));
   auto maybe = object->SetAccessor(
       context, ToV8StringUnsafe(GetIsolate(), field.c_str()),
-      &ModuleSystem::LazyFieldGetter, NULL, parameters);
+      &ModuleSystem::LazyFieldGetter, nullptr, parameters);
   CHECK(v8_helpers::IsTrue(maybe));
 }
 
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index ead1c7e..9dfd2e4 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -274,7 +274,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   if (web_frame_)
     return content::RenderFrame::FromWebFrame(web_frame_);
-  return NULL;
+  return nullptr;
 }
 
 void ScriptContext::SafeCallFunction(const v8::Local<v8::Function>& function,
@@ -339,7 +339,7 @@
   const Extension* extension = extension_.get();
   if (extension && extension->is_hosted_app() &&
       (api_name == "runtime.connect" || api_name == "runtime.sendMessage")) {
-    extension = NULL;
+    extension = nullptr;
   }
   return ExtensionAPI::GetSharedInstance()->IsAvailable(
       api_name, extension, context_type_, url(), check_alias,
diff --git a/extensions/renderer/user_script_set_manager.cc b/extensions/renderer/user_script_set_manager.cc
index 7177460..843d26b 100644
--- a/extensions/renderer/user_script_set_manager.cc
+++ b/extensions/renderer/user_script_set_manager.cc
@@ -72,7 +72,7 @@
 UserScriptSet* UserScriptSetManager::GetScriptsByHostID(
     const mojom::HostID& host_id) {
   UserScriptSetMap::const_iterator it = scripts_.find(host_id);
-  return it != scripts_.end() ? it->second.get() : NULL;
+  return it != scripts_.end() ? it->second.get() : nullptr;
 }
 
 void UserScriptSetManager::OnUpdateUserScripts(
diff --git a/extensions/renderer/v8_schema_registry.cc b/extensions/renderer/v8_schema_registry.cc
index 59cc2fc8..af56813 100644
--- a/extensions/renderer/v8_schema_registry.cc
+++ b/extensions/renderer/v8_schema_registry.cc
@@ -107,10 +107,10 @@
 std::unique_ptr<NativeHandler> V8SchemaRegistry::AsNativeHandler() {
   std::unique_ptr<ScriptContext> context(
       new ScriptContext(GetOrCreateContext(v8::Isolate::GetCurrent()),
-                        NULL,  // no frame
-                        NULL,  // no extension
+                        nullptr,  // no frame
+                        nullptr,  // no extension
                         Feature::UNSPECIFIED_CONTEXT,
-                        NULL,  // no effective extension
+                        nullptr,  // no effective extension
                         Feature::UNSPECIFIED_CONTEXT));
   return std::unique_ptr<NativeHandler>(
       new SchemaRegistryNativeHandler(this, std::move(context)));
@@ -136,7 +136,7 @@
 }
 
 v8::Local<v8::Object> V8SchemaRegistry::GetSchema(const std::string& api) {
-  if (schema_cache_ != NULL) {
+  if (schema_cache_ != nullptr) {
     v8::Local<v8::Object> cached_schema = schema_cache_->Get(api);
     if (!cached_schema.IsEmpty()) {
       return cached_schema;
diff --git a/extensions/shell/browser/desktop_controller.cc b/extensions/shell/browser/desktop_controller.cc
index b318299..589e315 100644
--- a/extensions/shell/browser/desktop_controller.cc
+++ b/extensions/shell/browser/desktop_controller.cc
@@ -11,7 +11,7 @@
 namespace extensions {
 namespace {
 
-DesktopController* g_instance = NULL;
+DesktopController* g_instance = nullptr;
 
 }  // namespace
 
@@ -27,7 +27,7 @@
 
 DesktopController::~DesktopController() {
   DCHECK(g_instance);
-  g_instance = NULL;
+  g_instance = nullptr;
 }
 
 }  // namespace extensions
diff --git a/extensions/shell/browser/shell_app_delegate.cc b/extensions/shell/browser/shell_app_delegate.cc
index 0a7ffbfd..6defa3a 100644
--- a/extensions/shell/browser/shell_app_delegate.cc
+++ b/extensions/shell/browser/shell_app_delegate.cc
@@ -46,7 +46,7 @@
     content::WebContents* source,
     const content::OpenURLParams& params) {
   NOTIMPLEMENTED();
-  return NULL;
+  return nullptr;
 }
 
 void ShellAppDelegate::AddNewContents(
diff --git a/extensions/shell/browser/shell_audio_controller_chromeos.cc b/extensions/shell/browser/shell_audio_controller_chromeos.cc
index 47efb48..f7905bd 100644
--- a/extensions/shell/browser/shell_audio_controller_chromeos.cc
+++ b/extensions/shell/browser/shell_audio_controller_chromeos.cc
@@ -23,7 +23,7 @@
     if (it->id == node_id)
       return &(*it);
   }
-  return NULL;
+  return nullptr;
 }
 
 }  // namespace
diff --git a/extensions/shell/browser/shell_desktop_controller_aura.cc b/extensions/shell/browser/shell_desktop_controller_aura.cc
index 8f435d91..b777363f 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura.cc
+++ b/extensions/shell/browser/shell_desktop_controller_aura.cc
@@ -163,7 +163,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   chromeos::PowerManagerClient::Get()->RemoveObserver(this);
 #endif
-  extensions::AppWindowClient::Set(NULL);
+  extensions::AppWindowClient::Set(nullptr);
 }
 
 void ShellDesktopControllerAura::PreMainMessageLoopRun() {
diff --git a/extensions/shell/browser/shell_extension_host_delegate.cc b/extensions/shell/browser/shell_extension_host_delegate.cc
index 11bf585..ea06351 100644
--- a/extensions/shell/browser/shell_extension_host_delegate.cc
+++ b/extensions/shell/browser/shell_extension_host_delegate.cc
@@ -27,7 +27,7 @@
   // TODO(jamescook): Create a JavaScriptDialogManager or reuse the one from
   // content_shell.
   NOTREACHED();
-  return NULL;
+  return nullptr;
 }
 
 void ShellExtensionHostDelegate::CreateTab(
diff --git a/extensions/shell/browser/shell_extensions_browser_client.cc b/extensions/shell/browser/shell_extensions_browser_client.cc
index 4e37d62..1f931a32 100644
--- a/extensions/shell/browser/shell_extensions_browser_client.cc
+++ b/extensions/shell/browser/shell_extensions_browser_client.cc
@@ -84,7 +84,7 @@
 BrowserContext* ShellExtensionsBrowserClient::GetOffTheRecordContext(
     BrowserContext* context) {
   // app_shell only supports a single context.
-  return NULL;
+  return nullptr;
 }
 
 BrowserContext* ShellExtensionsBrowserClient::GetOriginalContext(
@@ -176,7 +176,7 @@
 
 ProcessManagerDelegate*
 ShellExtensionsBrowserClient::GetProcessManagerDelegate() const {
-  return NULL;
+  return nullptr;
 }
 
 std::unique_ptr<ExtensionHostDelegate>
@@ -234,7 +234,7 @@
 
 const ComponentExtensionResourceManager*
 ShellExtensionsBrowserClient::GetComponentExtensionResourceManager() {
-  return NULL;
+  return nullptr;
 }
 
 void ShellExtensionsBrowserClient::BroadcastEventToRenderers(
diff --git a/extensions/shell/browser/shell_native_app_window.cc b/extensions/shell/browser/shell_native_app_window.cc
index 8c9c5db8..0cb8d7e 100644
--- a/extensions/shell/browser/shell_native_app_window.cc
+++ b/extensions/shell/browser/shell_native_app_window.cc
@@ -82,7 +82,7 @@
 
 gfx::NativeView ShellNativeAppWindow::GetHostView() const {
   NOTIMPLEMENTED();
-  return NULL;
+  return nullptr;
 }
 
 gfx::Point ShellNativeAppWindow::GetDialogPosition(const gfx::Size& size) {
@@ -129,7 +129,7 @@
 
 SkRegion* ShellNativeAppWindow::GetDraggableRegion() {
   NOTIMPLEMENTED();
-  return NULL;
+  return nullptr;
 }
 
 void ShellNativeAppWindow::UpdateShape(std::unique_ptr<ShapeRects> rects) {
diff --git a/extensions/shell/browser/shell_network_controller_chromeos.cc b/extensions/shell/browser/shell_network_controller_chromeos.cc
index b0aaf15..1e7152e 100644
--- a/extensions/shell/browser/shell_network_controller_chromeos.cc
+++ b/extensions/shell/browser/shell_network_controller_chromeos.cc
@@ -65,7 +65,7 @@
       base::BindRepeating(&HandleEnableWifiError));
 
   // If we're unconnected, trigger a connection attempt and start scanning.
-  NetworkConnectionStateChanged(NULL);
+  NetworkConnectionStateChanged(nullptr);
 }
 
 ShellNetworkController::~ShellNetworkController() {
@@ -130,7 +130,7 @@
   return network &&
                  (network->IsConnectedState() || network->IsConnectingState())
              ? network
-             : NULL;
+             : nullptr;
 }
 
 void ShellNetworkController::SetScanningEnabled(bool enabled) {
@@ -161,7 +161,7 @@
       state_ == STATE_WAITING_FOR_PREFERRED_RESULT)
     return;
 
-  const ash::NetworkState* best_network = NULL;
+  const ash::NetworkState* best_network = nullptr;
   bool can_connect_to_preferred_network = false;
 
   ash::NetworkHandler* handler = ash::NetworkHandler::Get();
diff --git a/extensions/shell/renderer/shell_content_renderer_client.cc b/extensions/shell/renderer/shell_content_renderer_client.cc
index 7852ff4..948d8d2 100644
--- a/extensions/shell/renderer/shell_content_renderer_client.cc
+++ b/extensions/shell/renderer/shell_content_renderer_client.cc
@@ -74,7 +74,7 @@
     content::RenderFrame* render_frame,
     const base::FilePath& plugin_path) {
   // Don't provide a custom "failed to load" plugin.
-  return NULL;
+  return nullptr;
 }
 
 void ShellContentRendererClient::WillSendRequest(
diff --git a/extensions/test/extensions_unittests_main.cc b/extensions/test/extensions_unittests_main.cc
index a5cc017..4e2355f 100644
--- a/extensions/test/extensions_unittests_main.cc
+++ b/extensions/test/extensions_unittests_main.cc
@@ -85,7 +85,7 @@
 }
 
 void ExtensionsTestSuite::Shutdown() {
-  extensions::ExtensionsClient::Set(NULL);
+  extensions::ExtensionsClient::Set(nullptr);
   client_.reset();
 
   ui::ResourceBundle::CleanupSharedInstance();
diff --git a/gpu/command_buffer/service/display_compositor_memory_and_task_controller_on_gpu.cc b/gpu/command_buffer/service/display_compositor_memory_and_task_controller_on_gpu.cc
index e029666..053e3bd6 100644
--- a/gpu/command_buffer/service/display_compositor_memory_and_task_controller_on_gpu.cc
+++ b/gpu/command_buffer/service/display_compositor_memory_and_task_controller_on_gpu.cc
@@ -42,43 +42,10 @@
       sync_point_manager_(sync_point_manager),
       gpu_preferences_(gpu_preferences),
       gpu_driver_bug_workarounds_(gpu_driver_bug_workarounds),
-      gpu_feature_info_(gpu_feature_info),
-      should_have_memory_tracker_(true) {
+      gpu_feature_info_(gpu_feature_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
 }
 
-// Used for InProcessCommandBuffer.
-DisplayCompositorMemoryAndTaskControllerOnGpu::
-    DisplayCompositorMemoryAndTaskControllerOnGpu(
-        CommandBufferTaskExecutor* task_executor,
-        ImageFactory* image_factory)
-    : shared_context_state_(task_executor->GetSharedContextState()),
-      command_buffer_id_(GenNextCommandBufferId()),
-      mailbox_manager_(task_executor->mailbox_manager()),
-      image_factory_(image_factory),
-      shared_image_manager_(task_executor->shared_image_manager()),
-      sync_point_manager_(task_executor->sync_point_manager()),
-      gpu_preferences_(task_executor->gpu_preferences()),
-      gpu_driver_bug_workarounds_(
-          GpuDriverBugWorkarounds(task_executor->gpu_feature_info()
-                                      .enabled_gpu_driver_bug_workarounds)),
-      gpu_feature_info_(task_executor->gpu_feature_info()) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
-
-  // Android WebView won't have a memory tracker.
-  if (task_executor->ShouldCreateMemoryTracker()) {
-    should_have_memory_tracker_ = true;
-    memory_tracker_ = std::make_unique<GpuCommandBufferMemoryTracker>(
-        command_buffer_id_,
-        base::trace_event::MemoryDumpManager::GetInstance()
-            ->GetTracingProcessId(),
-        base::ThreadTaskRunnerHandle::Get(),
-        /* obserer=*/nullptr);
-  } else {
-    should_have_memory_tracker_ = false;
-  }
-}
-
 DisplayCompositorMemoryAndTaskControllerOnGpu::
     ~DisplayCompositorMemoryAndTaskControllerOnGpu() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
@@ -86,13 +53,8 @@
 
 MemoryTracker* DisplayCompositorMemoryAndTaskControllerOnGpu::memory_tracker()
     const {
-  if (!should_have_memory_tracker_)
-    return nullptr;
-
-  if (memory_tracker_)
-    return memory_tracker_.get();
-  else
-    return shared_context_state_->memory_tracker();
+  DCHECK(shared_context_state_);
+  return shared_context_state_->memory_tracker();
 }
 
 // Static
diff --git a/gpu/command_buffer/service/display_compositor_memory_and_task_controller_on_gpu.h b/gpu/command_buffer/service/display_compositor_memory_and_task_controller_on_gpu.h
index 7f03ddb1..b7efc30 100644
--- a/gpu/command_buffer/service/display_compositor_memory_and_task_controller_on_gpu.h
+++ b/gpu/command_buffer/service/display_compositor_memory_and_task_controller_on_gpu.h
@@ -16,7 +16,6 @@
 #include "gpu/ipc/common/command_buffer_id.h"
 
 namespace gpu {
-class CommandBufferTaskExecutor;
 class ImageFactory;
 class MailboxManager;
 class SyncPointManager;
@@ -29,7 +28,6 @@
 // compositor.
 class GPU_GLES2_EXPORT DisplayCompositorMemoryAndTaskControllerOnGpu {
  public:
-  // Used for SkiaRenderer.
   DisplayCompositorMemoryAndTaskControllerOnGpu(
       scoped_refptr<SharedContextState> shared_context_state,
       MailboxManager* mailbox_manager,
@@ -39,10 +37,6 @@
       const GpuPreferences& gpu_preferences,
       const GpuDriverBugWorkarounds& gpu_driver_bug_workarounds,
       const GpuFeatureInfo& gpu_feature_info);
-  // Used for InProcessCommandBuffer.
-  DisplayCompositorMemoryAndTaskControllerOnGpu(
-      CommandBufferTaskExecutor* task_executor,
-      ImageFactory* image_factory);
   DisplayCompositorMemoryAndTaskControllerOnGpu(
       const DisplayCompositorMemoryAndTaskControllerOnGpu&) = delete;
   DisplayCompositorMemoryAndTaskControllerOnGpu& operator=(
@@ -85,10 +79,6 @@
   GpuDriverBugWorkarounds gpu_driver_bug_workarounds_;
   const GpuFeatureInfo& gpu_feature_info_;
 
-  // Only needed for InProcessCommandBuffer.
-  bool should_have_memory_tracker_ = false;
-  std::unique_ptr<MemoryTracker> memory_tracker_;
-
   SEQUENCE_CHECKER(gpu_sequence_checker_);
 };
 
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index f8533e7..2e380d0 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -718,6 +718,10 @@
   }
 }
 
+gl::GLDisplay* SharedContextState::display() {
+  return surface_.get()->GetGLDisplay();
+}
+
 bool SharedContextState::initialized() const {
   return true;
 }
diff --git a/gpu/command_buffer/service/shared_context_state.h b/gpu/command_buffer/service/shared_context_state.h
index 5f7a8b19..f7a4af4 100644
--- a/gpu/command_buffer/service/shared_context_state.h
+++ b/gpu/command_buffer/service/shared_context_state.h
@@ -36,6 +36,7 @@
 
 namespace gl {
 class GLContext;
+class GLDisplay;
 class GLShareGroup;
 class GLSurface;
 }  // namespace gl
@@ -129,6 +130,7 @@
   gl::GLContext* context() { return context_.get(); }
   gl::GLContext* real_context() { return real_context_.get(); }
   gl::GLSurface* surface() { return surface_.get(); }
+  gl::GLDisplay* display();
   viz::VulkanContextProvider* vk_context_provider() {
     return vk_context_provider_;
   }
diff --git a/gpu/command_buffer/service/shared_image_interface_in_process.cc b/gpu/command_buffer/service/shared_image_interface_in_process.cc
index 687b5c8..83e413d 100644
--- a/gpu/command_buffer/service/shared_image_interface_in_process.cc
+++ b/gpu/command_buffer/service/shared_image_interface_in_process.cc
@@ -33,7 +33,6 @@
   const raw_ptr<gpu::SharedContextState> context_state;
   const raw_ptr<SharedImageManager> shared_image_manager;
   const raw_ptr<ImageFactory> image_factory;
-  const raw_ptr<MemoryTracker> memory_tracker;
   const bool is_for_display_compositor;
 
   SetUpOnGpuParams(const GpuPreferences& gpu_preferences,
@@ -42,7 +41,6 @@
                    gpu::SharedContextState* context_state,
                    SharedImageManager* shared_image_manager,
                    ImageFactory* image_factory,
-                   MemoryTracker* memory_tracker,
                    bool is_for_display_compositor)
       : gpu_preferences(gpu_preferences),
         gpu_workarounds(gpu_workarounds),
@@ -50,7 +48,6 @@
         context_state(context_state),
         shared_image_manager(shared_image_manager),
         image_factory(image_factory),
-        memory_tracker(memory_tracker),
         is_for_display_compositor(is_for_display_compositor) {}
 
   ~SetUpOnGpuParams() = default;
@@ -61,8 +58,7 @@
 
 SharedImageInterfaceInProcess::SharedImageInterfaceInProcess(
     SingleTaskSequence* task_sequence,
-    DisplayCompositorMemoryAndTaskControllerOnGpu* display_controller,
-    raw_ptr<CommandBufferHelper> command_buffer_helper)
+    DisplayCompositorMemoryAndTaskControllerOnGpu* display_controller)
     : SharedImageInterfaceInProcess(
           task_sequence,
           display_controller->sync_point_manager(),
@@ -72,9 +68,7 @@
           display_controller->shared_context_state(),
           display_controller->shared_image_manager(),
           display_controller->image_factory(),
-          display_controller->memory_tracker(),
-          /*is_for_display_compositor=*/true,
-          command_buffer_helper) {}
+          /*is_for_display_compositor=*/true) {}
 
 SharedImageInterfaceInProcess::SharedImageInterfaceInProcess(
     SingleTaskSequence* task_sequence,
@@ -85,23 +79,19 @@
     gpu::SharedContextState* context_state,
     SharedImageManager* shared_image_manager,
     ImageFactory* image_factory,
-    MemoryTracker* memory_tracker,
-    bool is_for_display_compositor,
-    raw_ptr<CommandBufferHelper> command_buffer_helper)
+    bool is_for_display_compositor)
     : task_sequence_(task_sequence),
       command_buffer_id_(
           DisplayCompositorMemoryAndTaskControllerOnGpu::NextCommandBufferId()),
-      command_buffer_helper_(command_buffer_helper),
       shared_image_manager_(shared_image_manager),
       sync_point_manager_(sync_point_manager) {
   DETACH_FROM_SEQUENCE(gpu_sequence_checker_);
   task_sequence_->ScheduleTask(
-      base::BindOnce(&SharedImageInterfaceInProcess::SetUpOnGpu,
-                     base::Unretained(this),
-                     std::make_unique<SetUpOnGpuParams>(
-                         gpu_preferences, gpu_workarounds, gpu_feature_info,
-                         context_state, shared_image_manager, image_factory,
-                         memory_tracker, is_for_display_compositor)),
+      base::BindOnce(
+          &SharedImageInterfaceInProcess::SetUpOnGpu, base::Unretained(this),
+          std::make_unique<SetUpOnGpuParams>(
+              gpu_preferences, gpu_workarounds, gpu_feature_info, context_state,
+              shared_image_manager, image_factory, is_for_display_compositor)),
       {});
 }
 
@@ -127,7 +117,8 @@
             params->gpu_preferences, params->gpu_workarounds,
             params->gpu_feature_info, params->context_state,
             params->shared_image_manager, params->image_factory,
-            params->memory_tracker, params->is_for_display_compositor);
+            params->context_state->memory_tracker(),
+            params->is_for_display_compositor);
         return shared_image_factory;
       },
       std::move(params));
@@ -233,10 +224,7 @@
   if (!shared_image_factory_->CreateSharedImage(
           mailbox, format, size, color_space, surface_origin, alpha_type,
           surface_handle, usage)) {
-    if (command_buffer_helper_) {
-      // Signal errors by losing the command buffer.
-      command_buffer_helper_->SetError();
-    }
+    context_state_->MarkContextLost();
     return;
   }
   sync_point_client_state_->ReleaseFenceSync(sync_token.release_count());
@@ -292,10 +280,7 @@
   if (!shared_image_factory_->CreateSharedImage(
           mailbox, format, size, color_space, surface_origin, alpha_type, usage,
           pixel_data)) {
-    if (command_buffer_helper_) {
-      // Signal errors by losing the command buffer.
-      command_buffer_helper_->SetError();
-    }
+    context_state_->MarkContextLost();
     return;
   }
   sync_point_client_state_->ReleaseFenceSync(sync_token.release_count());
@@ -372,10 +357,7 @@
           mailbox, kDisplayCompositorClientId, std::move(handle), format, plane,
           surface_handle, size, color_space, surface_origin, alpha_type,
           usage)) {
-    if (command_buffer_helper_) {
-      // Signal errors by losing the command buffer.
-      command_buffer_helper_->SetError();
-    }
+    context_state_->MarkContextLost();
     return;
   }
   sync_point_client_state_->ReleaseFenceSync(sync_token.release_count());
@@ -447,10 +429,7 @@
 
   if (!shared_image_factory_ ||
       !shared_image_factory_->UpdateSharedImage(mailbox)) {
-    if (command_buffer_helper_) {
-      // Signal errors by losing the command buffer.
-      command_buffer_helper_->SetError();
-    }
+    context_state_->MarkContextLost();
     return;
   }
   sync_point_client_state_->ReleaseFenceSync(sync_token.release_count());
@@ -476,10 +455,7 @@
 
   if (!shared_image_factory_ ||
       !shared_image_factory_->DestroySharedImage(mailbox)) {
-    if (command_buffer_helper_) {
-      // Signal errors by losing the command buffer.
-      command_buffer_helper_->SetError();
-    }
+    context_state_->MarkContextLost();
   }
 }
 
@@ -524,23 +500,10 @@
   return shared_image_manager_->GetNativePixmap(mailbox);
 }
 
-void SharedImageInterfaceInProcess::WrapTaskWithGpuUrl(base::OnceClosure task) {
-  if (command_buffer_helper_) {
-    command_buffer_helper_->WrapTaskWithGpuCheck(std::move(task));
-  } else {
-    std::move(task).Run();
-  }
-}
-
 void SharedImageInterfaceInProcess::ScheduleGpuTask(
     base::OnceClosure task,
     std::vector<SyncToken> sync_token_fences) {
-  base::OnceClosure gpu_task =
-      base::BindOnce(&SharedImageInterfaceInProcess::WrapTaskWithGpuUrl,
-                     base::Unretained(this), std::move(task));
-
-  task_sequence_->ScheduleTask(std::move(gpu_task),
-                               std::move(sync_token_fences));
+  task_sequence_->ScheduleTask(std::move(task), std::move(sync_token_fences));
 }
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_interface_in_process.h b/gpu/command_buffer/service/shared_image_interface_in_process.h
index a7c0cf3..25b1c0ef 100644
--- a/gpu/command_buffer/service/shared_image_interface_in_process.h
+++ b/gpu/command_buffer/service/shared_image_interface_in_process.h
@@ -29,24 +29,14 @@
 class GpuDriverBugWorkarounds;
 struct GpuFeatureInfo;
 class ImageFactory;
-class MemoryTracker;
 struct SyncToken;
 
 // This is an implementation of the SharedImageInterface to be used on the viz
 // compositor thread. This class also implements the corresponding parts
 // happening on gpu thread.
-// TODO(weiliangc): Currently this is implemented as backed by
-// InProcessCommandBuffer. Add constructor for using with SkiaRenderer.
 class GPU_GLES2_EXPORT SharedImageInterfaceInProcess
     : public SharedImageInterface {
  public:
-  // This is only implemented by InProcessCommandBuffer.
-  class GPU_GLES2_EXPORT CommandBufferHelper {
-   public:
-    virtual void SetError() = 0;
-    virtual void WrapTaskWithGpuCheck(base::OnceClosure task) = 0;
-  };
-
   // The callers must guarantee that the instances passed via pointers are kept
   // alive for as long as the instance of this class is alive. This can be
   // achieved by ensuring that the ownership of the created
@@ -54,25 +44,21 @@
   // pointers.
   SharedImageInterfaceInProcess(
       SingleTaskSequence* task_sequence,
-      DisplayCompositorMemoryAndTaskControllerOnGpu* display_controller,
-      raw_ptr<CommandBufferHelper> command_buffer_helper);
+      DisplayCompositorMemoryAndTaskControllerOnGpu* display_controller);
   // The callers must guarantee that the instances passed via pointers are kept
   // alive for as long as the instance of this class is alive. This can be
   // achieved by ensuring that the ownership of the created
   // SharedImageInterfaceInProcess is the same as the ownership of the passed in
   // pointers.
-  SharedImageInterfaceInProcess(
-      SingleTaskSequence* task_sequence,
-      SyncPointManager* sync_point_manager,
-      const GpuPreferences& gpu_preferences,
-      const GpuDriverBugWorkarounds& gpu_workarounds,
-      const GpuFeatureInfo& gpu_feature_info,
-      gpu::SharedContextState* context_state,
-      SharedImageManager* shared_image_manager,
-      ImageFactory* image_factory,
-      MemoryTracker* tracker,
-      bool is_for_display_compositor = false,
-      raw_ptr<CommandBufferHelper> command_buffer_helper = nullptr);
+  SharedImageInterfaceInProcess(SingleTaskSequence* task_sequence,
+                                SyncPointManager* sync_point_manager,
+                                const GpuPreferences& gpu_preferences,
+                                const GpuDriverBugWorkarounds& gpu_workarounds,
+                                const GpuFeatureInfo& gpu_feature_info,
+                                gpu::SharedContextState* context_state,
+                                SharedImageManager* shared_image_manager,
+                                ImageFactory* image_factory,
+                                bool is_for_display_compositor);
 
   SharedImageInterfaceInProcess(const SharedImageInterfaceInProcess&) = delete;
   SharedImageInterfaceInProcess& operator=(
@@ -244,7 +230,6 @@
   // SharedImageInterfaceInProcess.
   raw_ptr<SingleTaskSequence> task_sequence_;
   const CommandBufferId command_buffer_id_;
-  raw_ptr<CommandBufferHelper> command_buffer_helper_;
 
   base::OnceCallback<std::unique_ptr<SharedImageFactory>()> create_factory_;
 
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index fa3a5e3..0ef83a5 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -97,19 +97,12 @@
 
 }  // namespace
 
-void InProcessCommandBuffer::SetError() {
-  // Signal errors by losing the command buffer.
-  command_buffer_->SetParseError(error::kLostContext);
-}
-
-void InProcessCommandBuffer::WrapTaskWithGpuCheck(base::OnceClosure task) {
-  RunTaskOnGpuThread(std::move(task));
-}
-
 InProcessCommandBuffer::InProcessCommandBuffer(
     CommandBufferTaskExecutor* task_executor,
     const GURL& active_url)
-    : active_url_(active_url),
+    : command_buffer_id_(
+          DisplayCompositorMemoryAndTaskControllerOnGpu::NextCommandBufferId()),
+      active_url_(active_url),
       flush_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                    base::WaitableEvent::InitialState::NOT_SIGNALED),
       task_executor_(task_executor),
@@ -217,7 +210,12 @@
   if (result == gpu::ContextResult::kSuccess) {
     capabilities_ = capabilities;
     shared_image_interface_ = std::make_unique<SharedImageInterfaceInProcess>(
-        task_sequence_, gpu_dependency_.get(), this);
+        task_sequence_, task_executor_->sync_point_manager(),
+        task_executor_->gpu_preferences(),
+        context_group_->feature_info()->workarounds(),
+        task_executor_->gpu_feature_info(), context_state_.get(),
+        task_executor_->shared_image_manager(), image_factory,
+        /*is_for_display_compositor=*/false);
   }
 
   return result;
@@ -229,10 +227,6 @@
   TRACE_EVENT0("gpu", "InProcessCommandBuffer::InitializeOnGpuThread");
   UpdateActiveUrl();
 
-  gpu_dependency_ =
-      std::make_unique<DisplayCompositorMemoryAndTaskControllerOnGpu>(
-          task_executor_, params.image_factory);
-
   GpuDriverBugWorkarounds workarounds(
       task_executor_->gpu_feature_info().enabled_gpu_driver_bug_workarounds);
 
@@ -243,7 +237,7 @@
         base::trace_event::MemoryDumpManager::GetInstance()
             ->GetTracingProcessId();
     memory_tracker = std::make_unique<GpuCommandBufferMemoryTracker>(
-        gpu_dependency_->command_buffer_id(), client_tracing_id,
+        GetCommandBufferID(), client_tracing_id,
         base::ThreadTaskRunnerHandle::Get(), /* obserer=*/nullptr);
   }
 
@@ -280,7 +274,7 @@
   }
 
   command_buffer_ = std::make_unique<CommandBufferService>(
-      this, gpu_dependency_->memory_tracker());
+      this, context_group_->memory_tracker());
 
   context_state_ = task_executor_->GetSharedContextState();
 
@@ -321,7 +315,7 @@
     std::unique_ptr<webgpu::WebGPUDecoder> webgpu_decoder(
         webgpu::WebGPUDecoder::Create(
             this, command_buffer_.get(), task_executor_->shared_image_manager(),
-            gpu_dependency_->memory_tracker(), task_executor_->outputter(),
+            context_group_->memory_tracker(), task_executor_->outputter(),
             task_executor_->gpu_preferences(), context_state_));
     gpu::ContextResult result =
         webgpu_decoder->Initialize(task_executor_->gpu_feature_info());
@@ -356,7 +350,7 @@
       decoder_.reset(raster::RasterDecoder::Create(
           this, command_buffer_.get(), task_executor_->outputter(),
           task_executor_->gpu_feature_info(), task_executor_->gpu_preferences(),
-          gpu_dependency_->memory_tracker(),
+          context_group_->memory_tracker(),
           task_executor_->shared_image_manager(), params.image_factory,
           context_state_, true /*is_privileged*/));
     } else {
@@ -525,8 +519,6 @@
     context_state_->MakeCurrent(nullptr);
   context_state_ = nullptr;
 
-  gpu_dependency_.reset();
-
   return true;
 }
 
@@ -1001,7 +993,7 @@
 }
 
 CommandBufferId InProcessCommandBuffer::GetCommandBufferID() const {
-  return gpu_dependency_->command_buffer_id();
+  return command_buffer_id_;
 }
 
 void InProcessCommandBuffer::FlushPendingWork() {
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h
index 06ca282..5adc4e0f 100644
--- a/gpu/ipc/in_process_command_buffer.h
+++ b/gpu/ipc/in_process_command_buffer.h
@@ -89,8 +89,7 @@
     : public CommandBuffer,
       public GpuControl,
       public CommandBufferServiceClient,
-      public DecoderClient,
-      public SharedImageInterfaceInProcess::CommandBufferHelper {
+      public DecoderClient {
  public:
   InProcessCommandBuffer(CommandBufferTaskExecutor* task_executor,
                          const GURL& active_url);
@@ -177,10 +176,6 @@
 
   gpu::SharedImageInterface* GetSharedImageInterface() const;
 
-  // SharedImageInterfaceInProcess::CommandBufferHelper implementation:
-  void SetError() override;
-  void WrapTaskWithGpuCheck(base::OnceClosure task) override;
-
  private:
   struct InitializeOnGpuThreadParams {
     const ContextCreationAttribs& attribs;
@@ -265,12 +260,11 @@
 
   void HandleReturnDataOnOriginThread(std::vector<uint8_t> data);
 
+  const CommandBufferId command_buffer_id_;
   const ContextUrl active_url_;
 
   // Members accessed on the gpu thread (possibly with the exception of
   // creation):
-  std::unique_ptr<DisplayCompositorMemoryAndTaskControllerOnGpu>
-      gpu_dependency_;
   bool use_virtualized_gl_context_ = false;
   raw_ptr<raster::GrShaderCache> gr_shader_cache_ = nullptr;
   scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm
index d169596..5e82755 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_coordinator.mm
@@ -253,8 +253,9 @@
 
 - (void)sendTabToTargetDeviceCacheGUID:(NSString*)cacheGUID
                       targetDeviceName:(NSString*)deviceName {
-  send_tab_to_self::RecordDeviceClicked(
-      send_tab_to_self::ShareEntryPoint::kShareMenu);
+  send_tab_to_self::RecordSendingEvent(
+      send_tab_to_self::ShareEntryPoint::kShareMenu,
+      send_tab_to_self::SendingEvent::kClickItem);
 
   SendTabToSelfSyncServiceFactory::GetForBrowserState(
       self.browser->GetBrowserState())
@@ -293,6 +294,14 @@
   switch (*displayReason) {
     case send_tab_to_self::EntryPointDisplayReason::kInformNoTargetDevice:
     case send_tab_to_self::EntryPointDisplayReason::kOfferFeature: {
+      const auto sending_event =
+          *displayReason ==
+                  send_tab_to_self::EntryPointDisplayReason::kOfferFeature
+              ? send_tab_to_self::SendingEvent::kShowDeviceList
+              : send_tab_to_self::SendingEvent::kShowNoTargetDeviceMessage;
+      send_tab_to_self::RecordSendingEvent(
+          send_tab_to_self::ShareEntryPoint::kShareMenu, sending_event);
+
       ChromeBrowserState* browserState = self.browser->GetBrowserState();
       send_tab_to_self::SendTabToSelfSyncService* syncService =
           SendTabToSelfSyncServiceFactory::GetForBrowserState(browserState);
@@ -328,6 +337,10 @@
       break;
     }
     case send_tab_to_self::EntryPointDisplayReason::kOfferSignIn: {
+      send_tab_to_self::RecordSendingEvent(
+          send_tab_to_self::ShareEntryPoint::kShareMenu,
+          send_tab_to_self::SendingEvent::kShowSigninPromo);
+
       __weak __typeof(self) weakSelf = self;
       ShowSigninCommandCompletionCallback callback = ^(BOOL succeeded) {
         [weakSelf onSigninComplete:succeeded];
diff --git a/mojo/core/BUILD.gn b/mojo/core/BUILD.gn
index 935295d4..a2ab802d 100644
--- a/mojo/core/BUILD.gn
+++ b/mojo/core/BUILD.gn
@@ -63,6 +63,7 @@
       "invitation_dispatcher.h",
       "ipcz_api.h",
       "ipcz_driver/driver.h",
+      "ipcz_driver/invitation.h",
       "ipcz_driver/shared_buffer.h",
       "ipcz_driver/transmissible_platform_handle.h",
       "ipcz_driver/transport.h",
@@ -97,6 +98,7 @@
       "invitation_dispatcher.cc",
       "ipcz_api.cc",
       "ipcz_driver/driver.cc",
+      "ipcz_driver/invitation.cc",
       "ipcz_driver/mojo_trap.cc",
       "ipcz_driver/mojo_trap.h",
       "ipcz_driver/object.cc",
diff --git a/mojo/core/channel.cc b/mojo/core/channel.cc
index 4ba6bd7..4afa959 100644
--- a/mojo/core/channel.cc
+++ b/mojo/core/channel.cc
@@ -12,6 +12,7 @@
 #include <utility>
 
 #include "base/check_op.h"
+#include "base/functional/overloaded.h"
 #include "base/logging.h"
 #include "base/memory/nonscannable_memory.h"
 #include "base/memory/ptr_util.h"
@@ -888,12 +889,24 @@
 // static
 scoped_refptr<Channel> Channel::CreateForIpczDriver(
     Delegate* delegate,
-    PlatformChannelEndpoint endpoint,
+    Endpoint endpoint,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
-  scoped_refptr<Channel> channel =
-      Create(delegate, ConnectionParams(std::move(endpoint)),
-             HandlePolicy::kAcceptHandles, std::move(io_task_runner));
-  return channel;
+#if BUILDFLAG(IS_NACL)
+  return nullptr;
+#else
+  ConnectionParams params =
+      absl::visit(base::Overloaded{
+                      [](PlatformChannelEndpoint& endpoint) {
+                        return ConnectionParams(std::move(endpoint));
+                      },
+                      [](PlatformChannelServerEndpoint& endpoint) {
+                        return ConnectionParams(std::move(endpoint));
+                      },
+                  },
+                  endpoint);
+  return Create(delegate, std::move(params), HandlePolicy::kAcceptHandles,
+                std::move(io_task_runner));
+#endif
 }
 
 void Channel::ShutDown() {
diff --git a/mojo/core/channel.h b/mojo/core/channel.h
index a31ab6e3..1afce974 100644
--- a/mojo/core/channel.h
+++ b/mojo/core/channel.h
@@ -18,10 +18,12 @@
 #include "build/build_config.h"
 #include "mojo/core/connection_params.h"
 #include "mojo/core/platform_handle_in_transit.h"
+#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
+#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
-namespace mojo {
-namespace core {
+namespace mojo::core {
 
 const size_t kChannelMessageAlignment = 8;
 
@@ -313,9 +315,11 @@
   // header, and the Channel is no longer responsible for encoding or decoding
   // any metadata about transmitted PlatformHandles, since the ipcz driver takes
   // care of that.
+  using Endpoint =
+      absl::variant<PlatformChannelEndpoint, PlatformChannelServerEndpoint>;
   static scoped_refptr<Channel> CreateForIpczDriver(
       Delegate* delegate,
-      PlatformChannelEndpoint endpoint,
+      Endpoint endpoint,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
 
   Channel(const Channel&) = delete;
@@ -481,7 +485,6 @@
   base::Process remote_process_;
 };
 
-}  // namespace core
-}  // namespace mojo
+}  // namespace mojo::core
 
 #endif  // MOJO_CORE_CHANNEL_H_
diff --git a/mojo/core/core_ipcz.cc b/mojo/core/core_ipcz.cc
index bc8bb18..b1a55298 100644
--- a/mojo/core/core_ipcz.cc
+++ b/mojo/core/core_ipcz.cc
@@ -21,6 +21,7 @@
 #include "base/synchronization/lock.h"
 #include "base/time/time.h"
 #include "mojo/core/ipcz_api.h"
+#include "mojo/core/ipcz_driver/invitation.h"
 #include "mojo/core/ipcz_driver/mojo_trap.h"
 #include "mojo/core/ipcz_driver/shared_buffer.h"
 #include "mojo/core/ipcz_driver/shared_buffer_mapping.h"
@@ -688,7 +689,12 @@
 
 MojoResult MojoCreateInvitationIpcz(const MojoCreateInvitationOptions* options,
                                     MojoHandle* invitation_handle) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  if (!invitation_handle ||
+      (options && options->struct_size < sizeof(*options))) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  *invitation_handle = ipcz_driver::Invitation::MakeBoxed();
+  return MOJO_RESULT_OK;
 }
 
 MojoResult MojoAttachMessagePipeToInvitationIpcz(
@@ -697,7 +703,14 @@
     uint32_t name_num_bytes,
     const MojoAttachMessagePipeToInvitationOptions* options,
     MojoHandle* message_pipe_handle) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  auto* invitation = ipcz_driver::Invitation::FromBox(invitation_handle);
+  if (!invitation || !message_pipe_handle ||
+      (options && options->struct_size < sizeof(*options))) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  return invitation->Attach(
+      base::make_span(static_cast<const uint8_t*>(name), name_num_bytes),
+      message_pipe_handle);
 }
 
 MojoResult MojoExtractMessagePipeFromInvitationIpcz(
@@ -706,7 +719,14 @@
     uint32_t name_num_bytes,
     const MojoExtractMessagePipeFromInvitationOptions* options,
     MojoHandle* message_pipe_handle) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  auto* invitation = ipcz_driver::Invitation::FromBox(invitation_handle);
+  if (!invitation || !message_pipe_handle ||
+      (options && options->struct_size < sizeof(*options))) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  return invitation->Extract(
+      base::make_span(static_cast<const uint8_t*>(name), name_num_bytes),
+      message_pipe_handle);
 }
 
 MojoResult MojoSendInvitationIpcz(
@@ -716,14 +736,32 @@
     MojoProcessErrorHandler error_handler,
     uintptr_t error_handler_context,
     const MojoSendInvitationOptions* options) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  auto* invitation = ipcz_driver::Invitation::FromBox(invitation_handle);
+  if (!invitation) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  const MojoResult result =
+      invitation->Send(process_handle, transport_endpoint, error_handler,
+                       error_handler_context, options);
+  if (result == MOJO_RESULT_OK) {
+    // On success, the invitation is consumed.
+    GetIpczAPI().Close(invitation_handle, IPCZ_NO_FLAGS, nullptr);
+  }
+  return result;
 }
 
 MojoResult MojoAcceptInvitationIpcz(
     const MojoInvitationTransportEndpoint* transport_endpoint,
     const MojoAcceptInvitationOptions* options,
     MojoHandle* invitation_handle) {
-  return MOJO_RESULT_UNIMPLEMENTED;
+  if (!transport_endpoint || !invitation_handle ||
+      (options && options->struct_size < sizeof(*options))) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  *invitation_handle =
+      ipcz_driver::Invitation::Accept(transport_endpoint, options);
+  return MOJO_RESULT_OK;
 }
 
 MojoResult MojoSetQuotaIpcz(MojoHandle handle,
diff --git a/mojo/core/core_ipcz_test.cc b/mojo/core/core_ipcz_test.cc
index 23ae69e0..a5fc451 100644
--- a/mojo/core/core_ipcz_test.cc
+++ b/mojo/core/core_ipcz_test.cc
@@ -10,24 +10,38 @@
 #include "base/containers/span.h"
 #include "base/strings/string_piece.h"
 #include "base/synchronization/waitable_event.h"
+#include "build/build_config.h"
 #include "mojo/core/ipcz_api.h"
 #include "mojo/core/ipcz_driver/transport.h"
+#include "mojo/core/test/mojo_test_base.h"
+#include "mojo/public/c/system/invitation.h"
 #include "mojo/public/c/system/thunks.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
+#include "mojo/public/cpp/system/platform_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace mojo::core {
 namespace {
 
+struct InvitationDetails {
+  MojoPlatformProcessHandle process;
+  MojoPlatformHandle handle;
+  MojoInvitationTransportEndpoint endpoint;
+};
+
 // Basic smoke tests for the Mojo Core API as implemented over ipcz.
-class CoreIpczTest : public testing::Test {
+class CoreIpczTest : public test::MojoTestBase {
  public:
   const MojoSystemThunks2& mojo() const { return *mojo_; }
   const IpczAPI& ipcz() const { return GetIpczAPI(); }
   IpczHandle node() const { return GetIpczNode(); }
 
-  CoreIpczTest() { CHECK(InitializeIpczNodeForProcess({.is_broker = true})); }
+  CoreIpczTest() : CoreIpczTest(/*is_broker=*/true) {}
+
+  enum { kForClient };
+  explicit CoreIpczTest(decltype(kForClient))
+      : CoreIpczTest(/*is_broker=*/false) {}
 
   ~CoreIpczTest() override { DestroyIpczNodeForProcess(); }
 
@@ -71,7 +85,96 @@
     return details;
   }
 
+  static void CreateAndShareInvitationTransport(MojoHandle pipe,
+                                                const base::Process& process,
+                                                InvitationDetails& details) {
+    PlatformChannel channel;
+    MojoHandle handle_for_client =
+        WrapPlatformHandle(channel.TakeRemoteEndpoint().TakePlatformHandle())
+            .release()
+            .value();
+    WriteMessageWithHandles(pipe, "", &handle_for_client, 1);
+
+    details.process.struct_size = sizeof(details.process);
+#if BUILDFLAG(IS_WIN)
+    details.process.value =
+        static_cast<uint64_t>(reinterpret_cast<uintptr_t>(process.Handle()));
+#else
+    details.process.value = static_cast<uint64_t>(process.Handle());
+#endif
+
+    details.handle.struct_size = sizeof(details.handle);
+    PlatformHandle::ToMojoPlatformHandle(
+        channel.TakeLocalEndpoint().TakePlatformHandle(), &details.handle);
+    details.endpoint = {
+        .struct_size = sizeof(details.endpoint),
+        .type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL,
+        .num_platform_handles = 1,
+        .platform_handles = &details.handle,
+    };
+  }
+
+  static void ReceiveInvitationTransport(MojoHandle pipe,
+                                         InvitationDetails& details) {
+    MojoHandle handle;
+    ReadMessageWithHandles(pipe, &handle, 1);
+
+    details.handle.struct_size = sizeof(details.handle);
+    PlatformHandle::ToMojoPlatformHandle(
+        UnwrapPlatformHandle(ScopedHandle(Handle(handle))), &details.handle);
+    details.endpoint = {
+        .struct_size = sizeof(details.endpoint),
+        .type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL,
+        .num_platform_handles = 1,
+        .platform_handles = &details.handle,
+    };
+  }
+
+  void WriteToMessagePipe(MojoHandle pipe, base::StringPiece contents) {
+    MojoMessageHandle message = CreateMessage(contents);
+    EXPECT_EQ(MOJO_RESULT_OK, mojo().WriteMessage(pipe, message, nullptr));
+  }
+
+  std::string ReadFromMessagePipe(MojoHandle pipe) {
+    base::WaitableEvent ready;
+    MojoHandle trap;
+    auto handler = +[](const MojoTrapEvent* event) {
+      if (event->result == MOJO_RESULT_OK) {
+        reinterpret_cast<base::WaitableEvent*>(event->trigger_context)
+            ->Signal();
+      }
+    };
+    EXPECT_EQ(MOJO_RESULT_OK, mojo().CreateTrap(handler, nullptr, &trap));
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mojo().AddTrigger(trap, pipe, MOJO_HANDLE_SIGNAL_READABLE,
+                                MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
+                                reinterpret_cast<uintptr_t>(&ready), nullptr));
+    const MojoResult result = mojo().ArmTrap(trap, nullptr, nullptr, nullptr);
+    if (result == MOJO_RESULT_OK) {
+      ready.Wait();
+    }
+    EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(trap));
+
+    MojoMessageHandle message;
+    EXPECT_EQ(MOJO_RESULT_OK, mojo().ReadMessage(pipe, nullptr, &message));
+    EXPECT_NE(MOJO_MESSAGE_HANDLE_INVALID, message);
+
+    void* buffer;
+    uint32_t buffer_size;
+    EXPECT_EQ(MOJO_RESULT_OK,
+              mojo().GetMessageData(message, nullptr, &buffer, &buffer_size,
+                                    nullptr, nullptr));
+
+    std::string contents(static_cast<char*>(buffer), buffer_size);
+    EXPECT_EQ(MOJO_RESULT_OK, mojo().DestroyMessage(message));
+    return contents;
+  }
+
  private:
+  explicit CoreIpczTest(bool is_broker) {
+    CHECK(InitializeIpczNodeForProcess({.is_broker = is_broker}));
+  }
+
   const MojoSystemThunks2* const mojo_{GetMojoIpczImpl()};
 };
 
@@ -106,6 +209,11 @@
   scoped_refptr<ipcz_driver::Transport> transport_;
 };
 
+class CoreIpczTestClient : public CoreIpczTest {
+ public:
+  CoreIpczTestClient() : CoreIpczTest(kForClient) {}
+};
+
 TEST_F(CoreIpczTest, Close) {
   // With ipcz-based Mojo Core, Mojo handles are ipcz handles. So Mojo Close()
   // forwards to ipcz Close().
@@ -443,5 +551,111 @@
   EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(buffer));
 }
 
+#if !BUILDFLAG(IS_IOS)
+
+constexpr base::StringPiece kAttachmentName = "interesting pipe name";
+
+constexpr base::StringPiece kTestMessages[] = {
+    "hello hello",
+    "i don't know why you say goodbye",
+    "actually nvm i do",
+    "lol bye",
+};
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(InvitationSingleAttachmentClient,
+                                  CoreIpczTestClient,
+                                  h) {
+  InvitationDetails details;
+  ReceiveInvitationTransport(h, details);
+
+  MojoHandle invitation;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mojo().AcceptInvitation(&details.endpoint, nullptr, &invitation));
+
+  MojoHandle new_pipe;
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().ExtractMessagePipeFromInvitation(
+                                invitation, kAttachmentName.data(),
+                                kAttachmentName.size(), nullptr, &new_pipe));
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(invitation));
+
+  WriteToMessagePipe(new_pipe, kTestMessages[3]);
+  EXPECT_EQ(kTestMessages[0], ReadFromMessagePipe(new_pipe));
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(new_pipe));
+}
+
+TEST_F(CoreIpczTest, InvitationSingleAttachment) {
+  RunTestClientWithController(
+      "InvitationSingleAttachmentClient", [&](ClientController& c) {
+        InvitationDetails details;
+        CreateAndShareInvitationTransport(c.pipe(), c.process(), details);
+
+        MojoHandle new_pipe;
+        MojoHandle invitation;
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  mojo().CreateInvitation(nullptr, &invitation));
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  mojo().AttachMessagePipeToInvitation(
+                      invitation, kAttachmentName.data(),
+                      kAttachmentName.size(), nullptr, &new_pipe));
+        EXPECT_EQ(MOJO_RESULT_OK, mojo().SendInvitation(
+                                      invitation, &details.process,
+                                      &details.endpoint, nullptr, 0, nullptr));
+        EXPECT_EQ(kTestMessages[3], ReadFromMessagePipe(new_pipe));
+        WriteToMessagePipe(new_pipe, kTestMessages[0]);
+        EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(new_pipe));
+      });
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(InvitationMultipleAttachmentsClient,
+                                  CoreIpczTestClient,
+                                  h) {
+  InvitationDetails details;
+  ReceiveInvitationTransport(h, details);
+
+  MojoHandle invitation;
+  EXPECT_EQ(MOJO_RESULT_OK,
+            mojo().AcceptInvitation(&details.endpoint, nullptr, &invitation));
+
+  for (uint32_t i = 0; i < std::size(kTestMessages); ++i) {
+    MojoHandle pipe;
+    EXPECT_EQ(MOJO_RESULT_OK, mojo().ExtractMessagePipeFromInvitation(
+                                  invitation, &i, sizeof(i), nullptr, &pipe));
+    WriteToMessagePipe(pipe, kTestMessages[i]);
+    EXPECT_EQ(kTestMessages[i], ReadFromMessagePipe(pipe));
+    EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(pipe));
+  }
+  EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(invitation));
+}
+
+TEST_F(CoreIpczTest, InvitationMultipleAttachments) {
+  RunTestClientWithController(
+      "InvitationMultipleAttachmentsClient", [&](ClientController& c) {
+        InvitationDetails details;
+        CreateAndShareInvitationTransport(c.pipe(), c.process(), details);
+
+        MojoHandle invitation;
+        EXPECT_EQ(MOJO_RESULT_OK,
+                  mojo().CreateInvitation(nullptr, &invitation));
+
+        MojoHandle pipes[std::size(kTestMessages)];
+        for (uint32_t i = 0; i < std::size(pipes); ++i) {
+          EXPECT_EQ(MOJO_RESULT_OK,
+                    mojo().AttachMessagePipeToInvitation(
+                        invitation, &i, sizeof(i), nullptr, &pipes[i]));
+        }
+        EXPECT_EQ(MOJO_RESULT_OK, mojo().SendInvitation(
+                                      invitation, &details.process,
+                                      &details.endpoint, nullptr, 0, nullptr));
+
+        for (size_t i = 0; i < std::size(pipes); ++i) {
+          EXPECT_EQ(kTestMessages[i], ReadFromMessagePipe(pipes[i]));
+          WriteToMessagePipe(pipes[i], kTestMessages[i]);
+          EXPECT_EQ(MOJO_RESULT_OK, mojo().Close(pipes[i]));
+        }
+      });
+}
+
+#endif  // !BUILDFLAG(IS_IOS)
+
 }  // namespace
 }  // namespace mojo::core
diff --git a/mojo/core/ipcz_driver/invitation.cc b/mojo/core/ipcz_driver/invitation.cc
new file mode 100644
index 0000000..71d614de
--- /dev/null
+++ b/mojo/core/ipcz_driver/invitation.cc
@@ -0,0 +1,253 @@
+// 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 "mojo/core/ipcz_driver/invitation.h"
+
+#include <algorithm>
+
+#include "mojo/core/ipcz_api.h"
+#include "mojo/core/ipcz_driver/transport.h"
+#include "mojo/core/platform_handle_utils.h"
+#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
+#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
+
+namespace mojo::core::ipcz_driver {
+
+namespace {
+
+// The Mojo attach/extract APIs originally took arbitrary string values to
+// identify pipe attachments, and there are still application using that
+// interface. ipcz on the other hand only allows the application to specify a
+// number of initial portals to open during ConnectNode().
+//
+// Fortunately all Mojo consumers across Chrome and Chrome OS fit into one of
+// two categories today:
+//
+//  (a) using an arbitrary string value (usually a GUID) for the attachment
+//      name, but attaching only one pipe.
+//
+//  (b) attaching multiple pipes, but using 32-bit or 64-bit `name` values that
+//      are sequential, zero-based, little-endian integers.
+//
+// We take the first 4 bytes of any name and interpret it as an index into an
+// array of initial portals. If the index is above a reasonably small upper
+// bound (8) then it's treated as zero.
+size_t GetAttachmentIndex(base::span<const uint8_t> name) {
+  if (name.size() != sizeof(uint32_t) && name.size() != sizeof(uint64_t)) {
+    // Use index 0 if the invitation name does not match a simple integer size.
+    // This is assumed to be case (a) above, where this will be the only
+    // attachment.
+    return 0;
+  }
+
+  // Otherwise interpret the first 4 bytes as an integer.
+  uint32_t index = *reinterpret_cast<const uint32_t*>(name.data());
+  if (index < Invitation::kMaxAttachments) {
+    // The resulting index is small enough to fit within the normal index range,
+    // so assume case (b) above:
+    return index;
+  }
+
+  // With the index out of range, assume the the integer sizing is a
+  // coincidence and treat this as case (a), where this should be the only
+  // attachment.
+  return 0;
+}
+
+IpczDriverHandle CreateTransportForMojoEndpoint(
+    Transport::Destination destination,
+    const MojoInvitationTransportEndpoint& endpoint,
+    base::Process remote_process = base::Process()) {
+  CHECK_EQ(endpoint.num_platform_handles, 1u);
+  auto handle =
+      PlatformHandle::FromMojoPlatformHandle(&endpoint.platform_handles[0]);
+  if (!handle.is_valid()) {
+    return IPCZ_INVALID_DRIVER_HANDLE;
+  }
+
+  Channel::Endpoint channel_endpoint;
+  if (endpoint.type == MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) {
+    channel_endpoint = PlatformChannelServerEndpoint(std::move(handle));
+  } else {
+    channel_endpoint = PlatformChannelEndpoint(std::move(handle));
+  }
+  auto transport = base::MakeRefCounted<Transport>(
+      destination, std::move(channel_endpoint), std::move(remote_process));
+  return ObjectBase::ReleaseAsHandle(std::move(transport));
+}
+
+}  // namespace
+
+Invitation::Invitation() = default;
+
+Invitation::~Invitation() {
+  Close();
+}
+
+MojoResult Invitation::Attach(base::span<const uint8_t> name,
+                              MojoHandle* handle) {
+  const size_t index = GetAttachmentIndex(name);
+  if (attachments_[index] != IPCZ_INVALID_HANDLE) {
+    return MOJO_RESULT_ALREADY_EXISTS;
+  }
+
+  // One portal is returned for immediate use; the other is retained so that we
+  // can merge it with a portal returned by ConnectNode() in Send() below.
+  IpczResult result = GetIpczAPI().OpenPortals(
+      GetIpczNode(), IPCZ_NO_FLAGS, nullptr, &attachments_[index], handle);
+  CHECK_EQ(result, IPCZ_RESULT_OK);
+
+  max_attachment_index_ = std::max(max_attachment_index_, index);
+  ++num_attachments_;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Invitation::Extract(base::span<const uint8_t> name,
+                               MojoHandle* handle) {
+  // We expect attachments to have been populated by Accept() already.
+  const size_t index = GetAttachmentIndex(name);
+  if (attachments_[index] == IPCZ_INVALID_HANDLE) {
+    return MOJO_RESULT_NOT_FOUND;
+  }
+
+  *handle = attachments_[index];
+  attachments_[index] = IPCZ_INVALID_HANDLE;
+  return MOJO_RESULT_OK;
+}
+
+MojoResult Invitation::Send(
+    const MojoPlatformProcessHandle* process_handle,
+    const MojoInvitationTransportEndpoint* transport_endpoint,
+    MojoProcessErrorHandler error_handler,
+    uintptr_t error_handler_context,
+    const MojoSendInvitationOptions* options) {
+  if (!transport_endpoint ||
+      transport_endpoint->struct_size < sizeof(*transport_endpoint) ||
+      transport_endpoint->num_platform_handles == 0 ||
+      !transport_endpoint->platform_handles) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+  if (options && options->struct_size < sizeof(*options)) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  base::Process remote_process;
+  if (process_handle) {
+    if (UnwrapAndClonePlatformProcessHandle(process_handle, remote_process) !=
+        MOJO_RESULT_OK) {
+      return MOJO_RESULT_INVALID_ARGUMENT;
+    }
+  }
+
+  // TODO: Support process error handler hooks and NotifyBadMessage.
+  // TODO: Support isolated connections.
+  const bool is_isolated =
+      options && (options->flags & MOJO_SEND_INVITATION_FLAG_ISOLATED) != 0;
+  CHECK(!is_isolated);
+
+  const IpczNodeOptions& config = GetIpczNodeOptions();
+  IpczConnectNodeFlags flags = 0;
+  if (!config.is_broker) {
+    // TODO: Support non-broker to non-broker connection. Requires new flags for
+    // MojoSendInvitation and MojoAcceptInvitation, because ipcz requires
+    // explicit opt-in from both sides of the connection in order for broker
+    // inheritance to be allowed.
+    flags |= IPCZ_CONNECT_NODE_TO_BROKER;
+    if (!config.use_local_shared_memory_allocation) {
+      flags |= IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE;
+    }
+  }
+
+  IpczDriverHandle transport = CreateTransportForMojoEndpoint(
+      Transport::kToNonBroker, *transport_endpoint, std::move(remote_process));
+  if (transport == IPCZ_INVALID_DRIVER_HANDLE) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  if (num_attachments_ == 0 || max_attachment_index_ != num_attachments_ - 1) {
+    return MOJO_RESULT_FAILED_PRECONDITION;
+  }
+
+  IpczHandle portals[kMaxAttachments];
+  IpczResult result = GetIpczAPI().ConnectNode(
+      GetIpczNode(), transport, num_attachments_, flags, nullptr, portals);
+  if (result != IPCZ_RESULT_OK) {
+    return result;
+  }
+
+  for (size_t i = 0; i < num_attachments_; ++i) {
+    result = GetIpczAPI().MergePortals(attachments_[i], portals[i],
+                                       IPCZ_NO_FLAGS, nullptr);
+    CHECK_EQ(result, IPCZ_RESULT_OK);
+    attachments_[i] = IPCZ_INVALID_HANDLE;
+  }
+  return MOJO_RESULT_OK;
+}
+
+// static
+MojoHandle Invitation::Accept(
+    const MojoInvitationTransportEndpoint* transport_endpoint,
+    const MojoAcceptInvitationOptions* options) {
+  if (!transport_endpoint ||
+      transport_endpoint->struct_size < sizeof(*transport_endpoint) ||
+      transport_endpoint->num_platform_handles == 0 ||
+      !transport_endpoint->platform_handles) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
+  auto invitation = base::MakeRefCounted<Invitation>();
+
+  const IpczNodeOptions& config = GetIpczNodeOptions();
+  CHECK(!config.is_broker);
+
+  IpczConnectNodeFlags flags = IPCZ_CONNECT_NODE_TO_BROKER;
+  if (!config.use_local_shared_memory_allocation) {
+    flags |= IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE;
+  }
+
+  // When accepting an invitation, we ConnectNode() with the maximum possible
+  // number of initial portals: unlike ipcz, Mojo APIs have no way for this end
+  // of a connection to express the expected number of attachments prior to
+  // calling MojoAcceptInvitation().
+  //
+  // As the application extracts attachments, the corresponding initial portals
+  // will be extracted from this set. Any unclaimed initial portals (which will
+  // not have a peer on the sending node anyway) will be cleaned up when the
+  // Invitation itself is destroyed.
+  IpczHandle portals[kMaxAttachments];
+  IpczDriverHandle transport =
+      CreateTransportForMojoEndpoint(Transport::kToBroker, *transport_endpoint);
+  if (transport == IPCZ_INVALID_DRIVER_HANDLE) {
+    return IPCZ_INVALID_DRIVER_HANDLE;
+  }
+
+  IpczResult result = GetIpczAPI().ConnectNode(
+      GetIpczNode(), transport, kMaxAttachments, flags, nullptr, portals);
+  CHECK_EQ(result, IPCZ_RESULT_OK);
+
+  for (size_t i = 0; i < kMaxAttachments; ++i) {
+    IpczHandle bridge;
+    GetIpczAPI().OpenPortals(GetIpczNode(), IPCZ_NO_FLAGS, nullptr,
+                             &invitation->attachments_[i], &bridge);
+    result =
+        GetIpczAPI().MergePortals(portals[i], bridge, IPCZ_NO_FLAGS, nullptr);
+  }
+  invitation->num_attachments_ = kMaxAttachments;
+  invitation->max_attachment_index_ = kMaxAttachments - 1;
+  return Box(std::move(invitation));
+}
+
+void Invitation::Close() {
+  // Particularly on accepted invitations, some attachments were created
+  // speculatively. If they weren't extracted by the application, close them.
+  for (IpczHandle& handle : attachments_) {
+    if (handle != IPCZ_INVALID_HANDLE) {
+      GetIpczAPI().Close(std::exchange(handle, IPCZ_INVALID_HANDLE),
+                         IPCZ_NO_FLAGS, nullptr);
+    }
+  }
+}
+
+}  // namespace mojo::core::ipcz_driver
diff --git a/mojo/core/ipcz_driver/invitation.h b/mojo/core/ipcz_driver/invitation.h
new file mode 100644
index 0000000..461a2a8
--- /dev/null
+++ b/mojo/core/ipcz_driver/invitation.h
@@ -0,0 +1,76 @@
+// 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 MOJO_CORE_IPCZ_DRIVER_INVITATION_H_
+#define MOJO_CORE_IPCZ_DRIVER_INVITATION_H_
+
+#include <cstdint>
+
+#include "base/containers/span.h"
+#include "base/containers/stack_container.h"
+#include "mojo/core/ipcz_driver/object.h"
+#include "mojo/public/c/system/invitation.h"
+#include "mojo/public/c/system/types.h"
+#include "third_party/ipcz/include/ipcz/ipcz.h"
+
+namespace mojo::core::ipcz_driver {
+
+// A Mojo invitation. Note that ipcz has no notion of invitation objects, so
+// this object exists to implement a reasonable approximation of Mojo invitation
+// behavior. See comments within the implementation for gritty details.
+class Invitation : public Object<Invitation> {
+ public:
+  // This limit translates to the maximum number of initial portals Mojo will
+  // request on any ConnectNode() call issued by this invitation. The limit is
+  // not strictly specified by ipcz, but it does guarantee that ConnectNode()
+  // must support at least 8 initial portals, so we cap our limit there. In
+  // practice no known Mojo consumer today uses more than 4.
+  static constexpr size_t kMaxAttachments = 8;
+
+  explicit Invitation();
+
+  static Type object_type() { return kInvitation; }
+
+  // Attaches a new pipe to this invitation using the given `name`. Returns
+  // the attached pipe's peer in `handle` if successful.
+  MojoResult Attach(base::span<const uint8_t> name, MojoHandle* handle);
+
+  // Extracts a pipe from the invitation, identified by the given `name`.
+  MojoResult Extract(base::span<const uint8_t> name, MojoHandle* handle);
+
+  // Uses ipcz ConnectNode() to effectively simulate sending this invitation
+  // over the given transport.
+  MojoResult Send(const MojoPlatformProcessHandle* process_handle,
+                  const MojoInvitationTransportEndpoint* transport_endpoint,
+                  MojoProcessErrorHandler error_handler,
+                  uintptr_t error_handler_context,
+                  const MojoSendInvitationOptions* options);
+
+  // Uses ipcz ConnectNode() to effectively simulate accepting a new invitation
+  // over the givern transport. Returns a new boxed Invitation handle.
+  static MojoHandle Accept(
+      const MojoInvitationTransportEndpoint* transport_endpoint,
+      const MojoAcceptInvitationOptions* options);
+
+  // ObjectBase:
+  void Close() override;
+
+ private:
+  ~Invitation() override;
+
+  // An array of pipe attachments. An invitation to be sent must have exactly
+  // `num_attachments_` contiguous attachments starting from the first element
+  // of this array, but attachments may be added out-of-order up to that point.
+  std::array<IpczHandle, kMaxAttachments> attachments_ = {};
+
+  // The total number of pipes attached so far.
+  size_t num_attachments_ = 0;
+
+  // The highest index of any attached pipe so far.
+  size_t max_attachment_index_ = 0;
+};
+
+}  // namespace mojo::core::ipcz_driver
+
+#endif  // MOJO_CORE_IPCZ_DRIVER_INVITATION_H_
diff --git a/mojo/core/ipcz_driver/transport.cc b/mojo/core/ipcz_driver/transport.cc
index 069778fc..b601c3f6 100644
--- a/mojo/core/ipcz_driver/transport.cc
+++ b/mojo/core/ipcz_driver/transport.cc
@@ -9,6 +9,7 @@
 
 #include "base/check_op.h"
 #include "base/containers/stack_container.h"
+#include "base/functional/overloaded.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/process/process.h"
 #include "build/build_config.h"
@@ -20,6 +21,7 @@
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/ipcz/include/ipcz/ipcz.h"
 
 namespace mojo::core::ipcz_driver {
@@ -101,7 +103,7 @@
 }  // namespace
 
 Transport::Transport(Destination destination,
-                     PlatformChannelEndpoint endpoint,
+                     Channel::Endpoint endpoint,
                      base::Process remote_process)
     : destination_(destination),
       remote_process_(std::move(remote_process)),
@@ -127,7 +129,7 @@
   std::vector<PendingTransmission> pending_transmissions;
   {
     base::AutoLock lock(lock_);
-    if (channel_ || !inactive_endpoint_.is_valid()) {
+    if (channel_ || !IsEndpointValid()) {
       return false;
     }
 
@@ -193,7 +195,7 @@
   scoped_refptr<Channel> channel;
   {
     base::AutoLock lock(lock_);
-    if (inactive_endpoint_.is_valid()) {
+    if (IsEndpointValid()) {
       PendingTransmission transmission;
       transmission.bytes = std::vector<uint8_t>(data.begin(), data.end());
       transmission.handles = std::move(platform_handles);
@@ -411,8 +413,10 @@
   DCHECK_EQ(handles.size(), 1u);
 #endif
 
-  DCHECK(inactive_endpoint_.is_valid());
-  handles[0] = inactive_endpoint_.TakePlatformHandle();
+  DCHECK(IsEndpointValid());
+  DCHECK(absl::holds_alternative<PlatformChannelEndpoint>(inactive_endpoint_));
+  handles[0] = absl::get<PlatformChannelEndpoint>(inactive_endpoint_)
+                   .TakePlatformHandle();
   return true;
 }
 
@@ -479,6 +483,18 @@
   self = std::move(self_reference_for_channel_);
 }
 
+bool Transport::IsEndpointValid() const {
+  return absl::visit(base::Overloaded{
+                         [](const PlatformChannelEndpoint& endpoint) {
+                           return endpoint.is_valid();
+                         },
+                         [](const PlatformChannelServerEndpoint& endpoint) {
+                           return endpoint.is_valid();
+                         },
+                     },
+                     inactive_endpoint_);
+}
+
 bool Transport::CanTransmitHandles() const {
 #if BUILDFLAG(IS_WIN)
   // On Windows, only transports with a broker on one end may transmit handles.
diff --git a/mojo/core/ipcz_driver/transport.h b/mojo/core/ipcz_driver/transport.h
index af37560..70ee56a 100644
--- a/mojo/core/ipcz_driver/transport.h
+++ b/mojo/core/ipcz_driver/transport.h
@@ -36,7 +36,7 @@
   };
 
   Transport(Destination destination,
-            PlatformChannelEndpoint endpoint,
+            Channel::Endpoint endpoint,
             base::Process remote_process = base::Process());
 
   static std::pair<scoped_refptr<Transport>, scoped_refptr<Transport>>
@@ -56,9 +56,9 @@
 
   // Takes ownership of the Transport's underlying channel endpoint, effectively
   // invalidating the transport. May only be called on a Transport which has not
-  // yet been activated.
+  // yet been activated, and only when the channel endpoint is not a server.
   PlatformChannelEndpoint TakeEndpoint() {
-    return std::move(inactive_endpoint_);
+    return std::move(absl::get<PlatformChannelEndpoint>(inactive_endpoint_));
   }
 
   // Activates this transport by creating and starting the underlying Channel
@@ -129,6 +129,7 @@
 
   ~Transport() override;
 
+  bool IsEndpointValid() const;
   bool CanTransmitHandles() const;
 
   // Indicates whether this transport should serialize its remote process handle
@@ -145,7 +146,7 @@
   // start its underlying Channel instance once activated. Not guarded by a lock
   // since it must not accessed beyond activation, where thread safety becomes a
   // factor.
-  PlatformChannelEndpoint inactive_endpoint_;
+  Channel::Endpoint inactive_endpoint_;
 
   base::Lock lock_;
   scoped_refptr<Channel> channel_ GUARDED_BY(lock_);
diff --git a/net/dns/README.md b/net/dns/README.md
index 87caba0..0908acd0 100644
--- a/net/dns/README.md
+++ b/net/dns/README.md
@@ -261,6 +261,11 @@
 `net::HostResolverProc` may be replaced by a chain of test implementations to
 override behavior.
 
+Data collected specifically for this source:
+
+* "Net.DNS.ProcTask.SuccessTime"
+* "Net.DNS.ProcTask.FailureTime"
+
 #### DNS
 
 `net::HostResolverSource::DNS`
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 471f922..0b09098c 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -2593,6 +2593,12 @@
                           const AddressList& addr_list) {
     DCHECK(proc_task_);
 
+    base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
+    if (net_error == OK)
+      UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.ProcTask.SuccessTime", duration);
+    else
+      UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.ProcTask.FailureTime", duration);
+
     if (dns_task_error_ != OK && net_error == OK) {
       // This ProcTask was a fallback resolution after a failed insecure
       // DnsTask.
diff --git a/net/first_party_sets/first_party_set_entry.h b/net/first_party_sets/first_party_set_entry.h
index d027701..8570a91c 100644
--- a/net/first_party_sets/first_party_set_entry.h
+++ b/net/first_party_sets/first_party_set_entry.h
@@ -17,6 +17,9 @@
   // The First-Party Set declaration listed this site as an associated site in
   // the set.
   kAssociated,
+  // The First-Party Set declaration listed this site as a service site in the
+  // set.
+  kService,
 };
 
 // This class bundles together metadata associated with an entry in a
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 691e767..fbd56ca 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -266,6 +266,8 @@
     "desktop_capturer_checker.h",
     "desktop_capturer_proxy.cc",
     "desktop_capturer_proxy.h",
+    "desktop_capturer_wrapper.cc",
+    "desktop_capturer_wrapper.h",
     "desktop_display_info.cc",
     "desktop_display_info.h",
     "desktop_display_info_loader.h",
diff --git a/remoting/host/basic_desktop_environment.cc b/remoting/host/basic_desktop_environment.cc
index dcf1668..715ac22 100644
--- a/remoting/host/basic_desktop_environment.cc
+++ b/remoting/host/basic_desktop_environment.cc
@@ -16,6 +16,7 @@
 #include "remoting/host/base/screen_controls.h"
 #include "remoting/host/client_session_control.h"
 #include "remoting/host/desktop_capturer_proxy.h"
+#include "remoting/host/desktop_capturer_wrapper.h"
 #include "remoting/host/desktop_display_info_monitor.h"
 #include "remoting/host/file_transfer/local_file_operations.h"
 #include "remoting/host/input_injector.h"
@@ -192,16 +193,28 @@
 BasicDesktopEnvironment::CreateVideoCapturer() {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
 
+  // TODO(joedow): Determine whether we can migrate additional platforms to
+  // using the DesktopCaptureWrapper instead of the DesktopCaptureProxy. Then
+  // clean up DesktopCapturerProxy::Core::CreateCapturer().
+#if BUILDFLAG(IS_LINUX) && !defined(REMOTING_USE_WAYLAND)
+  auto desktop_capturer = std::make_unique<DesktopCapturerWrapper>();
+#else  // !BUILDFLAG(IS_LINUX) || defined(REMOTING_USE_WAYLAND)
+  scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  capture_task_runner = ui_task_runner_;
+#else   // !BUILDFLAG(IS_CHROMEOS_ASH)
   // Each capturer instance should get its own thread so the capturers don't
   // compete with each other in multistream mode.
-  auto dedicated_task_runner = base::ThreadPool::CreateSingleThreadTaskRunner(
+  capture_task_runner = base::ThreadPool::CreateSingleThreadTaskRunner(
       {base::TaskPriority::HIGHEST},
       base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+  auto desktop_capturer =
+      std::make_unique<DesktopCapturerProxy>(std::move(capture_task_runner));
+#endif  // !BUILDFLAG(IS_LINUX) || defined(REMOTING_USE_WAYLAND)
 
-  auto result =
-      std::make_unique<DesktopCapturerProxy>(std::move(dedicated_task_runner));
-  result->CreateCapturer(desktop_capture_options());
-  return std::move(result);
+  desktop_capturer->CreateCapturer(desktop_capture_options());
+  return std::move(desktop_capturer);
 }
 
 BasicDesktopEnvironment::BasicDesktopEnvironment(
diff --git a/remoting/host/desktop_capturer_wrapper.cc b/remoting/host/desktop_capturer_wrapper.cc
new file mode 100644
index 0000000..317bb4a8
--- /dev/null
+++ b/remoting/host/desktop_capturer_wrapper.cc
@@ -0,0 +1,96 @@
+// 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 "remoting/host/desktop_capturer_wrapper.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
+
+namespace remoting {
+
+DesktopCapturerWrapper::DesktopCapturerWrapper() {
+  DETACH_FROM_THREAD(thread_checker_);
+}
+
+DesktopCapturerWrapper::~DesktopCapturerWrapper() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void DesktopCapturerWrapper::CreateCapturer(
+    const webrtc::DesktopCaptureOptions& options) {
+  DCHECK(!capturer_);
+
+  capturer_ = webrtc::DesktopCapturer::CreateScreenCapturer(options);
+
+  if (!capturer_) {
+    LOG(ERROR) << "Failed to initialize screen capturer.";
+  }
+}
+
+void DesktopCapturerWrapper::Start(Callback* callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(!callback_);
+
+  callback_ = callback;
+
+  if (capturer_) {
+    capturer_->Start(this);
+  }
+}
+
+void DesktopCapturerWrapper::SetSharedMemoryFactory(
+    std::unique_ptr<webrtc::SharedMemoryFactory> shared_memory_factory) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (capturer_) {
+    capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
+  }
+}
+
+void DesktopCapturerWrapper::CaptureFrame() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (capturer_) {
+    capturer_->CaptureFrame();
+  } else {
+    OnCaptureResult(webrtc::DesktopCapturer::Result::ERROR_PERMANENT, nullptr);
+  }
+}
+
+bool DesktopCapturerWrapper::GetSourceList(SourceList* sources) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool DesktopCapturerWrapper::SelectSource(SourceId id) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (capturer_) {
+    return capturer_->SelectSource(id);
+  }
+  return false;
+}
+
+void DesktopCapturerWrapper::OnCaptureResult(
+    webrtc::DesktopCapturer::Result result,
+    std::unique_ptr<webrtc::DesktopFrame> frame) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  callback_->OnCaptureResult(result, std::move(frame));
+}
+
+#if defined(WEBRTC_USE_GIO)
+void DesktopCapturerWrapper::GetMetadataAsync(
+    base::OnceCallback<void(webrtc::DesktopCaptureMetadata)> callback) {
+  NOTREACHED() << "Use DesktopCapturerProxy instead!";
+}
+#endif
+
+}  // namespace remoting
diff --git a/remoting/host/desktop_capturer_wrapper.h b/remoting/host/desktop_capturer_wrapper.h
new file mode 100644
index 0000000..e445d7b
--- /dev/null
+++ b/remoting/host/desktop_capturer_wrapper.h
@@ -0,0 +1,58 @@
+// 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 REMOTING_HOST_DESKTOP_CAPTURER_WRAPPER_H_
+#define REMOTING_HOST_DESKTOP_CAPTURER_WRAPPER_H_
+
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "remoting/protocol/desktop_capturer.h"
+
+namespace webrtc {
+class DesktopCaptureOptions;
+}  // namespace webrtc
+
+namespace remoting {
+
+// Simple wrapper class which holds a webrtc::DesktopCapturer and exposes a
+// remoting::DesktopCapturer interface for interacting with it.
+class DesktopCapturerWrapper : public DesktopCapturer,
+                               public webrtc::DesktopCapturer::Callback {
+ public:
+  DesktopCapturerWrapper();
+  DesktopCapturerWrapper(const DesktopCapturerWrapper&) = delete;
+  DesktopCapturerWrapper& operator=(const DesktopCapturerWrapper&) = delete;
+  ~DesktopCapturerWrapper() override;
+
+  void CreateCapturer(const webrtc::DesktopCaptureOptions& options);
+
+  // webrtc::DesktopCapturer interface.
+  void Start(Callback* callback) override;
+  void SetSharedMemoryFactory(std::unique_ptr<webrtc::SharedMemoryFactory>
+                                  shared_memory_factory) override;
+  void CaptureFrame() override;
+  bool GetSourceList(SourceList* sources) override;
+  bool SelectSource(SourceId id) override;
+#if defined(WEBRTC_USE_GIO)
+  void GetMetadataAsync(base::OnceCallback<void(webrtc::DesktopCaptureMetadata)>
+                            callback) override;
+#endif
+
+ private:
+  // webrtc::DesktopCapturer::Callback implementation.
+  void OnCaptureResult(webrtc::DesktopCapturer::Result result,
+                       std::unique_ptr<webrtc::DesktopFrame> frame) override;
+
+  raw_ptr<webrtc::DesktopCapturer::Callback> callback_ = nullptr;
+  std::unique_ptr<webrtc::DesktopCapturer> capturer_;
+
+  THREAD_CHECKER(thread_checker_);
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_DESKTOP_CAPTURER_WRAPPER_H_
diff --git a/remoting/protocol/webrtc_video_encoder_factory.cc b/remoting/protocol/webrtc_video_encoder_factory.cc
index e7a032fe..63102437 100644
--- a/remoting/protocol/webrtc_video_encoder_factory.cc
+++ b/remoting/protocol/webrtc_video_encoder_factory.cc
@@ -22,24 +22,24 @@
 
 WebrtcVideoEncoderFactory::WebrtcVideoEncoderFactory()
     : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
-  formats_.emplace_back(webrtc::SdpVideoFormat("VP8"));
-  formats_.emplace_back(webrtc::SdpVideoFormat("VP9"));
+  formats_.emplace_back("VP8");
+  formats_.emplace_back("VP9");
   formats_.emplace_back(webrtc::SdpVideoFormat(
       "VP9", {{webrtc::kVP9FmtpProfileId,
                webrtc::VP9ProfileToString(webrtc::VP9Profile::kProfile1)}}));
+  formats_.emplace_back("AV1");
+  formats_.emplace_back(webrtc::SdpVideoFormat(
+      "AV1",
+      {{webrtc::kAV1FmtpProfile,
+        webrtc::AV1ProfileToString(webrtc::AV1Profile::kProfile1).data()}}));
 #if defined(USE_H264_ENCODER)
   // This call will query the underlying media classes to determine whether
   // hardware encoding is supported or not. We use a default resolution and
   // framerate so the call doesn't fail due to invalid params.
   if (WebrtcVideoEncoderGpu::IsSupportedByH264({{1920, 1080}, 30})) {
-    formats_.emplace_back(webrtc::SdpVideoFormat("H264"));
+    formats_.emplace_back("H264");
   }
 #endif
-  formats_.emplace_back(webrtc::SdpVideoFormat("AV1"));
-  formats_.emplace_back(webrtc::SdpVideoFormat(
-      "AV1",
-      {{webrtc::kAV1FmtpProfile,
-        webrtc::AV1ProfileToString(webrtc::AV1Profile::kProfile1).data()}}));
 }
 
 WebrtcVideoEncoderFactory::~WebrtcVideoEncoderFactory() = default;
diff --git a/services/network/public/cpp/first_party_sets_mojom_traits.cc b/services/network/public/cpp/first_party_sets_mojom_traits.cc
index 85a1aade..987cfc3 100644
--- a/services/network/public/cpp/first_party_sets_mojom_traits.cc
+++ b/services/network/public/cpp/first_party_sets_mojom_traits.cc
@@ -34,6 +34,9 @@
     case network::mojom::SiteType::kAssociated:
       *out = net::SiteType::kAssociated;
       return true;
+    case network::mojom::SiteType::kService:
+      *out = net::SiteType::kService;
+      return true;
   }
   return false;
 }
@@ -46,6 +49,8 @@
       return network::mojom::SiteType::kPrimary;
     case net::SiteType::kAssociated:
       return network::mojom::SiteType::kAssociated;
+    case net::SiteType::kService:
+      return network::mojom::SiteType::kService;
   }
   NOTREACHED();
   return network::mojom::SiteType::kPrimary;
diff --git a/services/network/public/cpp/first_party_sets_mojom_traits_unittest.cc b/services/network/public/cpp/first_party_sets_mojom_traits_unittest.cc
index 867cb2f..02754b1 100644
--- a/services/network/public/cpp/first_party_sets_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/first_party_sets_mojom_traits_unittest.cc
@@ -27,8 +27,11 @@
 }
 
 TEST(FirstPartySetsTraitsTest, Roundtrips_SiteType) {
-  for (net::SiteType site_type :
-       {net::SiteType::kPrimary, net::SiteType::kAssociated}) {
+  for (net::SiteType site_type : {
+           net::SiteType::kPrimary,
+           net::SiteType::kAssociated,
+           net::SiteType::kService,
+       }) {
     net::SiteType roundtrip;
     ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::SiteType>(
         site_type, roundtrip));
diff --git a/services/network/public/mojom/first_party_sets.mojom b/services/network/public/mojom/first_party_sets.mojom
index 085385c..7930ba8 100644
--- a/services/network/public/mojom/first_party_sets.mojom
+++ b/services/network/public/mojom/first_party_sets.mojom
@@ -16,6 +16,7 @@
 enum SiteType {
   kPrimary,
   kAssociated,
+  kService,
 };
 
 // This struct should match //net/first_party_sets/first_party_set_entry.h.
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index eb5fbd4..9642ab9 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1879,7 +1879,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R107-15064.0.0",
+        "cros_img": "atlas-release/R107-15081.0.0",
         "name": "lacros_all_tast_tests ATLAS_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -1943,7 +1943,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R107-15064.0.0",
+        "cros_img": "eve-release/R107-15081.0.0",
         "name": "lacros_all_tast_tests EVE_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -2050,7 +2050,7 @@
       {
         "args": [],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R107-15064.0.0",
+        "cros_img": "jacuzzi-release/R107-15081.0.0",
         "name": "lacros_all_tast_tests JACUZZI_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -2155,7 +2155,7 @@
       {
         "args": [],
         "cros_board": "herobrine",
-        "cros_img": "herobrine-release/R107-15064.0.0",
+        "cros_img": "herobrine-release/R107-15081.0.0",
         "name": "lacros_fyi_tast_tests HEROBRINE_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 2798c2a..4b048ea 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -13148,7 +13148,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -13573,7 +13573,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 055fbde2..3dfcaf0 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -45950,7 +45950,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46375,7 +46375,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46804,7 +46804,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47229,7 +47229,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47726,7 +47726,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48151,7 +48151,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48648,7 +48648,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49073,7 +49073,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M105",
-              "revision": "version:105.0.5195.99"
+              "revision": "version:105.0.5195.111"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index 95e578d..58eec17 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1037,7 +1037,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R107-15064.0.0",
+        "cros_img": "octopus-release/R107-15081.0.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1085,7 +1085,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R107-15064.0.0",
+        "cros_img": "octopus-release/R107-15081.0.0",
         "name": "ozone_unittests OCTOPUS_RELEASE_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1136,7 +1136,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R107-15064.0.0",
+        "cros_img": "hana-release/R107-15081.0.0",
         "name": "lacros_all_tast_tests HANA_RELEASE_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1184,7 +1184,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R107-15064.0.0",
+        "cros_img": "strongbad-release/R107-15081.0.0",
         "name": "lacros_all_tast_tests STRONGBAD_RELEASE_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1232,7 +1232,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R107-15064.0.0",
+        "cros_img": "hana-release/R107-15081.0.0",
         "name": "ozone_unittests HANA_RELEASE_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1276,7 +1276,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R107-15064.0.0",
+        "cros_img": "strongbad-release/R107-15081.0.0",
         "name": "ozone_unittests STRONGBAD_RELEASE_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1320,7 +1320,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R107-15064.0.0",
+        "cros_img": "hana-release/R107-15081.0.0",
         "name": "viz_unittests HANA_RELEASE_LKGM",
         "swarming": {},
         "test": "viz_unittests",
@@ -1364,7 +1364,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R107-15064.0.0",
+        "cros_img": "strongbad-release/R107-15081.0.0",
         "name": "viz_unittests STRONGBAD_RELEASE_LKGM",
         "swarming": {},
         "test": "viz_unittests",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index e29e6e2..4d3aa8d 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -481,7 +481,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M105',
-          'revision': 'version:105.0.5195.99',
+          'revision': 'version:105.0.5195.111',
         }
       ],
     },
@@ -601,7 +601,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M105',
-          'revision': 'version:105.0.5195.99',
+          'revision': 'version:105.0.5195.111',
         }
       ],
     },
@@ -721,7 +721,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M105',
-          'revision': 'version:105.0.5195.99',
+          'revision': 'version:105.0.5195.111',
         }
       ],
     },
@@ -801,8 +801,8 @@
   'CROS_ATLAS_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'atlas',
-      'cros_chrome_version': '106.0.5243.3',
-      'cros_img': 'atlas-release/R107-15064.0.0',
+      'cros_chrome_version': '107.0.5263.0',
+      'cros_img': 'atlas-release/R107-15081.0.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_RELEASE_LKGM',
@@ -837,8 +837,8 @@
   'CROS_EVE_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '106.0.5243.3',
-      'cros_img': 'eve-release/R107-15064.0.0',
+      'cros_chrome_version': '107.0.5263.0',
+      'cros_img': 'eve-release/R107-15081.0.0',
     },
     'enabled': True,
     'identifier': 'EVE_RELEASE_LKGM',
@@ -883,8 +883,8 @@
   'CROS_HANA_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'hana',
-      'cros_chrome_version': '106.0.5243.3',
-      'cros_img': 'hana-release/R107-15064.0.0',
+      'cros_chrome_version': '107.0.5263.0',
+      'cros_img': 'hana-release/R107-15081.0.0',
     },
     'enabled': True,
     'identifier': 'HANA_RELEASE_LKGM',
@@ -919,8 +919,8 @@
   'CROS_HEROBRINE_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'herobrine',
-      'cros_chrome_version': '106.0.5243.3',
-      'cros_img': 'herobrine-release/R107-15064.0.0',
+      'cros_chrome_version': '107.0.5263.0',
+      'cros_img': 'herobrine-release/R107-15081.0.0',
     },
     'enabled': True,
     'identifier': 'HEROBRINE_RELEASE_LKGM',
@@ -928,8 +928,8 @@
   'CROS_JACUZZI_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'jacuzzi',
-      'cros_chrome_version': '106.0.5243.3',
-      'cros_img': 'jacuzzi-release/R107-15064.0.0',
+      'cros_chrome_version': '107.0.5263.0',
+      'cros_img': 'jacuzzi-release/R107-15081.0.0',
     },
     'enabled': True,
     'identifier': 'JACUZZI_RELEASE_LKGM',
@@ -974,8 +974,8 @@
   'CROS_OCTOPUS_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '106.0.5243.3',
-      'cros_img': 'octopus-release/R107-15064.0.0',
+      'cros_chrome_version': '107.0.5263.0',
+      'cros_img': 'octopus-release/R107-15081.0.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_RELEASE_LKGM',
@@ -1010,8 +1010,8 @@
   'CROS_STRONGBAD_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'strongbad',
-      'cros_chrome_version': '106.0.5243.3',
-      'cros_img': 'strongbad-release/R107-15064.0.0',
+      'cros_chrome_version': '107.0.5263.0',
+      'cros_img': 'strongbad-release/R107-15081.0.0',
     },
     'enabled': True,
     'identifier': 'STRONGBAD_RELEASE_LKGM',
diff --git a/third_party/blink/common/web_package/web_package_request_matcher.cc b/third_party/blink/common/web_package/web_package_request_matcher.cc
index d119713f..2422794f 100644
--- a/third_party/blink/common/web_package/web_package_request_matcher.cc
+++ b/third_party/blink/common/web_package/web_package_request_matcher.cc
@@ -4,11 +4,11 @@
 
 #include "third_party/blink/public/common/web_package/web_package_request_matcher.h"
 
-#include <algorithm>
 #include <limits>
 #include <memory>
 #include <utility>
 
+#include "base/containers/contains.h"
 #include "base/containers/span.h"
 #include "base/numerics/checked_math.h"
 #include "base/strings/string_number_conversions.h"
@@ -160,9 +160,7 @@
 
     // Step 3. If "identity" is not a member of preferred-codings, append
     // "identity". [spec text]
-    if (!std::any_of(
-            preferred_codings.begin(), preferred_codings.end(),
-            [](const WeightedValue& p) { return p.value == kIdentity; })) {
+    if (!base::Contains(preferred_codings, kIdentity, &WeightedValue::value)) {
       preferred_codings.push_back({kIdentity, 0.0});
     }
 
diff --git a/third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.mojom b/third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.mojom
index 4cbe032..a64bc30 100644
--- a/third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.mojom
+++ b/third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.mojom
@@ -150,6 +150,6 @@
   // modifier.
   DispatchAcceleratorEvent(AcceleratorEvent event);
 
-  // Notify the renderer that it should dispatch a window closed event.
-  DispatchWindowClosedEvent(mojo_base.mojom.UnguessableToken window_id);
+  // Notify the renderer that a window has been closed.
+  DispatchWindowClosedEvent(CrosWindowInfo window);
 };
diff --git a/third_party/blink/public/mojom/file/file_utilities.mojom b/third_party/blink/public/mojom/file/file_utilities.mojom
index 4f564a4..97448ae 100644
--- a/third_party/blink/public/mojom/file/file_utilities.mojom
+++ b/third_party/blink/public/mojom/file/file_utilities.mojom
@@ -38,7 +38,7 @@
   //
   // By the same reasoning above (renderers on most OSes can call ftruncate()
   // directly), this call returns unsanitized base::File::Error information.
-  [EnableIf=is_mac]
+  [EnableIf=is_mac, Sync]
   SetLength(mojo_base.mojom.File backing_file, int64 length) =>
   (mojo_base.mojom.File backing_file, bool result);
 };
diff --git a/third_party/blink/public/mojom/file_system_access/file_system_access_access_handle_host.mojom b/third_party/blink/public/mojom/file_system_access/file_system_access_access_handle_host.mojom
index 1caaf94..b6d515e 100644
--- a/third_party/blink/public/mojom/file_system_access/file_system_access_access_handle_host.mojom
+++ b/third_party/blink/public/mojom/file_system_access/file_system_access_access_handle_host.mojom
@@ -25,5 +25,6 @@
   // corresponding file descriptor. This may allow it to observe writes from
   // other renderers to the same (origin-scoped) file, so no cross-origin data
   // would be leaked.
+  [Sync]
   Close() => ();
 };
diff --git a/third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom b/third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom
index 1fc6fff0..6a09623 100644
--- a/third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom
+++ b/third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom
@@ -37,7 +37,8 @@
   // Returns the `length` of the associated file and a file error, which is
   // `base::File::Error::FILE_OK` if the operation completed successfully.
   // The returned length of the file will never be more than the max int64.
-  GetLength() => (mojo_base.mojom.FileError error, uint64 length);
+  [Sync]
+  GetLength() => (mojo_base.mojom.FileError error, int64 length);
 
   // Truncates a file to `length`. If `length` is larger than the file size, the
   // file will be extended with null bytes. Ideally `length` would be a uint64,
@@ -45,5 +46,6 @@
   // FileSystemAccessFileDelegateHostImpl will report a bad message if `length`
   // is negative. Returns a file error, which is `base::File::Error::FILE_OK` if
   // the operation completed successfully.
+  [Sync]
   SetLength(int64 length) => (mojo_base.mojom.FileError error);
 };
diff --git a/third_party/blink/public/web/web_frame_widget.h b/third_party/blink/public/web/web_frame_widget.h
index 9b9d54e..6264d51 100644
--- a/third_party/blink/public/web/web_frame_widget.h
+++ b/third_party/blink/public/web/web_frame_widget.h
@@ -215,6 +215,10 @@
   // `FrameWidgetTestHelper::CreateTestWebFrameWidget()`.
   virtual FrameWidgetTestHelper* GetFrameWidgetTestHelperForTesting() = 0;
 
+  // This should be called for the local root frame before calling the final
+  // UpdateAllLifecyclePhases() just before dumping pixels.
+  virtual void PrepareForFinalLifecyclUpdateForTesting() = 0;
+
  private:
   // This is a private virtual method so we don't expose cc::LayerTreeHost
   // outside of this class. Friend classes may be added in order to access it.
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
index 9fad8a6..b7d4953 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
@@ -35,6 +35,7 @@
 
 #include "base/containers/span.h"
 #include "base/dcheck_is_on.h"
+#include "base/ranges/algorithm.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
 #include "third_party/blink/public/common/messaging/message_port_descriptor.h"
@@ -290,10 +291,9 @@
   bool IsLockedToAgentCluster() const {
     return !wasm_modules_.IsEmpty() ||
            !shared_array_buffers_contents_.IsEmpty() ||
-           std::any_of(attachments_.begin(), attachments_.end(),
-                       [](const auto& entry) {
-                         return entry.value->IsLockedToAgentCluster();
-                       });
+           base::ranges::any_of(attachments_, [](const auto& entry) {
+             return entry.value->IsLockedToAgentCluster();
+           });
   }
 
   // Returns true after serializing script values that remote origins cannot
diff --git a/third_party/blink/renderer/build/scripts/templates/origin_trials.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/origin_trials.cc.tmpl
index a330714..7021ad6 100644
--- a/third_party/blink/renderer/build/scripts/templates/origin_trials.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/origin_trials.cc.tmpl
@@ -5,7 +5,6 @@
 
 #include "third_party/blink/public/common/origin_trials/origin_trials.h"
 
-#include <algorithm>
 #include <array>
 #include <iterator>
 
@@ -23,7 +22,7 @@
 namespace {
 
 static constexpr size_t kMaxFeaturesPerTrial = {{max_features_per_trial}};
-static constexpr struct {
+static constexpr struct TrialToFeature {
   const char* trial_name;
   unsigned feature_count;
   std::array<OriginTrialFeature, kMaxFeaturesPerTrial> features;
@@ -38,9 +37,8 @@
 } // namespace
 
 bool origin_trials::IsTrialValid(base::StringPiece trial_name) {
-  return std::any_of(
-      std::begin(kTrialToFeaturesMap), std::end(kTrialToFeaturesMap),
-      [&](const auto& entry) { return entry.trial_name == trial_name; });
+  return base::Contains(kTrialToFeaturesMap, trial_name,
+                        &TrialToFeature::trial_name);
 }
 
 bool origin_trials::IsTrialEnabledForInsecureContext(base::StringPiece trial_name) {
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc
index 36ccb08..f2ccf36 100644
--- a/third_party/blink/renderer/core/css/css_property_equality.cc
+++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -210,7 +210,7 @@
     case CSSPropertyID::kObjectPosition:
       return a.ObjectPosition() == b.ObjectPosition();
     case CSSPropertyID::kObjectViewBox:
-      return a.ObjectViewBox() == b.ObjectViewBox();
+      return base::ValuesEquivalent(a.ObjectViewBox(), b.ObjectViewBox());
     case CSSPropertyID::kOffsetAnchor:
       return a.OffsetAnchor() == b.OffsetAnchor();
     case CSSPropertyID::kOffsetDistance:
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index b9c8730..21a9d64b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -4734,8 +4734,6 @@
 
   if (frame_->IsLocalRoot())
     EnsureUkmAggregator().DidReachFirstContentfulPaint();
-
-  GetLayoutView()->GetDeferredShapingController().OnFirstContentfulPaint();
 }
 
 void LocalFrameView::RegisterForLifecycleNotifications(
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index d2e2dad..29d1d9b 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -100,6 +100,7 @@
 #include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
 #include "third_party/blink/renderer/core/input/touch_action_util.h"
+#include "third_party/blink/renderer/core/layout/deferred_shaping_controller.h"
 #include "third_party/blink/renderer/core/layout/hit_test_location.h"
 #include "third_party/blink/renderer/core/layout/hit_test_request.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
@@ -4332,6 +4333,21 @@
   return nullptr;
 }
 
+void WebFrameWidgetImpl::PrepareForFinalLifecyclUpdateForTesting() {
+  ForEachLocalFrameControlledByWidget(
+      LocalRootImpl()->GetFrame(),
+      WTF::BindRepeating([](WebLocalFrameImpl* local_frame) {
+        LocalFrame* core_frame = local_frame->GetFrame();
+        if (!core_frame)
+          return;
+        Document* document = core_frame->GetDocument();
+        if (!document)
+          return;
+        if (auto* ds_controller = DeferredShapingController::From(*document))
+          ds_controller->ReshapeAllDeferred(ReshapeReason::kTesting);
+      }));
+}
+
 void WebFrameWidgetImpl::SetMayThrottleIfUndrawnFrames(
     bool may_throttle_if_undrawn_frames) {
   if (!View()->does_composite())
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index a6f55c1..ad3bee90 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -349,6 +349,7 @@
   void ResetZoomLevelForTesting() override;
   void SetDeviceScaleFactorForTesting(float factor) override;
   FrameWidgetTestHelper* GetFrameWidgetTestHelperForTesting() override;
+  void PrepareForFinalLifecyclUpdateForTesting() override;
 
   // Called when a drag-n-drop operation should begin.
   virtual void StartDragging(const WebDragData&,
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index bb0d971..56efeda 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -567,7 +567,7 @@
   // re-created on-demand when SpeechSynthesis() is called.
   if (speech_synthesis_) {
     speech_synthesis_->Cancel();
-    speech_synthesis_ = nullptr;
+    speech_synthesis_.Clear();
   }
 
   if (should_delay_load_event_) {
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 583ed87a..938d402 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -1086,7 +1086,8 @@
       LayoutUnit baseline =
           NGBoxFragment(layout_box->StyleRef().GetWritingDirection(),
                         *To<NGPhysicalBoxFragment>(child_fragment))
-              .BaselineOrSynthesize(layout_box->StyleRef().GetFontBaseline());
+              .FirstBaselineOrSynthesize(
+                  layout_box->StyleRef().GetFontBaseline());
       float adjusted_baseline = AdjustForAbsoluteZoom::AdjustFloat(
           baseline + box->MarginTop(), box->StyleRef());
 
diff --git a/third_party/blink/renderer/core/layout/deferred_shaping_controller.cc b/third_party/blink/renderer/core/layout/deferred_shaping_controller.cc
index 799d45a1e..e251895 100644
--- a/third_party/blink/renderer/core/layout/deferred_shaping_controller.cc
+++ b/third_party/blink/renderer/core/layout/deferred_shaping_controller.cc
@@ -154,6 +154,9 @@
       reason_string = "scrolling APIs";
       feature = WebFeature::kDeferredShaping2ReshapedByScrolling;
       break;
+    case ReshapeReason::kTesting:
+      reason_string = "a test";
+      break;
   }
   if (feature != WebFeature::kMaxValue) {
     UseCounter::Count(*document_, feature);
diff --git a/third_party/blink/renderer/core/layout/deferred_shaping_controller.h b/third_party/blink/renderer/core/layout/deferred_shaping_controller.h
index c05aad9..62346dbc 100644
--- a/third_party/blink/renderer/core/layout/deferred_shaping_controller.h
+++ b/third_party/blink/renderer/core/layout/deferred_shaping_controller.h
@@ -36,6 +36,7 @@
   kLastResort,
   kPrinting,
   kScrollingApi,
+  kTesting,
 };
 
 // DeferredShapingController class manages states of the Deferred Shaping
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
index 342d203..c5a1760 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
@@ -174,7 +174,8 @@
          NGBoxFragment(
              algorithm_->StyleRef().GetWritingDirection(),
              To<NGPhysicalBoxFragment>(layout_result_->PhysicalFragment()))
-             .BaselineOrSynthesize(algorithm_->StyleRef().GetFontBaseline());
+             .FirstBaselineOrSynthesize(
+                 algorithm_->StyleRef().GetFontBaseline());
 }
 
 LayoutUnit FlexItem::AvailableAlignmentSpace() const {
diff --git a/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc b/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc
index 597faa2..076b608b 100644
--- a/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc
+++ b/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc
@@ -143,8 +143,8 @@
                          To<NGPhysicalBoxFragment>(result->PhysicalFragment()));
 
   resolver_->Resolve(MakeGarbageCollected<CustomLayoutFragment>(
-      child_, token_, std::move(result), fragment.Size(), fragment.Baseline(),
-      resolver_->GetScriptState()->GetIsolate()));
+      child_, token_, std::move(result), fragment.Size(),
+      fragment.FirstBaseline(), resolver_->GetScriptState()->GetIsolate()));
 }
 
 void CustomLayoutWorkTask::RunIntrinsicSizesTask(
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 4368776..538ab3e6 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
@@ -1893,7 +1893,7 @@
   absl::optional<LayoutUnit> child_baseline =
       space.BaselineAlgorithmType() == NGBaselineAlgorithmType::kFirstLine
           ? fragment.FirstBaseline()
-          : fragment.Baseline();
+          : fragment.LastBaseline();
   if (child_baseline)
     child_baseline = *child_baseline + child.offset.block_offset;
   if (container_builder_.Baseline() != child_baseline) {
@@ -1919,9 +1919,7 @@
 
   const auto baseline_type = Style().GetFontBaseline();
   const LayoutUnit baseline_offset =
-      block_offset + (Node().IsButton()
-                          ? fragment.FirstBaselineOrSynthesize(baseline_type)
-                          : fragment.BaselineOrSynthesize(baseline_type));
+      block_offset + fragment.FirstBaselineOrSynthesize(baseline_type);
 
   // We prefer a baseline from a child with baseline alignment, and no
   // auto-margins in the cross axis (even if we have to synthesize the
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 73217a7..88039b2 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -864,7 +864,7 @@
 LayoutUnit NGGridLayoutAlgorithm::GetLogicalBaseline(
     const NGBoxFragment& baseline_fragment,
     BaselineGroup baseline_group) const {
-  return baseline_fragment.BaselineOrSynthesize(Style().GetFontBaseline());
+  return baseline_fragment.FirstBaselineOrSynthesize(Style().GetFontBaseline());
 }
 
 LayoutUnit NGGridLayoutAlgorithm::ContributionSizeForGridItem(
@@ -1376,9 +1376,10 @@
         baseline_writing_direction,
         To<NGPhysicalBoxFragment>(result->PhysicalFragment()));
 
-    grid_item.SetAlignmentFallback(track_direction, Style(),
-                                   /* has_synthesized_baseline */
-                                   !baseline_fragment.Baseline().has_value());
+    grid_item.SetAlignmentFallback(
+        track_direction, Style(),
+        /* has_synthesized_baseline */
+        !baseline_fragment.FirstBaseline().has_value());
 
     LayoutUnit baseline =
         ComputeMarginsFor(space, item_style, baseline_writing_direction)
@@ -2821,7 +2822,7 @@
     };
 
     const LayoutUnit baseline =
-        block_offset + fragment.BaselineOrSynthesize(font_baseline_);
+        block_offset + fragment.FirstBaselineOrSynthesize(font_baseline_);
     if (grid_item.IsBaselineSpecifiedForDirection(kForRows)) {
       if (!alignment_baseline_ ||
           IsBeforeInGridOrder(grid_item.resolved_position,
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc
index 39562fc..8df6b1f 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc
@@ -181,13 +181,13 @@
 
   const LayoutUnit numerator_ascent =
       numerator_margins.block_start +
-      numerator_fragment.BaselineOrSynthesize(baseline_type);
+      numerator_fragment.FirstBaselineOrSynthesize(baseline_type);
   const LayoutUnit numerator_descent = numerator_fragment.BlockSize() +
                                        numerator_margins.BlockSum() -
                                        numerator_ascent;
   const LayoutUnit denominator_ascent =
       denominator_margins.block_start +
-      denominator_fragment.BaselineOrSynthesize(baseline_type);
+      denominator_fragment.FirstBaselineOrSynthesize(baseline_type);
   const LayoutUnit denominator_descent = denominator_fragment.BlockSize() +
                                          denominator_margins.BlockSum() -
                                          denominator_ascent;
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_padded_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_padded_layout_algorithm.cc
index 7f2fb08..b1c4ec17 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_padded_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_padded_layout_algorithm.cc
@@ -84,7 +84,7 @@
     NGBoxFragment fragment(ConstraintSpace().GetWritingDirection(),
                            content_fragment);
     content_ascent = content_margins.block_start +
-                     fragment.Baseline().value_or(fragment.BlockSize());
+                     fragment.FirstBaseline().value_or(fragment.BlockSize());
     content_descent =
         fragment.BlockSize() + content_margins.BlockSum() - content_ascent;
   }
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_radical_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_radical_layout_algorithm.cc
index 893258c0..6940eb1 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_radical_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_radical_layout_algorithm.cc
@@ -91,8 +91,8 @@
         ComputeMarginsFor(constraint_space, base.Style(), ConstraintSpace());
     NGBoxFragment fragment(ConstraintSpace().GetWritingDirection(),
                            base_fragment);
-    base_ascent =
-        base_margins.block_start + fragment.BaselineOrSynthesize(baseline_type);
+    base_ascent = base_margins.block_start +
+                  fragment.FirstBaselineOrSynthesize(baseline_type);
     base_descent = fragment.BlockSize() + base_margins.BlockSum() - base_ascent;
   }
   if (index) {
@@ -109,7 +109,7 @@
                            index_fragment);
     index_inline_size = fragment.InlineSize() + index_margins.InlineSum();
     index_ascent = index_margins.block_start +
-                   fragment.BaselineOrSynthesize(baseline_type);
+                   fragment.FirstBaselineOrSynthesize(baseline_type);
     index_descent =
         fragment.BlockSize() + index_margins.BlockSum() - index_ascent;
     horizontal = GetRadicalHorizontalParameters(Style());
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc
index 4376b721..80417d2 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc
@@ -68,7 +68,7 @@
           NGBoxFragment fragment(
               ConstraintSpace().GetWritingDirection(),
               To<NGPhysicalBoxFragment>(result->PhysicalFragment()));
-          LayoutUnit ascent = fragment.BaselineOrSynthesize(baseline_type);
+          LayoutUnit ascent = fragment.FirstBaselineOrSynthesize(baseline_type);
           stretch_sizes.ascent = std::max(stretch_sizes.ascent, ascent),
           stretch_sizes.descent =
               std::max(stretch_sizes.descent, fragment.BlockSize() - ascent);
@@ -159,7 +159,7 @@
     inline_offset += margins.inline_start;
 
     LayoutUnit ascent =
-        margins.block_start + fragment.BaselineOrSynthesize(baseline_type);
+        margins.block_start + fragment.FirstBaselineOrSynthesize(baseline_type);
     *max_row_block_baseline = std::max(*max_row_block_baseline, ascent);
 
     // TODO(crbug.com/1125136): take into account italic correction.
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
index 40fae567..f3649e9e 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
@@ -279,7 +279,7 @@
   child_and_metrics.margins =
       ComputeMarginsFor(constraint_space, child.Style(), ConstraintSpace());
   child_and_metrics.ascent =
-      fragment.BaselineOrSynthesize(Style().GetFontBaseline());
+      fragment.FirstBaselineOrSynthesize(Style().GetFontBaseline());
   child_and_metrics.descent = fragment.BlockSize() - child_and_metrics.ascent +
                               child_and_metrics.margins.block_end;
   child_and_metrics.ascent += child_and_metrics.margins.block_start;
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
index 720c2a81..a513248 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
@@ -296,7 +296,8 @@
   NGBoxFragment base_fragment(
       ConstraintSpace().GetWritingDirection(),
       To<NGPhysicalBoxFragment>(base_layout_result->PhysicalFragment()));
-  LayoutUnit base_ascent = base_fragment.BaselineOrSynthesize(baseline_type);
+  LayoutUnit base_ascent =
+      base_fragment.FirstBaselineOrSynthesize(baseline_type);
 
   // All children are positioned centered relative to the container (and
   // therefore centered relative to themselves).
@@ -327,7 +328,7 @@
       }
     } else {
       LayoutUnit over_ascent =
-          over_fragment.BaselineOrSynthesize(baseline_type);
+          over_fragment.FirstBaselineOrSynthesize(baseline_type);
       ascent += std::max(over_fragment.BlockSize() + parameters.over_gap_min,
                          over_ascent + parameters.over_shift_min);
     }
@@ -362,7 +363,7 @@
         descent += parameters.under_gap_min;
     } else {
       LayoutUnit under_ascent =
-          under_fragment.BaselineOrSynthesize(baseline_type);
+          under_fragment.FirstBaselineOrSynthesize(baseline_type);
       descent += std::max(parameters.under_gap_min,
                           parameters.under_shift_min - under_ascent);
     }
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 c7f54c4d..ce8a06c6 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
@@ -2805,8 +2805,11 @@
   // Set the last baseline only if required.
   if (ConstraintSpace().BaselineAlgorithmType() !=
       NGBaselineAlgorithmType::kFirstLine) {
-    if (auto last_baseline = fragment.Baseline())
+    // TODO(ikilpatrick): Select baseline depending on type.
+    if (auto last_baseline = fragment.LastBaseline())
       container_builder_.SetLastBaseline(block_offset + *last_baseline);
+    else if (auto first_baseline = fragment.FirstBaseline())
+      container_builder_.SetLastBaseline(block_offset + *first_baseline);
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc
index a810c9e..e9bc96d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc
@@ -15,11 +15,15 @@
                                           FontBaseline baseline_type) const {
   // For checkbox and radio controls, we always use the border edge instead of
   // the margin edge.
-  if (physical_fragment_.Style().IsCheckboxOrRadioPart()) {
+  if (physical_fragment_.Style().IsCheckboxOrRadioPart())
     return FontHeight(margins.line_over + BlockSize(), margins.line_under);
-  }
 
-  if (const absl::optional<LayoutUnit> baseline = Baseline()) {
+  // TODO(ikilpatrick): Select baseline based on type.
+  absl::optional<LayoutUnit> baseline = LastBaseline();
+  if (!baseline)
+    baseline = FirstBaseline();
+
+  if (baseline) {
     FontHeight metrics = writing_direction_.IsFlippedLines()
                              ? FontHeight(BlockSize() - *baseline, *baseline)
                              : FontHeight(*baseline, BlockSize() - *baseline);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
index 40e1816..d67e31e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
@@ -42,29 +42,12 @@
     return BlockSize() / 2;
   }
 
-  // Returns the baseline for this fragment wrt. the parent writing mode. Will
-  // return a null baseline if:
-  //  - The fragment has no baseline.
-  //  - The writing modes differ.
-  absl::optional<LayoutUnit> Baseline() const {
+  absl::optional<LayoutUnit> LastBaseline() const {
     if (writing_direction_.GetWritingMode() !=
         physical_fragment_.Style().GetWritingMode())
       return absl::nullopt;
 
-    if (auto last_baseline = PhysicalBoxFragment().LastBaseline())
-      return last_baseline;
-
-    return PhysicalBoxFragment().FirstBaseline();
-  }
-
-  LayoutUnit BaselineOrSynthesize(FontBaseline baseline_type) const {
-    if (auto baseline = Baseline())
-      return *baseline;
-
-    if (baseline_type == kAlphabeticBaseline)
-      return writing_direction_.IsFlippedLines() ? LayoutUnit() : BlockSize();
-
-    return BlockSize() / 2;
+    return PhysicalBoxFragment().LastBaseline();
   }
 
   // Compute baseline metrics (ascent/descent) for this box.
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 fd6fdf2..42de8a3 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
@@ -1277,7 +1277,7 @@
         To<NGPhysicalBoxFragment>(child_result->PhysicalFragment());
     NGBoxFragment fragment(table_writing_direction, physical_fragment);
     if (child.IsTableSection() && !table_baseline) {
-      if (const auto& section_baseline = fragment.Baseline())
+      if (const auto& section_baseline = fragment.FirstBaseline())
         table_baseline = *section_baseline + child_block_offset;
     }
 
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 3fe6966..dbb483e 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
@@ -106,8 +106,8 @@
         To<NGPhysicalBoxFragment>(row_result->PhysicalFragment()));
 
     if (!section_baseline) {
-      DCHECK(fragment.Baseline());
-      section_baseline = fragment.Baseline();
+      DCHECK(fragment.FirstBaseline());
+      section_baseline = fragment.FirstBaseline();
     }
     container_builder_.AddResult(*row_result, offset);
     offset.block_offset += fragment.BlockSize();
diff --git a/third_party/blink/renderer/core/paint/paint_timing.cc b/third_party/blink/renderer/core/paint/paint_timing.cc
index d900d127..ef6bd71 100644
--- a/third_party/blink/renderer/core/paint/paint_timing.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/layout/deferred_shaping_controller.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/loader/interactive_detector.h"
 #include "third_party/blink/renderer/core/loader/progress_tracker.h"
@@ -357,6 +358,11 @@
     base::TimeDelta fcp = stamp - timing->NavigationStartAsMonotonicTime();
     coordinator->OnFirstContentfulPaint(fcp);
   }
+
+  if (auto* ds_controller =
+          DeferredShapingController::From(*GetSupplementable())) {
+    ds_controller->OnFirstContentfulPaint();
+  }
 }
 
 void PaintTiming::SetFirstImagePaintPresentation(base::TimeTicks stamp) {
diff --git a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window.cc b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window.cc
index 433463c..e47eebd8 100644
--- a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window.cc
+++ b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window.cc
@@ -66,6 +66,10 @@
   ScriptWrappable::Trace(visitor);
 }
 
+void CrosWindow::Update(mojom::blink::CrosWindowInfoPtr window_info_ptr) {
+  window_ = std::move(window_info_ptr);
+}
+
 String CrosWindow::appId() {
   return window_->app_id;
 }
diff --git a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window.h b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window.h
index b2859d8..f1ca62b 100644
--- a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window.h
+++ b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window.h
@@ -24,6 +24,9 @@
 
   void Trace(Visitor*) const override;
 
+  // Sets the CrosWindowInfoPtr to `window_info_ptr`.
+  void Update(mojom::blink::CrosWindowInfoPtr window_info_ptr);
+
   String id();
 
   String title();
diff --git a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.cc b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.cc
index b6933709..755c4f6b 100644
--- a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.cc
+++ b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.cc
@@ -159,23 +159,24 @@
 }
 
 void CrosWindowManagement::DispatchWindowClosedEvent(
-    const base::UnguessableToken& window_id) {
-  WTF::String closed_window_id_string = WTF::String(window_id.ToString());
-  auto* info_ptr =
+    mojom::blink::CrosWindowInfoPtr window) {
+  WTF::String closed_window_id_string = WTF::String(window->id.ToString());
+  Member<CrosWindow>* window_ptr =
       std::find_if(windows_.begin(), windows_.end(),
-                   [&closed_window_id_string](Member<CrosWindow> info) {
-                     return info.Get()->id() == closed_window_id_string;
+                   [&closed_window_id_string](Member<CrosWindow> cros_window) {
+                     return cros_window->id() == closed_window_id_string;
                    });
 
   auto* event_init = CrosWindowEventInit::Create();
-
-  // If we don't find the closed window by id (i.e. the cache is unpopulated),
-  // manually create the cros window.
-  if (info_ptr == windows_.end()) {
+  if (window_ptr == windows_.end()) {
     event_init->setWindow(
-        MakeGarbageCollected<CrosWindow>(this, std::move(window_id)));
+        MakeGarbageCollected<CrosWindow>(this, std::move(window)));
   } else {
-    event_init->setWindow(*info_ptr);
+    // Update cached CrosWindow member with CrosWindowInfoPtr with nulled
+    // attributes.
+    window_ptr->Get()->Update(std::move(window));
+    event_init->setWindow(window_ptr->Get());
+    windows_.erase(window_ptr);
   }
 
   DispatchEvent(
diff --git a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.h b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.h
index ed4c526..dc468a53 100644
--- a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.h
+++ b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.h
@@ -61,7 +61,7 @@
   void DispatchAcceleratorEvent(
       mojom::blink::AcceleratorEventPtr event) override;
   void DispatchWindowClosedEvent(
-      const base::UnguessableToken& window_id) override;
+      mojom::blink::CrosWindowInfoPtr window) override;
 
  private:
   HeapMojoRemote<mojom::blink::CrosWindowManagementFactory>
diff --git a/third_party/blink/renderer/modules/cache_storage/cache.cc b/third_party/blink/renderer/modules/cache_storage/cache.cc
index 27d73255..fbe3a643 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache.cc
@@ -4,10 +4,10 @@
 
 #include "third_party/blink/renderer/modules/cache_storage/cache.h"
 
-#include <algorithm>
 #include <memory>
 #include <utility>
 
+#include "base/containers/contains.h"
 #include "base/metrics/histogram_macros.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "third_party/blink/public/common/cache_storage/cache_storage_utils.h"
@@ -62,9 +62,8 @@
   if (headers->Get("vary", varyHeader)) {
     Vector<String> fields;
     varyHeader.Split(',', fields);
-    return std::any_of(fields.begin(), fields.end(), [](const String& field) {
-      return field.StripWhiteSpace() == "*";
-    });
+    String (String::*strip_whitespace)() const = &String::StripWhiteSpace;
+    return base::Contains(fields, "*", strip_whitespace);
   }
   return false;
 }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index d1101948..d89b14f4 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h"
 
-#include <algorithm>
 #include <cmath>
 #include <memory>
 
@@ -12,6 +11,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/checked_math.h"
+#include "base/ranges/algorithm.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_metrics.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_canvasfilter_string.h"
@@ -698,8 +698,8 @@
 }
 
 static bool LineDashSequenceIsValid(const Vector<double>& dash) {
-  return std::all_of(dash.begin(), dash.end(),
-                     [](double d) { return std::isfinite(d) && d >= 0; });
+  return base::ranges::all_of(
+      dash, [](double d) { return std::isfinite(d) && d >= 0; });
 }
 
 void BaseRenderingContext2D::setLineDash(const Vector<double>& dash) {
diff --git a/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper_unittest.cc b/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper_unittest.cc
index 3257900..ad3d7e4 100644
--- a/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper_unittest.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper_unittest.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/direct_sockets/tcp_writable_stream_wrapper.h"
 
 #include "base/callback_helpers.h"
+#include "base/ranges/algorithm.h"
 #include "base/test/mock_callback.h"
 #include "net/base/net_errors.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
@@ -140,7 +141,7 @@
 }
 
 bool IsAllNulls(base::span<const uint8_t> data) {
-  return std::all_of(data.begin(), data.end(), [](uint8_t c) { return !c; });
+  return base::ranges::all_of(data, [](uint8_t c) { return !c; });
 }
 
 TEST(TCPWritableStreamWrapperTest, AsyncWrite) {
diff --git a/third_party/blink/renderer/modules/eventsource/event_source.cc b/third_party/blink/renderer/modules/eventsource/event_source.cc
index 374c9173..d686e5a4 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source.cc
+++ b/third_party/blink/renderer/modules/eventsource/event_source.cc
@@ -34,6 +34,7 @@
 
 #include <memory>
 
+#include "base/ranges/algorithm.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_url_request.h"
@@ -77,8 +78,8 @@
                const std::string& value,
                network::mojom::FetchResponseType response_type) {
   if (response_type == network::mojom::FetchResponseType::kCors &&
-      (value.size() > 128 || std::any_of(value.begin(), value.end(),
-                                         IsCorsUnsafeRequestHeaderByte))) {
+      (value.size() > 128 ||
+       base::ranges::any_of(value, IsCorsUnsafeRequestHeaderByte))) {
     UseCounter::Count(context,
                       WebFeature::kFetchEventSourceLastEventIdCorsUnSafe);
   }
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h
index d528a1f..bd95caa7 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h
@@ -7,6 +7,7 @@
 
 #include "base/files/file.h"
 #include "base/files/file_error_or.h"
+#include "base/sequence_checker.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_capacity_allocation_host.mojom-blink.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h"
@@ -47,30 +48,49 @@
   virtual base::FileErrorOr<int> Write(int64_t offset,
                                        const base::span<uint8_t> data) = 0;
 
+  // Returns the current size of this file, or a file error on failure.
+  virtual base::FileErrorOr<int64_t> GetLength() = 0;
+
   // Asynchronously get the size of the file. Returns the current size of this
   // file, or a file error on failure.
-  virtual void GetLength(
+  virtual void GetLengthAsync(
       base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) = 0;
 
+  // Tuncates the file to the given length. `length` cannot be negative.
+  // If `length` is greater than the current size of the file, the file is
+  // extended with zeros.
+  virtual base::FileErrorOr<bool> SetLength(int64_t length) = 0;
+
   // Asynchronously truncates the file to the given length. `length` cannot be
   // negative. If `length` is greater than the current size of the file, the
   // file is extended with zeros.
-  virtual void SetLength(
+  virtual void SetLengthAsync(
       int64_t length,
       base::OnceCallback<void(base::File::Error)> callback) = 0;
 
+  // Instructs the filesystem to flush the file to disk.
+  virtual bool Flush() = 0;
+
   // Asynchronously instructs the filesystem to flush the file to disk.
-  virtual void Flush(base::OnceCallback<void(bool)> callback) = 0;
+  virtual void FlushAsync(base::OnceCallback<void(bool)> callback) = 0;
+
+  // Close the file. Destroying this object will close the file automatically.
+  virtual void Close() = 0;
 
   // Asynchronously close the file. Destroying this object will close the file
   // automatically.
-  virtual void Close(base::OnceClosure callback) = 0;
+  virtual void CloseAsync(base::OnceClosure callback) = 0;
 
   // Returns `true` if the file handle wrapped by this object is valid.
   virtual bool IsValid() const = 0;
 
   // GarbageCollected
   virtual void Trace(Visitor* visitor) const {}
+
+ protected:
+  // Every subclass method that runs on the main thread should
+  // DCHECK_CALLED_ON_VALID_SEQUENCE with this checker.
+  SEQUENCE_CHECKER(sequence_checker_);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
index 0d54498..3d5d290 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
@@ -101,6 +101,7 @@
 base::FileErrorOr<int> FileSystemAccessIncognitoFileDelegate::Read(
     int64_t offset,
     base::span<uint8_t> data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK_GE(offset, 0);
 
   base::File::Error file_error;
@@ -126,6 +127,7 @@
 base::FileErrorOr<int> FileSystemAccessIncognitoFileDelegate::Write(
     int64_t offset,
     const base::span<uint8_t> data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK_GE(offset, 0);
 
   mojo::ScopedDataPipeProducerHandle producer_handle;
@@ -159,19 +161,38 @@
   return file_error == base::File::Error::FILE_OK ? bytes_written : file_error;
 }
 
-void FileSystemAccessIncognitoFileDelegate::GetLength(
+base::FileErrorOr<int64_t> FileSystemAccessIncognitoFileDelegate::GetLength() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::File::Error file_error;
+  int64_t length;
+  mojo_ptr_->GetLength(&file_error, &length);
+  CHECK_GE(length, 0);
+  return file_error == base::File::Error::FILE_OK ? length : file_error;
+}
+
+void FileSystemAccessIncognitoFileDelegate::GetLengthAsync(
     base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   mojo_ptr_->GetLength(WTF::Bind(
       [](base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback,
-         base::File::Error file_error, uint64_t length) {
+         base::File::Error file_error, int64_t length) {
+        CHECK_GE(length, 0);
         std::move(callback).Run(
             file_error == base::File::Error::FILE_OK ? length : file_error);
       },
       std::move(callback)));
 }
 
-void FileSystemAccessIncognitoFileDelegate::SetLength(
+base::FileErrorOr<bool> FileSystemAccessIncognitoFileDelegate::SetLength(
+    int64_t length) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK_GE(length, 0);
+  base::File::Error file_error;
+  mojo_ptr_->SetLength(length, &file_error);
+  return file_error == base::File::Error::FILE_OK ? true : file_error;
+}
+
+void FileSystemAccessIncognitoFileDelegate::SetLengthAsync(
     int64_t length,
     base::OnceCallback<void(base::File::Error)> callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
@@ -180,7 +201,17 @@
   mojo_ptr_->SetLength(length, WTF::Bind(std::move(callback)));
 }
 
-void FileSystemAccessIncognitoFileDelegate::Flush(
+bool FileSystemAccessIncognitoFileDelegate::Flush() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Flush is a no-op for in-memory file systems. Even if the file delegate is
+  // used for other FS types, writes through the FileSystemOperationRunner are
+  // automatically flushed. If this proves to be too slow, we can consider
+  // changing the FileSystemAccessFileDelegateHostImpl to write with a
+  // FileStreamWriter and only flushing when this method is called.
+  return true;
+}
+
+void FileSystemAccessIncognitoFileDelegate::FlushAsync(
     base::OnceCallback<void(bool)> callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   // Flush is a no-op for in-memory file systems. Even if the file delegate is
@@ -192,7 +223,13 @@
                          WTF::Bind(std::move(callback), /*success=*/true));
 }
 
-void FileSystemAccessIncognitoFileDelegate::Close(base::OnceClosure callback) {
+void FileSystemAccessIncognitoFileDelegate::Close() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  mojo_ptr_.reset();
+}
+
+void FileSystemAccessIncognitoFileDelegate::CloseAsync(
+    base::OnceClosure callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   mojo_ptr_.reset();
 
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.h
index fd8273ac..b371795b 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.h
@@ -39,15 +39,17 @@
                               base::span<uint8_t> data) override;
   base::FileErrorOr<int> Write(int64_t offset,
                                const base::span<uint8_t> data) override;
-
-  void GetLength(
+  base::FileErrorOr<int64_t> GetLength() override;
+  void GetLengthAsync(
       base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) override;
-  void SetLength(int64_t length,
-                 base::OnceCallback<void(base::File::Error)> callback) override;
-
-  void Flush(base::OnceCallback<void(bool)> callback) override;
-  void Close(base::OnceClosure callback) override;
-
+  base::FileErrorOr<bool> SetLength(int64_t new_length) override;
+  void SetLengthAsync(
+      int64_t new_length,
+      base::OnceCallback<void(base::File::Error)> callback) override;
+  bool Flush() override;
+  void FlushAsync(base::OnceCallback<void(bool)> callback) override;
+  void Close() override;
+  void CloseAsync(base::OnceClosure callback) override;
   bool IsValid() const override { return mojo_ptr_.is_bound(); }
 
   void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
index a396f6d12..573cd4f 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
@@ -68,6 +68,7 @@
 base::FileErrorOr<int> FileSystemAccessRegularFileDelegate::Read(
     int64_t offset,
     base::span<uint8_t> data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK_GE(offset, 0);
 
   int size = base::checked_cast<int>(data.size());
@@ -82,6 +83,7 @@
 base::FileErrorOr<int> FileSystemAccessRegularFileDelegate::Write(
     int64_t offset,
     const base::span<uint8_t> data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK_GE(offset, 0);
 
   int write_size = base::checked_cast<int>(data.size());
@@ -112,7 +114,15 @@
   return result < 0 ? base::File::GetLastFileError() : result;
 }
 
-void FileSystemAccessRegularFileDelegate::GetLength(
+base::FileErrorOr<int64_t> FileSystemAccessRegularFileDelegate::GetLength() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  int64_t length = backing_file_.GetLength();
+
+  // If the length is negative, the file operation failed.
+  return length >= 0 ? length : base::File::GetLastFileError();
+}
+
+void FileSystemAccessRegularFileDelegate::GetLengthAsync(
     base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   auto wrapped_callback =
@@ -160,7 +170,46 @@
   std::move(callback).Run(std::move(error_or_length));
 }
 
-void FileSystemAccessRegularFileDelegate::SetLength(
+base::FileErrorOr<bool> FileSystemAccessRegularFileDelegate::SetLength(
+    int64_t new_length) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK_GE(new_length, 0);
+
+  if (!capacity_tracker_->RequestFileCapacityChangeSync(new_length))
+    return base::File::FILE_ERROR_NO_SPACE;
+
+#if BUILDFLAG(IS_MAC)
+  // On macOS < 10.15, a sandboxing limitation causes failures in ftruncate()
+  // syscalls issued from renderers. For this reason, base::File::SetLength()
+  // fails in the renderer. We work around this problem by calling ftruncate()
+  // in the browser process. See https://crbug.com/1084565.
+  if (!base::mac::IsAtLeastOS10_15()) {
+    if (!file_utilities_host_.is_bound()) {
+      context_->GetBrowserInterfaceBroker().GetInterface(
+          file_utilities_host_.BindNewPipeAndPassReceiver(task_runner_));
+    }
+    bool result;
+    file_utilities_host_->SetLength(std::move(backing_file_), new_length,
+                                    &backing_file_, &result);
+    if (result) {
+      capacity_tracker_->CommitFileSizeChange(new_length);
+      return true;
+    }
+    // Unfortunately we don't have access to the error code when using
+    // the FileUtilitiesHost, so we can say the operation failed but
+    // not why (ex: out of quota).
+    return base::File::Error::FILE_ERROR_FAILED;
+  }
+#endif  // BUILDFLAG(IS_MAC)
+
+  if (backing_file_.SetLength(new_length)) {
+    capacity_tracker_->CommitFileSizeChange(new_length);
+    return true;
+  }
+  return base::File::GetLastFileError();
+}
+
+void FileSystemAccessRegularFileDelegate::SetLengthAsync(
     int64_t new_length,
     base::OnceCallback<void(base::File::Error)> callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
@@ -265,7 +314,12 @@
   std::move(callback).Run(error);
 }
 
-void FileSystemAccessRegularFileDelegate::Flush(
+bool FileSystemAccessRegularFileDelegate::Flush() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return backing_file_.Flush();
+}
+
+void FileSystemAccessRegularFileDelegate::FlushAsync(
     base::OnceCallback<void(bool)> callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   auto wrapped_callback =
@@ -306,7 +360,13 @@
   std::move(callback).Run(success);
 }
 
-void FileSystemAccessRegularFileDelegate::Close(base::OnceClosure callback) {
+void FileSystemAccessRegularFileDelegate::Close() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  backing_file_.Close();
+}
+
+void FileSystemAccessRegularFileDelegate::CloseAsync(
+    base::OnceClosure callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   auto wrapped_callback = CrossThreadOnceClosure(std::move(callback));
 
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h
index e07ac0904..54963344 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.h
@@ -65,15 +65,17 @@
                               base::span<uint8_t> data) override;
   base::FileErrorOr<int> Write(int64_t offset,
                                const base::span<uint8_t> data) override;
-
-  void GetLength(
+  base::FileErrorOr<int64_t> GetLength() override;
+  void GetLengthAsync(
       base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) override;
-  void SetLength(int64_t new_length,
-                 base::OnceCallback<void(base::File::Error)> callback) override;
-
-  void Flush(base::OnceCallback<void(bool)> callback) override;
-  void Close(base::OnceClosure callback) override;
-
+  base::FileErrorOr<bool> SetLength(int64_t new_length) override;
+  void SetLengthAsync(
+      int64_t new_length,
+      base::OnceCallback<void(base::File::Error)> callback) override;
+  bool Flush() override;
+  void FlushAsync(base::OnceCallback<void(bool)> callback) override;
+  void Close() override;
+  void CloseAsync(base::OnceClosure callback) override;
   bool IsValid() const override { return backing_file_.IsValid(); }
 
  private:
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
index 35f522bb..271fe23 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
@@ -4,9 +4,11 @@
 
 #include "third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h"
 
+#include "base/feature_list.h"
 #include "base/files/file_error_or.h"
 #include "base/numerics/checked_math.h"
 #include "build/build_config.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h"
@@ -33,13 +35,39 @@
 }
 
 void FileSystemSyncAccessHandle::Trace(Visitor* visitor) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ScriptWrappable::Trace(visitor);
   visitor->Trace(file_delegate_);
   visitor->Trace(access_handle_remote_);
   visitor->Trace(queued_close_resolver_);
 }
 
-ScriptPromise FileSystemSyncAccessHandle::close(ScriptState* script_state) {
+ScriptValue FileSystemSyncAccessHandle::close(ScriptState* script_state) {
+  if (base::FeatureList::IsEnabled(
+          blink::features::kSyncAccessHandleAllSyncSurface)) {
+    CloseSync(script_state);
+    return ScriptValue::From(script_state, ToV8UndefinedGenerator());
+  } else {
+    return ScriptValue::From(script_state, CloseAsync(script_state));
+  }
+}
+
+void FileSystemSyncAccessHandle::CloseSync(ScriptState* script_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (is_closed_ || !access_handle_remote_.is_bound()) {
+    // close() is idempotent.
+    return;
+  }
+
+  DCHECK(file_delegate_->IsValid()) << "file delgate invalidated before close";
+
+  is_closed_ = true;
+  file_delegate_->Close();
+  access_handle_remote_->Close();
+}
+
+ScriptPromise FileSystemSyncAccessHandle::CloseAsync(
+    ScriptState* script_state) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   auto promise = resolver->Promise();
 
@@ -80,7 +108,7 @@
   DCHECK(file_delegate_->IsValid())
       << "file I/O operation queued after file closed";
 
-  file_delegate_->Close(WTF::Bind(
+  file_delegate_->CloseAsync(WTF::Bind(
       [](ScriptPromiseResolver* resolver,
          FileSystemSyncAccessHandle* access_handle) {
         ScriptState* script_state = resolver->GetScriptState();
@@ -95,29 +123,58 @@
       WrapPersistent(resolver), WrapPersistent(this)));
 }
 
-ScriptPromise FileSystemSyncAccessHandle::flush(
-    ScriptState* script_state,
-    ExceptionState& exception_state) {
+ScriptValue FileSystemSyncAccessHandle::flush(ScriptState* script_state,
+                                              ExceptionState& exception_state) {
+  if (base::FeatureList::IsEnabled(
+          blink::features::kSyncAccessHandleAllSyncSurface)) {
+    FlushSync(script_state, exception_state);
+    return ScriptValue::From(script_state, ToV8UndefinedGenerator());
+  } else {
+    return ScriptValue::From(script_state, FlushAsync(script_state));
+  }
+}
+
+void FileSystemSyncAccessHandle::FlushSync(ScriptState* script_state,
+                                           ExceptionState& exception_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (is_closed_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "The file was already closed");
-    return ScriptPromise();
+    return;
+  }
+
+  DCHECK(file_delegate_->IsValid()) << "file delgate invalidated before flush";
+
+  bool success = file_delegate()->Flush();
+  if (!success) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "flush failed");
+  }
+}
+
+ScriptPromise FileSystemSyncAccessHandle::FlushAsync(
+    ScriptState* script_state) {
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise result = resolver->Promise();
+
+  if (is_closed_) {
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
+        "The file was already closed"));
+    return result;
   }
 
   if (!EnterOperation()) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kInvalidStateError,
-        "Another I/O operation is in progress on the same file");
-    return ScriptPromise();
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
+        "Another I/O operation is in progress on the same file"));
+    return result;
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = resolver->Promise();
-
   DCHECK(file_delegate()->IsValid())
       << "file I/O operation queued after file closed";
 
-  file_delegate()->Flush(WTF::Bind(WTF::Bind(
+  file_delegate()->FlushAsync(WTF::Bind(WTF::Bind(
       [](ScriptPromiseResolver* resolver,
          FileSystemSyncAccessHandle* access_handle, bool success) {
         ScriptState* script_state = resolver->GetScriptState();
@@ -139,29 +196,63 @@
   return result;
 }
 
-ScriptPromise FileSystemSyncAccessHandle::getSize(
+ScriptValue FileSystemSyncAccessHandle::getSize(
     ScriptState* script_state,
     ExceptionState& exception_state) {
+  if (base::FeatureList::IsEnabled(
+          blink::features::kSyncAccessHandleAllSyncSurface)) {
+    return ScriptValue::From(script_state,
+                             GetSizeSync(script_state, exception_state));
+  } else {
+    return ScriptValue::From(script_state, GetSizeAsync(script_state));
+  }
+}
+
+uint64_t FileSystemSyncAccessHandle::GetSizeSync(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (is_closed_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "The file was already closed");
-    return ScriptPromise();
+    return 0;
+  }
+
+  DCHECK(file_delegate_->IsValid())
+      << "file delgate invalidated before getSize";
+
+  base::FileErrorOr<int64_t> error_or_length = file_delegate()->GetLength();
+  if (error_or_length.is_error()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "getSize failed");
+    return 0;
+  }
+  return base::as_unsigned(error_or_length.value());
+}
+
+ScriptPromise FileSystemSyncAccessHandle::GetSizeAsync(
+    ScriptState* script_state) {
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise result = resolver->Promise();
+
+  if (is_closed_) {
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
+        "The file was already closed"));
+    return result;
   }
 
   if (!EnterOperation()) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kInvalidStateError,
-        "Another I/O operation is in progress on the same file");
-    return ScriptPromise();
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
+        "Another I/O operation is in progress on the same file"));
+    return result;
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = resolver->Promise();
-
   DCHECK(file_delegate()->IsValid())
       << "file I/O operation queued after file closed";
 
-  file_delegate()->GetLength(WTF::Bind(
+  file_delegate()->GetLengthAsync(WTF::Bind(
       [](ScriptPromiseResolver* resolver,
          FileSystemSyncAccessHandle* access_handle,
          base::FileErrorOr<int64_t> error_or_length) {
@@ -184,35 +275,86 @@
   return result;
 }
 
-ScriptPromise FileSystemSyncAccessHandle::truncate(
+ScriptValue FileSystemSyncAccessHandle::truncate(
     ScriptState* script_state,
     uint64_t size,
     ExceptionState& exception_state) {
+  if (base::FeatureList::IsEnabled(
+          blink::features::kSyncAccessHandleAllSyncSurface)) {
+    TruncateSync(script_state, size, exception_state);
+    return ScriptValue::From(script_state, ToV8UndefinedGenerator());
+  } else {
+    return ScriptValue::From(script_state, TruncateAsync(script_state, size));
+  }
+}
+
+void FileSystemSyncAccessHandle::TruncateSync(ScriptState* script_state,
+                                              uint64_t size,
+                                              ExceptionState& exception_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (is_closed_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "The file was already closed");
-    return ScriptPromise();
+    return;
+  }
+
+  DCHECK(file_delegate_->IsValid())
+      << "file delgate invalidated before truncate";
+
+  if (!base::CheckedNumeric<int64_t>(size).IsValid() ||
+      !base::CheckedNumeric<uint64_t>(size).IsValid()) {
+    exception_state.ThrowTypeError("Cannot truncate file to given length");
+    return;
+  }
+
+  base::FileErrorOr<bool> result = file_delegate()->SetLength(size);
+  if (!result.is_error())
+    return;
+
+  base::File::Error file_error = result.error();
+  if (file_error == base::File::FILE_ERROR_NO_SPACE) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kQuotaExceededError,
+                                      "No space available for this operation");
+    return;
+  }
+  if (file_error != base::File::FILE_OK) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "truncate failed");
+    return;
+  }
+}
+
+ScriptPromise FileSystemSyncAccessHandle::TruncateAsync(
+    ScriptState* script_state,
+    uint64_t size) {
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise result = resolver->Promise();
+
+  if (is_closed_) {
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
+        "The file was already closed"));
+    return result;
+  }
+
+  if (!base::CheckedNumeric<int64_t>(size).IsValid() ||
+      !base::CheckedNumeric<uint64_t>(size).IsValid()) {
+    resolver->Reject(V8ThrowException::CreateTypeError(
+        script_state->GetIsolate(), "Cannot truncate file to given length"));
+    return result;
   }
 
   if (!EnterOperation()) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kInvalidStateError,
-        "Another I/O operation is in progress on the same file");
-    return ScriptPromise();
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kInvalidStateError,
+        "Another I/O operation is in progress on the same file"));
+    return result;
   }
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-  ScriptPromise result = resolver->Promise();
-
   DCHECK(file_delegate()->IsValid())
       << "file I/O operation queued after file closed";
 
-  if (!base::CheckedNumeric<int64_t>(size).IsValid()) {
-    exception_state.ThrowTypeError("Cannot truncate file to given length");
-    return result;
-  }
-
-  file_delegate()->SetLength(
+  file_delegate()->SetLengthAsync(
       size,
       WTF::Bind(
           [](ScriptPromiseResolver* resolver,
@@ -248,13 +390,28 @@
     MaybeShared<DOMArrayBufferView> buffer,
     FileSystemReadWriteOptions* options,
     ExceptionState& exception_state) {
-  OperationScope scope(this);
-  if (!scope.entered_operation()) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kInvalidStateError,
-        "There is a pending operation on the access handle");
-    return 0;
+  if (base::FeatureList::IsEnabled(
+          blink::features::kSyncAccessHandleAllSyncSurface)) {
+    return DoRead(buffer, options, exception_state);
+  } else {
+    // TODO(crbug.com/1338340): OperationScope is only used for async methods.
+    // Remove once we migrate all methods to be sync.
+    OperationScope scope(this);
+    if (!scope.entered_operation()) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kInvalidStateError,
+          "There is a pending operation on the access handle");
+      return 0;
+    }
+    return DoRead(buffer, options, exception_state);
   }
+}
+
+uint64_t FileSystemSyncAccessHandle::DoRead(
+    MaybeShared<DOMArrayBufferView> buffer,
+    FileSystemReadWriteOptions* options,
+    ExceptionState& exception_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!file_delegate()->IsValid() || is_closed_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
@@ -285,13 +442,28 @@
     MaybeShared<DOMArrayBufferView> buffer,
     FileSystemReadWriteOptions* options,
     ExceptionState& exception_state) {
-  OperationScope scope(this);
-  if (!scope.entered_operation()) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kInvalidStateError,
-        "There is a pending operation on the access handle");
-    return 0;
+  if (base::FeatureList::IsEnabled(
+          blink::features::kSyncAccessHandleAllSyncSurface)) {
+    return DoWrite(buffer, options, exception_state);
+  } else {
+    // TODO(crbug.com/1338340): OperationScope is only used for async methods.
+    // Remove once we migrate all methods to be sync.
+    OperationScope scope(this);
+    if (!scope.entered_operation()) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kInvalidStateError,
+          "There is a pending operation on the access handle");
+      return 0;
+    }
+    return DoWrite(buffer, options, exception_state);
   }
+}
+
+uint64_t FileSystemSyncAccessHandle::DoWrite(
+    MaybeShared<DOMArrayBufferView> buffer,
+    FileSystemReadWriteOptions* options,
+    ExceptionState& exception_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!file_delegate()->IsValid() || is_closed_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h
index 46b6a4c..c17a5f9 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h
@@ -5,6 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_SYNC_ACCESS_HANDLE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_SYNC_ACCESS_HANDLE_H_
 
+#include "base/feature_list.h"
+#include "base/sequence_checker.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_access_handle_host.mojom-blink.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -38,13 +41,13 @@
   // GarbageCollected
   void Trace(Visitor* visitor) const override;
 
-  ScriptPromise close(ScriptState*);
+  ScriptValue close(ScriptState*);
 
-  ScriptPromise flush(ScriptState*, ExceptionState&);
+  ScriptValue flush(ScriptState*, ExceptionState&);
 
-  ScriptPromise getSize(ScriptState*, ExceptionState&);
+  ScriptValue getSize(ScriptState*, ExceptionState&);
 
-  ScriptPromise truncate(ScriptState*, uint64_t size, ExceptionState&);
+  ScriptValue truncate(ScriptState*, uint64_t size, ExceptionState&);
 
   uint64_t read(MaybeShared<DOMArrayBufferView> buffer,
                 FileSystemReadWriteOptions* options,
@@ -55,9 +58,30 @@
                  ExceptionState&);
 
  private:
+  void CloseSync(ScriptState*);
+  ScriptPromise CloseAsync(ScriptState*);
+  void FlushSync(ScriptState*, ExceptionState&);
+  ScriptPromise FlushAsync(ScriptState*);
+  uint64_t GetSizeSync(ScriptState*, ExceptionState&);
+  ScriptPromise GetSizeAsync(ScriptState*);
+  void TruncateSync(ScriptState*, uint64_t size, ExceptionState&);
+  ScriptPromise TruncateAsync(ScriptState*, uint64_t size);
+  uint64_t DoRead(MaybeShared<DOMArrayBufferView> buffer,
+                  FileSystemReadWriteOptions* options,
+                  ExceptionState&);
+  uint64_t DoWrite(MaybeShared<DOMArrayBufferView> buffer,
+                   FileSystemReadWriteOptions* options,
+                   ExceptionState&);
+
   void DispatchQueuedClose();
 
+  // Must be called right before calling async methods on file_delegate.
   bool EnterOperation() {
+    if (base::FeatureList::IsEnabled(
+            blink::features::kSyncAccessHandleAllSyncSurface)) {
+      NOTREACHED();
+      return false;
+    }
     if (io_pending_)
       return false;
     io_pending_ = true;
@@ -65,15 +89,28 @@
   }
 
   void ExitOperation() {
+    if (base::FeatureList::IsEnabled(
+            blink::features::kSyncAccessHandleAllSyncSurface)) {
+      NOTREACHED();
+      return;
+    }
     DCHECK(io_pending_);
     io_pending_ = false;
     DispatchQueuedClose();
   }
+
   FileSystemAccessFileDelegate* file_delegate() {
-    DCHECK(io_pending_);
+    DCHECK(io_pending_ ||
+           base::FeatureList::IsEnabled(
+               blink::features::kSyncAccessHandleAllSyncSurface));
     return file_delegate_.Get();
   }
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // TODO(crbug.com/1338340): This method is only used for async methods.
+  // Remove once we migrate all methods to be sync.
+  //
   // The {OperationScope} is used to call {EnterOperation()} and
   // {ExitOperation()} in the synchronous functions {read} and {write}.
   // {OperationScope} calls {ExitOperation()} automatically in its destructor.
@@ -108,6 +145,9 @@
   HeapMojoRemote<mojom::blink::FileSystemAccessAccessHandleHost>
       access_handle_remote_;
 
+  // TODO(crbug.com/1338340): This method is only used for async methods.
+  // Remove once we migrate all methods to be sync.
+  //
   // True when an I/O operation other than close is underway.
   //
   // Set to {true} whenever an async operation is started, and back to {false}
@@ -123,6 +163,9 @@
 
   bool is_closed_ = false;
 
+  // crbug.com/1338340: Note that this is only used (and valid) when async
+  // methods are in-use before the migration to the all-sync interface.
+  //
   // Non-null when a close() I/O is queued behind another I/O operation.
   //
   // Set when close() is called while another I/O operation is underway. Cleared
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.idl b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.idl
index a3afbb0..58bb7c14 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.idl
@@ -5,24 +5,32 @@
 // TODO(fivedots): Replace link with explainer, once it lands.
 // https://docs.google.com/document/d/1g7ZCqZ5NdiU7oqyCpsc2iZ7rRAY1ZXO-9VoG4LfP7fM
 
+// TODO(crbug.com/1338340): Update `any` return type to a specific type once
+// we migrate all methods to be sync. Currently, `any` is used to hold either
+// Promise or value during migration.
+
 [
   Exposed=DedicatedWorker,
   RuntimeEnabled=FileSystemAccessAccessHandle,
   SecureContext
 ] interface FileSystemSyncAccessHandle {
-  [CallWith=ScriptState, Measure] Promise<void> close();
+  [CallWith=ScriptState, Measure] any close();
 
 [
   CallWith=ScriptState, RaisesException, Measure
-] Promise<void> flush();
+] any flush();
 
 [
   CallWith=ScriptState, RaisesException, Measure
-] Promise<unsigned long long> getSize();
+] any getSize();
 
+// TODO(crbug.com/1338340): Add back [EnforceRange] to `size` after all methods
+// migrated to be sync. This check is currently done in 
+// `file_system_sync_access_handle.cc` due to `any` return type wrapping a
+// ScriptPromise throwing a TypeError instead of rejecting a promise.
 [
   CallWith = ScriptState, RaisesException, Measure
-] Promise<void> truncate([EnforceRange] unsigned long long size);
+] any truncate(unsigned long long size);
 
 [
   RaisesException, Measure
diff --git a/third_party/blink/renderer/modules/filesystem/dom_file_path.cc b/third_party/blink/renderer/modules/filesystem/dom_file_path.cc
index 1f05e81..8df0e822 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_file_path.cc
+++ b/third_party/blink/renderer/modules/filesystem/dom_file_path.cc
@@ -30,6 +30,7 @@
 
 #include "third_party/blink/renderer/modules/filesystem/dom_file_path.h"
 
+#include "base/ranges/algorithm.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -119,10 +120,9 @@
   // ".." or "." is likely an attempt to break out of the sandbox.
   Vector<String> components;
   path.Split(DOMFilePath::kSeparator, components);
-  return std::none_of(components.begin(), components.end(),
-                      [](const String& component) {
-                        return component == "." || component == "..";
-                      });
+  return base::ranges::none_of(components, [](const String& component) {
+    return component == "." || component == "..";
+  });
 }
 
 bool DOMFilePath::IsValidName(const String& name) {
diff --git a/third_party/blink/renderer/modules/native_io/native_io_file_manager.cc b/third_party/blink/renderer/modules/native_io/native_io_file_manager.cc
index f5680dc..18eb76a 100644
--- a/third_party/blink/renderer/modules/native_io/native_io_file_manager.cc
+++ b/third_party/blink/renderer/modules/native_io/native_io_file_manager.cc
@@ -4,10 +4,10 @@
 
 #include "third_party/blink/renderer/modules/native_io/native_io_file_manager.h"
 
-#include <algorithm>
 #include <utility>
 
 #include "base/files/file.h"
+#include "base/ranges/algorithm.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
@@ -53,11 +53,9 @@
     return false;
 
   if (name.Is8Bit()) {
-    return std::all_of(name.Span8().begin(), name.Span8().end(),
-                       &IsValidNativeIONameCharacter);
+    return base::ranges::all_of(name.Span8(), &IsValidNativeIONameCharacter);
   }
-  return std::all_of(name.Span16().begin(), name.Span16().end(),
-                     &IsValidNativeIONameCharacter);
+  return base::ranges::all_of(name.Span16(), &IsValidNativeIONameCharacter);
 }
 
 void ThrowStorageAccessError(ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/modules/sensor/sensor.cc b/third_party/blink/renderer/modules/sensor/sensor.cc
index 002aa424..7985345 100644
--- a/third_party/blink/renderer/modules/sensor/sensor.cc
+++ b/third_party/blink/renderer/modules/sensor/sensor.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/ranges/algorithm.h"
 #include "services/device/public/cpp/generic_sensor/sensor_traits.h"
 #include "services/device/public/mojom/sensor.mojom-blink.h"
 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-blink.h"
@@ -28,11 +29,11 @@
 bool AreFeaturesEnabled(
     ExecutionContext* context,
     const Vector<mojom::blink::PermissionsPolicyFeature>& features) {
-  return std::all_of(features.begin(), features.end(),
-                     [context](mojom::blink::PermissionsPolicyFeature feature) {
-                       return context->IsFeatureEnabled(
-                           feature, ReportOptions::kReportOnFailure);
-                     });
+  return base::ranges::all_of(
+      features, [context](mojom::blink::PermissionsPolicyFeature feature) {
+        return context->IsFeatureEnabled(feature,
+                                         ReportOptions::kReportOnFailure);
+      });
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index cc93dfc..970c834 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -30,7 +30,6 @@
 
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 
-#include <algorithm>
 #include <memory>
 #include <utility>
 
@@ -40,6 +39,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/ranges/algorithm.h"
 #include "base/time/time.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/cpp/cross_origin_embedder_policy.h"
@@ -2360,8 +2360,8 @@
   // Count standardized payment method identifiers, such as "basic-card" or
   // "tokenized-card". Omit counting the URL-based payment method identifiers,
   // such as "https://bobpay.xyz".
-  if (std::any_of(
-          event_data->method_data.begin(), event_data->method_data.end(),
+  if (base::ranges::any_of(
+          event_data->method_data,
           [](const payments::mojom::blink::PaymentMethodDataPtr& datum) {
             return datum && !datum->supported_method.StartsWith("http");
           })) {
diff --git a/third_party/blink/renderer/modules/webtransport/outgoing_stream_test.cc b/third_party/blink/renderer/modules/webtransport/outgoing_stream_test.cc
index 28965e8..4aeaf0b9 100644
--- a/third_party/blink/renderer/modules/webtransport/outgoing_stream_test.cc
+++ b/third_party/blink/renderer/modules/webtransport/outgoing_stream_test.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/ranges/algorithm.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/task_type.h"
@@ -168,7 +169,7 @@
 }
 
 bool IsAllNulls(base::span<const uint8_t> data) {
-  return std::all_of(data.begin(), data.end(), [](uint8_t c) { return !c; });
+  return base::ranges::all_of(data, [](uint8_t c) { return !c; });
 }
 
 TEST(OutgoingStreamTest, AsyncWrite) {
diff --git a/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc b/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc
index a8e3b24..997639a 100644
--- a/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
 
+#include <algorithm>
+
 #include "base/logging.h"
 #include "cc/layers/layer.h"
 #include "third_party/blink/renderer/platform/geometry/geometry_as_json.h"
diff --git a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
index cf32bbf..b34170a 100644
--- a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
+++ b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
@@ -148,8 +148,8 @@
   unsigned query_id = 0;
   ri->GenQueriesEXT(1, &query_id);
 
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-  // On Windows and Mac, GMB data read will do synchronization on its own.
+#if BUILDFLAG(IS_WIN)
+  // On Windows, GMB data read will do synchronization on its own.
   // No need for GL_COMMANDS_COMPLETED_CHROMIUM QueryEXT.
   GLenum queryTarget = GL_COMMANDS_ISSUED_CHROMIUM;
 #else
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index c708a2e..9214df01 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+
 #include <memory>
 
 #include "base/bind.h"
@@ -11,6 +12,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -494,23 +496,21 @@
 }
 
 bool PageSchedulerImpl::IsWaitingForMainFrameContentfulPaint() const {
-  return std::any_of(frame_schedulers_.begin(), frame_schedulers_.end(),
-                     [](const FrameSchedulerImpl* fs) {
-                       return fs->IsWaitingForContentfulPaint() &&
-                              !fs->IsInEmbeddedFrameTree() &&
-                              fs->GetFrameType() ==
-                                  FrameScheduler::FrameType::kMainFrame;
-                     });
+  return base::ranges::any_of(
+      frame_schedulers_, [](const FrameSchedulerImpl* fs) {
+        return fs->IsWaitingForContentfulPaint() &&
+               !fs->IsInEmbeddedFrameTree() &&
+               fs->GetFrameType() == FrameScheduler::FrameType::kMainFrame;
+      });
 }
 
 bool PageSchedulerImpl::IsWaitingForMainFrameMeaningfulPaint() const {
-  return std::any_of(frame_schedulers_.begin(), frame_schedulers_.end(),
-                     [](const FrameSchedulerImpl* fs) {
-                       return fs->IsWaitingForMeaningfulPaint() &&
-                              !fs->IsInEmbeddedFrameTree() &&
-                              fs->GetFrameType() ==
-                                  FrameScheduler::FrameType::kMainFrame;
-                     });
+  return base::ranges::any_of(
+      frame_schedulers_, [](const FrameSchedulerImpl* fs) {
+        return fs->IsWaitingForMeaningfulPaint() &&
+               !fs->IsInEmbeddedFrameTree() &&
+               fs->GetFrameType() == FrameScheduler::FrameType::kMainFrame;
+      });
 }
 
 void PageSchedulerImpl::WriteIntoTrace(perfetto::TracedValue context,
diff --git a/third_party/blink/renderer/platform/wtf/hash_table.h b/third_party/blink/renderer/platform/wtf/hash_table.h
index 0ed43a68..6817ebee 100644
--- a/third_party/blink/renderer/platform/wtf/hash_table.h
+++ b/third_party/blink/renderer/platform/wtf/hash_table.h
@@ -2249,12 +2249,20 @@
     ++impl_;
     return *this;
   }
-  // postfix ++ intentionally omitted
+  HashTableConstIteratorAdapter operator++(int) {
+    HashTableConstIteratorAdapter copy = *this;
+    ++*this;
+    return copy;
+  }
   HashTableConstIteratorAdapter& operator--() {
     --impl_;
     return *this;
   }
-  // postfix -- intentionally omitted
+  HashTableConstIteratorAdapter operator--(int) {
+    HashTableConstIteratorAdapter copy = *this;
+    --*this;
+    return copy;
+  }
   typename HashTableType::const_iterator impl_;
 };
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 941c81d..5ef01cd 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -82,10 +82,6 @@
 # Tested by paint/background/root-element-background-transparency.html for now.
 external/wpt/css/compositing/root-element-background-transparency.html [ Failure ]
 
-# Deferred Shaping
-# The following test fails due to scrollable area size difference.
-external/wpt/css/CSS2/backgrounds/background-attachment-004.xht [ Failure ]
-
 # ====== Synchronous, budgeted HTML parser tests from here ========================
 # See crbug.com/1254921 for details.
 # These tests either started failing outright, or became flaky, when the new HTML
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 453c8a4..a2d9322 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
@@ -267677,15 +267677,15 @@
      ],
      "parsing": {
       "color-computed-expected.txt": [
-       "93d1203d1b8280b9be45be04b2242771a60bdf05",
+       "8a49339eb9d9dd50bfaceef1475858ad01287ad1",
        []
       ],
       "color-contrast-computed-expected.txt": [
-       "242a2c6625f189802bdf5bf1d258a580eec7ea0b",
+       "b419ec4f531d9f27d980cb4f576e473cccd62d6c",
        []
       ],
       "color-contrast-valid-expected.txt": [
-       "74fffce2be1a9c0692ec38ce4fc8d244f15182c2",
+       "95de7e5b3209f5d6a1973ad64a4ad018a5188455",
        []
       ],
       "color-mix-computed-expected.txt": [
@@ -267701,7 +267701,7 @@
        []
       ],
       "color-valid-expected.txt": [
-       "c49de7e65720a9f9a0f87dd16c71a67a28ab41ed",
+       "6a5bf04d55f7ba2b4355756b6d0af07ad9d76f54",
        []
       ],
       "relative-color-computed-expected.txt": [
@@ -346115,7 +346115,7 @@
      []
     ],
     "video-encoder-utils.js": [
-     "58f0ce83f3748b4efe7825c4632eab7ac1f0277a",
+     "892393394ec49a3ff0eca0d3ca1c1971f42d5aba",
      []
     ],
     "videoFrame-construction.crossOriginIsolated.https.any.js.headers": [
@@ -567367,7 +567367,7 @@
      ]
     ],
     "video-encoder.https.any.js": [
-     "791b910ee40f4213ce868fbe277af3b46ca06b3c",
+     "063be1ef0fd40bd3674715ebaa67f6ea2fed3f26",
      [
       "webcodecs/video-encoder.https.any.html",
       {
@@ -567457,7 +567457,7 @@
      ]
     ],
     "videoDecoder-codec-specific.https.any.js": [
-     "fab0b24220dc9be9f34186c851dc20fd21ee1c57",
+     "d74fff886a954d1f1e6ec624eca05f8e2e4ab921",
      [
       "webcodecs/videoDecoder-codec-specific.https.any.html?av1",
       {
@@ -607377,7 +607377,7 @@
      },
      "get_computed_label": {
       "get.py": [
-       "d10ccf7f24b36eb784bf09e4e10f3a8d094102c3",
+       "12aa82eb4e460daf2248bca2a0072e7cdde43574",
        [
         null,
         {}
@@ -607386,7 +607386,7 @@
      },
      "get_computed_role": {
       "get.py": [
-       "bc4be6efd658fce26771aca8f047d91c75e766be",
+       "d242f7383223b38d5059f683951ee626a9352a72",
        [
         null,
         {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-transition-mutation.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-transition-mutation.html
new file mode 100644
index 0000000..1542afa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-transition-mutation.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Unrelated mutation does not affect object-view-box transition</title>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=element>
+</div>
+<style>
+  #element {
+    object-view-box: inset(0px);
+    transition: object-view-box 100s -50s linear;
+  }
+</style>
+<script>
+  test((t) => {
+    // Ensure a before-style for element.
+    getComputedStyle(element).objectViewBox;
+    // Trigger a transition from inset(0px) to inset(20px).
+    element.style.objectViewBox = 'inset(20px)';
+    assert_equals(getComputedStyle(element).objectViewBox, 'inset(10px)', 'before mutation');
+    // Now do a style mutation that's unrelated to the computed value
+    // of object-view-box, and check again.
+    element.style.setProperty('--x', '1');
+    assert_equals(getComputedStyle(element).objectViewBox, 'inset(10px)', 'after mutation');
+  }, 'Unrelated mutation does not affect object-view-box transition');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_label/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_label/get.py
index d10ccf7f..12aa82e 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_label/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_label/get.py
@@ -25,11 +25,6 @@
     assert_error(response, "stale element reference")
 
 
-def test_no_user_prompt(session):
-    response = get_computed_label(session, "foo")
-    assert_error(response, "no such alert")
-
-
 @pytest.mark.parametrize("html,tag,label", [
     ("<button>ok</button>", "button", "ok"),
     ("<button aria-labelledby=\"one two\"></button><div id=one>ok</div><div id=two>go</div>", "button", "ok go"),
@@ -37,7 +32,7 @@
     ("<label><input> foo</label>", "input", "foo"),
     ("<label for=b>foo<label><input id=b>", "input", "foo")])
 def test_get_computed_label(session, inline, html, tag, label):
-    session.url = inline("{0}".format(tag))
+    session.url = inline(html)
     element = session.find.css(tag, all=False)
     result = get_computed_label(session, element.id)
     assert_success(result, label)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_role/get.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_role/get.py
index bc4be6e..d242f738 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_role/get.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/get_computed_role/get.py
@@ -25,13 +25,8 @@
     assert_error(response, "stale element reference")
 
 
-def test_no_user_prompt(session):
-    response = get_computed_role(session, "foo")
-    assert_error(response, "no such alert")
-
-
 @pytest.mark.parametrize("html,tag,expected", [
-    ("<li role=menuitem>foo", "li", "menu"),
+    ("<li role=menuitem>foo", "li", "menuitem"),
     ("<input role=searchbox>", "input", "searchbox"),
     ("<img role=presentation>", "img", "presentation")])
 def test_computed_roles(session, inline, html, tag, expected):
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png b/third_party/blink/web_tests/platform/linux/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
index cc425f7..f94dc3a4 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
deleted file mode 100644
index 1c9bc73..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.14/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png b/third_party/blink/web_tests/platform/mac/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
index d930e7b6..1c9bc73 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png b/third_party/blink/web_tests/platform/win/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
index b1de178a..c3269103 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
+++ b/third_party/blink/web_tests/platform/win/external/wpt/css/CSS2/backgrounds/background-attachment-004-expected.png
Binary files differ
diff --git a/third_party/closure_compiler/externs/autofill_private.js b/third_party/closure_compiler/externs/autofill_private.js
index 7c7ff06..483c261 100644
--- a/third_party/closure_compiler/externs/autofill_private.js
+++ b/third_party/closure_compiler/externs/autofill_private.js
@@ -106,7 +106,6 @@
  *   expirationYear: (string|undefined),
  *   nickname: (string|undefined),
  *   network: (string|undefined),
- *   imageSrc: (string|undefined),
  *   metadata: (!chrome.autofillPrivate.AutofillMetadata|undefined)
  * }}
  */
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index 431cadd..348f081 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -81,7 +81,6 @@
   MEDIA_STOPPED_PLAYING: 'mediaStoppedPlaying',
   MENU_END: 'menuEnd',
   MENU_ITEM_SELECTED: 'menuItemSelected',
-  MENU_LIST_ITEM_SELECTED: 'menuListItemSelected',
   MENU_LIST_VALUE_CHANGED: 'menuListValueChanged',
   MENU_POPUP_END: 'menuPopupEnd',
   MENU_POPUP_START: 'menuPopupStart',
diff --git a/third_party/ipcz/src/ipcz/monitored_atomic.h b/third_party/ipcz/src/ipcz/monitored_atomic.h
index b271e0b..11a268fa 100644
--- a/third_party/ipcz/src/ipcz/monitored_atomic.h
+++ b/third_party/ipcz/src/ipcz/monitored_atomic.h
@@ -6,6 +6,7 @@
 #define IPCZ_SRC_IPCZ_MONITORED_VALUE_H_
 
 #include <atomic>
+#include <limits>
 #include <type_traits>
 
 namespace ipcz {
diff --git a/third_party/wayland/features.gni b/third_party/wayland/features.gni
index da7496ce..0cdca8c 100644
--- a/third_party/wayland/features.gni
+++ b/third_party/wayland/features.gni
@@ -9,7 +9,7 @@
 declare_args() {
   # This variable is deprecated.  use_system_libwayland_client should be used
   # instead.
-  use_system_libwayland = is_linux && !is_castos &&
+  use_system_libwayland = is_linux && !is_castos && !is_msan &&
                           default_toolchain != "//build/toolchain/cros:target"
 
   # Controls whether the build should use the version of Wayland
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index 54038d4..336ff2a 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -386,7 +386,7 @@
 
 
 def DownloadPinnedClang():
-  PINNED_CLANG_VERSION = 'llvmorg-16-init-572-gdde41c6c-3'
+  PINNED_CLANG_VERSION = 'llvmorg-16-init-3375-gfed71b04-1'
   DownloadAndUnpackPackage('clang', PINNED_CLANG_DIR, GetDefaultHostOs(),
                            PINNED_CLANG_VERSION)
 
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 5d87051..f5bb5e9 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -322,7 +322,7 @@
     "includes": [2100],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/webui_gallery/resources.grd": {
-    "META": {"sizes": {"includes": [20]}},
+    "META": {"sizes": {"includes": [30]}},
     "includes": [2130],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/whats_new/resources.grd": {
@@ -435,6 +435,10 @@
   "components/resources/dev_ui_components_resources.grd": {
     "includes": [2560],
   },
+  "<(SHARED_INTERMEDIATE_DIR)/content/browser/resources/indexed_db/resources.grd": {
+    "META": {"sizes": {"includes": [10]}},
+    "includes": [2570],
+  },
   "<(SHARED_INTERMEDIATE_DIR)/content/browser/resources/media/resources.grd": {
     "META": {"sizes": {"includes": [10],}},
     "includes": [2580],
@@ -625,14 +629,28 @@
     "META": {"sizes": {"includes": [50],}},
    "includes": [3360],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/projector_app/resources/ash_projector_app_untrusted_resources.grd": {
+
+  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/projector_app/resources/app/trusted/ash_projector_app_trusted_resources.grd": {
     "META": {"sizes": {"includes": [50],}},
     "includes": [3380],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/projector_app/resources/ash_projector_app_trusted_resources.grd": {
+ "<(SHARED_INTERMEDIATE_DIR)/ash/webui/projector_app/resources/app/untrusted/ash_projector_app_untrusted_resources.grd": {
     "META": {"sizes": {"includes": [50],}},
-    "includes": [3400],
+    "includes": [3395],
   },
+  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/projector_app/resources/annotator/untrusted/ash_projector_annotator_untrusted_resources.grd": {
+    "META": {"sizes": {"includes": [50],}},
+    "includes": [3405],
+  },
+  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/projector_app/resources/annotator/trusted/ash_projector_annotator_trusted_resources.grd": {
+    "META": {"sizes": {"includes": [50],}},
+    "includes": [3410],
+  },
+  "<(SHARED_INTERMEDIATE_DIR)/ash/webui/projector_app/resources/common/ash_projector_common_resources.grd": {
+    "META": {"sizes": {"includes": [50],}},
+    "includes": [3415],
+  },
+
   # Both projector_app_bundle_resources.grd and projector_app_bundle_mock_resources.grd
   # start with the same id because only one of them is built depending on if
   # src_internal is available. Lower bound for number of resource ids is number
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py
index 3163fc1..624a0a6 100644
--- a/tools/json_schema_compiler/cc_generator.py
+++ b/tools/json_schema_compiler/cc_generator.py
@@ -956,23 +956,22 @@
                 type_.name,
                 self._util_cc_helper.GetValueTypeString('%%(src_var)s')))))
       if is_ptr:
-        c.Append('%(dst_var)s.reset();')
+        if cpp_util.ShouldUseAbslOptional(underlying_type):
+          c.Append('%(dst_var)s = absl::nullopt;')
+        else:
+          c.Append('%(dst_var)s.reset();')
       c.Append('return %(failure_value)s;')
       (c.Eblock('}'))
       if is_ptr:
-        if is_string_or_function:
+        if cpp_util.ShouldUseAbslOptional(underlying_type):
+          c.Append('%(dst_var)s = *temp;')
+        elif is_string_or_function:
           c.Append('%(dst_var)s = std::make_unique<%(cpp_type)s>(*temp);')
-        elif cpp_util.ShouldUseAbslOptional(underlying_type):
-          c.Append('%(dst_var)s = ' +
-            'temp.value();')
         else:
           c.Append('%(dst_var)s = ' +
             'std::make_unique<%(cpp_type)s>(temp.value());')
       else:
-        if is_string_or_function:
-          c.Append('%(dst_var)s = *temp;')
-        else:
-          c.Append('%(dst_var)s = temp.value();')
+        c.Append('%(dst_var)s = *temp;')
     elif underlying_type.property_type == PropertyType.OBJECT:
       if is_ptr:
         (c.Sblock('if (!%(src_var)s.is_dict()) {')
diff --git a/tools/json_schema_compiler/cpp_util.py b/tools/json_schema_compiler/cpp_util.py
index d88edf0..747723a4 100644
--- a/tools/json_schema_compiler/cpp_util.py
+++ b/tools/json_schema_compiler/cpp_util.py
@@ -112,7 +112,8 @@
 
   if type_.property_type in (PropertyType.BOOLEAN,
                              PropertyType.DOUBLE,
-                             PropertyType.INTEGER):
+                             PropertyType.INTEGER,
+                             PropertyType.STRING):
     return True
 
   return False
diff --git a/tools/json_schema_compiler/test/choices_unittest.cc b/tools/json_schema_compiler/test/choices_unittest.cc
index 96f63403..6a1a902 100644
--- a/tools/json_schema_compiler/test/choices_unittest.cc
+++ b/tools/json_schema_compiler/test/choices_unittest.cc
@@ -131,7 +131,7 @@
   EXPECT_FALSE(out.integers.as_integers.get());
   EXPECT_EQ(4, *out.integers.as_integer);
 
-  EXPECT_FALSE(out.strings->as_string.get());
+  EXPECT_FALSE(out.strings->as_string);
   ASSERT_TRUE(out.strings->as_strings.get());
   EXPECT_EQ(strings, *out.strings->as_strings);
 }
diff --git a/tools/json_schema_compiler/test/error_generation_unittest.cc b/tools/json_schema_compiler/test/error_generation_unittest.cc
index 5cc6732..a8cdb99 100644
--- a/tools/json_schema_compiler/test/error_generation_unittest.cc
+++ b/tools/json_schema_compiler/test/error_generation_unittest.cc
@@ -246,7 +246,7 @@
     EXPECT_FALSE(errors::OptionalTestType::Populate(*value, &out, &error));
     EXPECT_TRUE(EqualsUtf16("'string': expected string, got integer",
         error));
-    EXPECT_EQ(NULL, out.string.get());
+    EXPECT_FALSE(out.string);
   }
 }
 
diff --git a/tools/json_schema_compiler/test/idl_schemas_unittest.cc b/tools/json_schema_compiler/test/idl_schemas_unittest.cc
index 839ac03..3c91732 100644
--- a/tools/json_schema_compiler/test/idl_schemas_unittest.cc
+++ b/tools/json_schema_compiler/test/idl_schemas_unittest.cc
@@ -97,7 +97,7 @@
   std::unique_ptr<Function8::Params> f8_params =
       Function8::Params::Create(list);
   EXPECT_EQ(8, f8_params->arg1);
-  EXPECT_EQ(nullptr, f8_params->arg2.get());
+  EXPECT_FALSE(f8_params->arg2.has_value());
   list.Append("foo");
   f8_params = Function8::Params::Create(list);
   EXPECT_EQ(8, f8_params->arg1);
diff --git a/tools/json_schema_compiler/test/objects_unittest.cc b/tools/json_schema_compiler/test/objects_unittest.cc
index 7f64331..db65f12 100644
--- a/tools/json_schema_compiler/test/objects_unittest.cc
+++ b/tools/json_schema_compiler/test/objects_unittest.cc
@@ -101,7 +101,7 @@
   parent.pods = std::move(pods);
   parent.strs.push_back("pstr");
   parent.blob.additional_properties.SetString("key", "val");
-  parent.choice.as_string = std::make_unique<std::string>("string");
+  parent.choice.as_string = "string";
 
   objects_movable::MovableParent parent2(std::move(parent));
   ASSERT_EQ(2u, parent2.pods.size());
@@ -116,7 +116,7 @@
   ASSERT_EQ(1u, parent2.strs.size());
   EXPECT_EQ("pstr", parent2.strs[0]);
   EXPECT_FALSE(parent2.choice.as_movable_pod.get());
-  ASSERT_TRUE(parent2.choice.as_string.get());
+  ASSERT_TRUE(parent2.choice.as_string);
   EXPECT_EQ("string", *parent2.choice.as_string);
   std::string blob_string;
   EXPECT_TRUE(
@@ -137,7 +137,7 @@
   EXPECT_TRUE(parent2.pods.empty());
   EXPECT_TRUE(parent2.strs.empty());
   EXPECT_TRUE(parent2.blob.additional_properties.DictEmpty());
-  EXPECT_FALSE(parent2.choice.as_string.get());
+  EXPECT_FALSE(parent2.choice.as_string);
   ASSERT_TRUE(parent2.choice.as_movable_pod.get());
   EXPECT_EQ(objects_movable::FOO_BAZ, parent2.choice.as_movable_pod->foo);
   EXPECT_EQ("str", parent2.choice.as_movable_pod->str);
diff --git a/tools/json_schema_compiler/test/simple_api_unittest.cc b/tools/json_schema_compiler/test/simple_api_unittest.cc
index f9cf6e9..03a61a1 100644
--- a/tools/json_schema_compiler/test/simple_api_unittest.cc
+++ b/tools/json_schema_compiler/test/simple_api_unittest.cc
@@ -98,7 +98,7 @@
     std::unique_ptr<simple_api::OptionalString::Params> params(
         simple_api::OptionalString::Params::Create(params_value));
     EXPECT_TRUE(params.get());
-    EXPECT_FALSE(params->str.get());
+    EXPECT_FALSE(params->str);
   }
   {
     base::Value::List params_value;
@@ -106,7 +106,7 @@
     std::unique_ptr<simple_api::OptionalString::Params> params(
         simple_api::OptionalString::Params::Create(params_value));
     EXPECT_TRUE(params.get());
-    EXPECT_TRUE(params->str.get());
+    EXPECT_TRUE(params->str);
     EXPECT_EQ("asdf", *params->str);
   }
 }
@@ -118,7 +118,7 @@
     std::unique_ptr<simple_api::OptionalString::Params> params(
         simple_api::OptionalString::Params::Create(params_value));
     EXPECT_TRUE(params.get());
-    EXPECT_FALSE(params->str.get());
+    EXPECT_FALSE(params->str);
   }
 }
 
@@ -140,7 +140,7 @@
     std::unique_ptr<simple_api::OptionalBeforeRequired::Params> params(
         simple_api::OptionalBeforeRequired::Params::Create(params_value));
     EXPECT_TRUE(params.get());
-    EXPECT_FALSE(params->first.get());
+    EXPECT_FALSE(params->first);
     EXPECT_EQ("asdf", params->second);
   }
 }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 63103dd0..9e4488e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -56130,6 +56130,7 @@
       label="ExperimentalAccessibilityDictationHints:enabled"/>
   <int value="-2080657940" label="DirectActions:disabled"/>
   <int value="-2080504230" label="TabHoverCards:disabled"/>
+  <int value="-2080227937" label="ImeTrayHideVoiceButton:enabled"/>
   <int value="-2077268643" label="disable-device-enumeration"/>
   <int value="-2076313806" label="TrustTokens:disabled"/>
   <int value="-2075870708" label="MediaRemotingEncrypted:disabled"/>
@@ -59263,6 +59264,7 @@
       label="EnableEphemeralGuestProfilesOnDesktop:disabled"/>
   <int value="-176524053"
       label="VmCameraMicIndicatorsAndNotifications:disabled"/>
+  <int value="-176297603" label="ImeTrayHideVoiceButton:disabled"/>
   <int value="-176163507" label="SystemExtensions:enabled"/>
   <int value="-175696105" label="MessagesForAndroidUpdatePassword:enabled"/>
   <int value="-175692563" label="Vp9kSVCHWDecoding:disabled"/>
@@ -60199,6 +60201,7 @@
   <int value="387678734" label="ShortcutCustomizationApp:disabled"/>
   <int value="388118272" label="DynamicColorAndroid:disabled"/>
   <int value="388328387" label="WindowsMixedReality:enabled"/>
+  <int value="388632022" label="JavaScriptExperimentalSharedMemory:disabled"/>
   <int value="388786873" label="EnableUnifiedMultiDeviceSettings:enabled"/>
   <int value="388996324" label="CustomContextMenu:disabled"/>
   <int value="390360656" label="TrustSafetySentimentSurvey:enabled"/>
@@ -61931,6 +61934,7 @@
   <int value="1466380480" label="enable-device-discovery-notifications"/>
   <int value="1466502102" label="DelayNavigation:disabled"/>
   <int value="1467322206" label="AssistAutoCorrect:enabled"/>
+  <int value="1467494310" label="JavaScriptExperimentalSharedMemory:enabled"/>
   <int value="1467583815" label="AVIF:disabled"/>
   <int value="1469407485" label="disable-accelerated-2d-canvas"/>
   <int value="1471169381" label="kWebSQLAccess:enabled"/>
@@ -87580,17 +87584,17 @@
 </enum>
 
 <enum name="SendTabToSelfClickResult">
-  <int value="0" label="SendTabToSelf entry point is shown.">
+  <int value="0" label="(obsolete) SendTabToSelf entry point is shown.">
     <obsolete>
       Removed on 06/2020.
     </obsolete>
   </int>
   <int value="1" label="SendTabToSelf target device is clicked."/>
   <int value="2" label="SendTabToSelf device list is shown.">
-    <obsolete>
-      Removed on 06/2020.
-    </obsolete>
+    Removed on 06/2020 and brought back 09/2022.
   </int>
+  <int value="3" label="No target device message shown."/>
+  <int value="4" label="Sign-in promo shown."/>
 </enum>
 
 <enum name="SendTabToSelfNotification">
@@ -87613,7 +87617,11 @@
   <int value="1" label="Dismissed"/>
   <int value="2" label="Opened"/>
   <int value="3" label="TimedOut"/>
-  <int value="4" label="Sent"/>
+  <int value="4" label="(obsolete) Sent">
+    <obsolete>
+      Removed 09/2022.
+    </obsolete>
+  </int>
   <int value="5" label="DismissReasonUnknown"/>
 </enum>
 
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 257c5bf..4827945b 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -3957,7 +3957,7 @@
 </histogram>
 
 <histogram name="Media.PipelineStatus.Start" enum="PipelineStatus"
-    expires_after="2022-10-09">
+    expires_after="2023-10-09">
   <owner>xhwang@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index b1f37055..d5127d4 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -1149,6 +1149,28 @@
   </summary>
 </histogram>
 
+<histogram name="Net.DNS.ProcTask.FailureTime" units="ms"
+    expires_after="2023-08-28">
+  <owner>horo@chromium.org</owner>
+  <owner>src/net/OWNERS</owner>
+  <summary>
+    Duration of time taken by ProcTask (the grouping of all individual DNS
+    queries for a single instance of resolving a name using the system resolver)
+    in resolutions that failed.
+  </summary>
+</histogram>
+
+<histogram name="Net.DNS.ProcTask.SuccessTime" units="ms"
+    expires_after="2023-08-28">
+  <owner>horo@chromium.org</owner>
+  <owner>src/net/OWNERS</owner>
+  <summary>
+    Duration of time taken by ProcTask (the grouping of all individual DNS
+    queries for a single instance of resolving a name using the system resolver)
+    in resolutions that succeeded.
+  </summary>
+</histogram>
+
 <histogram name="Net.DNS.Request.TotalTime" units="ms" expires_after="never">
 <!-- expires-never: Core network stack health metric -->
 
diff --git a/tools/metrics/histograms/metadata/obsolete_histograms.xml b/tools/metrics/histograms/metadata/obsolete_histograms.xml
index 42e71c9e..6773885 100644
--- a/tools/metrics/histograms/metadata/obsolete_histograms.xml
+++ b/tools/metrics/histograms/metadata/obsolete_histograms.xml
@@ -39044,30 +39044,6 @@
   </summary>
 </histogram>
 
-<histogram name="Net.DNS.ProcTask.FailureTime" units="ms"
-    expires_after="2018-11-08">
-  <obsolete>
-    Removed 11/2018.
-  </obsolete>
-  <owner>pauljensen@chromium.org</owner>
-  <owner>mef@chromium.org</owner>
-  <summary>
-    Duration of time taken by ProcTask in resolutions that failed.
-  </summary>
-</histogram>
-
-<histogram name="Net.DNS.ProcTask.SuccessTime" units="ms"
-    expires_after="2018-11-08">
-  <obsolete>
-    Removed 11/2018.
-  </obsolete>
-  <owner>pauljensen@chromium.org</owner>
-  <owner>mef@chromium.org</owner>
-  <summary>
-    Duration of time taken by ProcTask in resolutions that succeeded.
-  </summary>
-</histogram>
-
 <histogram name="Net.DNS.RecordParser.DomainNameLength" units="octets"
     expires_after="M80">
   <obsolete>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 76391b60..b23f2eb 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -11944,11 +11944,9 @@
   <summary>
     Records user flow for SendTabToSelf feature.
 
-    Only the &quot;SendTabToSelf target device is clicked&quot; bucket is
-    recorded today, causing sharing of a tab with the tapped device. Until M84
-    other buckets were recorded. Because each bucket represents a different
-    aspect of the flow, it's important to look at &quot;bucket count&quot;, not
-    &quot;bucket proportion&quot;.
+    Because each bucket represents a different aspect of the flow, it's
+    important to look at &quot;bucket count&quot;, not &quot;bucket
+    proportion&quot;.
 
     On iOS this was not recorded between M85 and M90, inclusive (see
     crbug.com/1204944).
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index 6f34c63..635a3c9 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -218,8 +218,8 @@
   </token>
 </histogram>
 
-<histogram name="SBClientDownload.DownloadRequestDurationMedium" units="ms"
-    expires_after="2022-12-25">
+<histogram name="SBClientDownload.DownloadRequestDurationMedium.{Analysis}"
+    units="ms" expires_after="2022-12-25">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -234,6 +234,13 @@
     SBClientDownload.DownloadRequestDuration. It has a maximum of 3 minutes to
     help investigate long-tail downloads.
   </summary>
+  <token key="Analysis">
+    <variant name="Dmg"/>
+    <variant name="Document" summary="Office document"/>
+    <variant name="None" summary="executable"/>
+    <variant name="Rar"/>
+    <variant name="Zip"/>
+  </token>
 </histogram>
 
 <histogram name="SBClientDownload.DownloadRequestNetError" enum="NetErrorCodes"
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 7763d03..8e1c047 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "f499d491081fb689a75d89cd3183e30281669be2",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/13c35692e86358d64b2aa0f89195480ddacd8b37/trace_processor_shell.exe"
+            "hash": "0fea885f374262b32d7a93cb93bfc0d34e8b25c5",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/3a01865035f177527f59b045ff52a485e2dc4284/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "01f45911243f77f13533d8b5e4b31e2f06f30af9",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/e5f4582104f08abdd38246c6085684dc1c4e8112/trace_processor_shell"
+            "hash": "faeb612cb5e0f46fcdc25520e1a5b155045bc31b",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/3a01865035f177527f59b045ff52a485e2dc4284/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
             "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "ec688db739a3781e079d0851bfc4e50df3c3eed4",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/13c35692e86358d64b2aa0f89195480ddacd8b37/trace_processor_shell"
+            "hash": "26539bba65a233763f94f473c78b79126c3c84a7",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/3a01865035f177527f59b045ff52a485e2dc4284/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/polymer/html_to_wrapper.gni b/tools/polymer/html_to_wrapper.gni
index d15e644..58ded3fb 100644
--- a/tools/polymer/html_to_wrapper.gni
+++ b/tools/polymer/html_to_wrapper.gni
@@ -57,6 +57,14 @@
       ]
     }
 
+    if (defined(invoker.scheme)) {
+      assert(invoker.scheme == "chrome" || invoker.scheme == "relative")
+      args += [
+        "--scheme",
+        invoker.scheme,
+      ]
+    }
+
     if (defined(invoker.use_js) && invoker.use_js) {
       args += [ "--use_js" ]
     }
diff --git a/tools/polymer/html_to_wrapper.py b/tools/polymer/html_to_wrapper.py
index 16366f4..4131ee92 100644
--- a/tools/polymer/html_to_wrapper.py
+++ b/tools/polymer/html_to_wrapper.py
@@ -33,21 +33,21 @@
 import node
 
 # Template for non-Polymer elements.
-_NON_POLYMER_ELEMENT_TEMPLATE = """import {getTrustedHTML} from \'chrome://resources/js/static_types.js\';
+_NON_POLYMER_ELEMENT_TEMPLATE = """import {getTrustedHTML} from '%(scheme)s//resources/js/static_types.js';
 export function getTemplate() {
-  return getTrustedHTML`<!--_html_template_start_-->%s<!--_html_template_end_-->`;
+  return getTrustedHTML`<!--_html_template_start_-->%(content)s<!--_html_template_end_-->`;
 }"""
 
 # Template for Polymer elements.
-_ELEMENT_TEMPLATE = """import {html} from \'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js\';
+_ELEMENT_TEMPLATE = """import {html} from '%(scheme)s//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 export function getTemplate() {
-  return html`<!--_html_template_start_-->%s<!--_html_template_end_-->`;
+  return html`<!--_html_template_start_-->%(content)s<!--_html_template_end_-->`;
 }"""
 
-_ICONS_TEMPLATE = """import 'chrome://resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
-import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+_ICONS_TEMPLATE = """import '%(scheme)s//resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
+import {html} from '%(scheme)s//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-const template = html`%s`;
+const template = html`%(content)s`;
 document.head.appendChild(template.content);
 """
 
@@ -62,6 +62,9 @@
   parser.add_argument('--template',
                       choices=['polymer', 'native'],
                       default='polymer')
+  parser.add_argument('--scheme',
+                      choices=['chrome', 'relative'],
+                      default='chrome')
 
   args = parser.parse_args(argv)
 
@@ -113,7 +116,10 @@
       if filename == 'icons.html' or filename.endswith('_icons.html'):
         template = _ICONS_TEMPLATE
 
-      wrapper = template % html_content
+      wrapper = template % {
+          'content': html_content,
+          'scheme': 'chrome:' if args.scheme == 'chrome' else '',
+      }
 
       out_folder_for_file = path.join(out_folder, path.dirname(in_file))
       makedirs(out_folder_for_file, exist_ok=True)
diff --git a/tools/polymer/html_to_wrapper_test.py b/tools/polymer/html_to_wrapper_test.py
index c7afb21..f438a18d 100755
--- a/tools/polymer/html_to_wrapper_test.py
+++ b/tools/polymer/html_to_wrapper_test.py
@@ -23,7 +23,7 @@
 
   def _read_out_file(self, file_name):
     assert self._out_folder
-    with open(os.path.join(self._out_folder, file_name), 'rb') as f:
+    with open(os.path.join(self._out_folder, file_name), 'r') as f:
       return f.read()
 
   def _run_test(self,
@@ -32,7 +32,8 @@
                 wrapper_file_expected,
                 template=None,
                 minify=False,
-                use_js=False):
+                use_js=False,
+                scheme=None):
     assert not self._out_folder
     self._out_folder = tempfile.mkdtemp(dir=_HERE_DIR)
     args = [
@@ -50,11 +51,14 @@
     if use_js:
       args.append('--use_js')
 
+    if scheme:
+      args += ['--scheme', scheme]
+
     html_to_wrapper.main(args)
 
     actual_wrapper = self._read_out_file(wrapper_file)
     with open(os.path.join(_HERE_DIR, 'tests', wrapper_file_expected),
-              'rb') as f:
+              'r') as f:
       expected_wrapper = f.read()
     self.assertMultiLineEqual(str(expected_wrapper), str(actual_wrapper))
 
@@ -85,6 +89,18 @@
                    'html_to_wrapper/foo_expected.html.ts',
                    use_js=True)
 
+  def testHtmlToWrapperSchemeRelative(self):
+    self._run_test('html_to_wrapper/foo.html',
+                   'html_to_wrapper/foo.html.ts',
+                   'html_to_wrapper/foo_relative_expected.html.ts',
+                   scheme='relative')
+
+  def testHtmlToWrapperSchemeChrome(self):
+    self._run_test('html_to_wrapper/foo.html',
+                   'html_to_wrapper/foo.html.ts',
+                   'html_to_wrapper/foo_expected.html.ts',
+                   scheme='chrome')
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/polymer/tests/html_to_wrapper/foo_relative_expected.html.ts b/tools/polymer/tests/html_to_wrapper/foo_relative_expected.html.ts
new file mode 100644
index 0000000..1ef0b76
--- /dev/null
+++ b/tools/polymer/tests/html_to_wrapper/foo_relative_expected.html.ts
@@ -0,0 +1,10 @@
+import {html} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+export function getTemplate() {
+  return html`<!--_html_template_start_--><style>
+  div {
+    font-size: 2rem;
+  }
+</style>
+<div>Hello world</div>
+<!--_html_template_end_-->`;
+}
\ No newline at end of file
diff --git a/tools/typescript/definitions/autofill_private.d.ts b/tools/typescript/definitions/autofill_private.d.ts
index 9f9ce64..da6bf63 100644
--- a/tools/typescript/definitions/autofill_private.d.ts
+++ b/tools/typescript/definitions/autofill_private.d.ts
@@ -81,7 +81,6 @@
         expirationYear?: string;
         nickname?: string;
         network?: string;
-        imageSrc?: string;
         metadata?: AutofillMetadata;
       }
 
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index 4df9187d..03c49d47 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -272,6 +272,7 @@
     "ax_tree_combiner_unittest.cc",
     "ax_tree_id_unittest.cc",
     "ax_tree_manager_base_unittest.cc",
+    "ax_tree_manager_unittest.cc",
     "ax_tree_serializer_unittest.cc",
     "ax_tree_source_checker_unittest.cc",
     "ax_tree_unittest.cc",
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index a5211c5..c8039018 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -144,7 +144,7 @@
 }
 
 const base::Feature kAccessibilityOSSettingsVisibility{
-    "AccessibilityOSSettingsVisibility", base::FEATURE_DISABLED_BY_DEFAULT};
+    "AccessibilityOSSettingsVisibility", base::FEATURE_ENABLED_BY_DEFAULT};
 
 bool IsAccessibilityOSSettingsVisibilityEnabled() {
   return base::FeatureList::IsEnabled(
diff --git a/ui/accessibility/ax_tree_manager.cc b/ui/accessibility/ax_tree_manager.cc
index fa696df3..87064a4 100644
--- a/ui/accessibility/ax_tree_manager.cc
+++ b/ui/accessibility/ax_tree_manager.cc
@@ -69,6 +69,13 @@
     tree_observation_.Observe(ax_tree());
 }
 
+void AXTreeManager::Initialize(const ui::AXTreeUpdate& initial_tree) {
+  if (!ax_tree()->Unserialize(initial_tree)) {
+    LOG(FATAL) << "No recovery is possible if the initial tree is broken: "
+               << ax_tree()->error();
+  }
+}
+
 AXNode* AXTreeManager::GetNode(const AXNodeID node_id) const {
   return ax_tree_ ? ax_tree_->GetFromId(node_id) : nullptr;
 }
diff --git a/ui/accessibility/ax_tree_manager.h b/ui/accessibility/ax_tree_manager.h
index ea80329e..684ac5c8 100644
--- a/ui/accessibility/ax_tree_manager.h
+++ b/ui/accessibility/ax_tree_manager.h
@@ -57,6 +57,8 @@
   // tree.
   virtual AXNode* GetParentNodeFromParentTreeAsAXNode() const = 0;
 
+  void Initialize(const AXTreeUpdate& initial_tree);
+
   // Called when the tree manager is about to be removed from the tree map,
   // `AXTreeManagerMap`.
   void WillBeRemovedFromMap();
diff --git a/ui/accessibility/ax_tree_manager_unittest.cc b/ui/accessibility/ax_tree_manager_unittest.cc
new file mode 100644
index 0000000..4beb4bd
--- /dev/null
+++ b/ui/accessibility/ax_tree_manager_unittest.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 "ui/accessibility/ax_tree_manager.h"
+#include "ui/accessibility/ax_serializable_tree.h"
+#include "ui/accessibility/ax_tree_update.h"
+#include "ui/accessibility/test_ax_tree_manager.h"
+#include "ui/accessibility/ax_node.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+TEST(AXTreeManagerTest, ConstructFromInitialState) {
+  AXNodeData root;
+  root.id = 1;
+  root.role = ax::mojom::Role::kRootWebArea;
+  std::string name = "Hello";
+  root.AddStringAttribute(ax::mojom::StringAttribute::kName, name);
+
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.push_back(root);
+  initial_state.has_tree_data = true;
+
+  TestAXTreeManager manager(std::make_unique<AXSerializableTree>());
+
+  manager.Initialize(initial_state);
+
+  AXNode* returned_root = manager.GetRoot();
+  ASSERT_EQ(
+      returned_root->GetStringAttribute(ax::mojom::StringAttribute::kName),
+      name);
+}
+
+}  // namespace ui
diff --git a/ui/accessibility/extensions/chromevoxclassic/cvox2/background/desktop_automation_handler.js b/ui/accessibility/extensions/chromevoxclassic/cvox2/background/desktop_automation_handler.js
index 0c0cd11..3f6f73d 100644
--- a/ui/accessibility/extensions/chromevoxclassic/cvox2/background/desktop_automation_handler.js
+++ b/ui/accessibility/extensions/chromevoxclassic/cvox2/background/desktop_automation_handler.js
@@ -51,7 +51,7 @@
   this.addListener_(e.HOVER, this.onHover);
   this.addListener_(e.LOAD_COMPLETE, this.onLoadComplete);
   this.addListener_(e.MENU_END, this.onMenuEnd);
-  this.addListener_(e.MENU_LIST_ITEM_SELECTED, this.onEventIfSelected);
+  this.addListener_(e.MENU_ITEM_SELECTED, this.onEventIfSelected);
   this.addListener_(e.MENU_START, this.onMenuStart);
   this.addListener_(e.SCROLL_POSITION_CHANGED, this.onScrollPositionChanged);
   this.addListener_(e.SELECTION, this.onSelection);
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 00959865..4818571 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -5209,6 +5209,16 @@
       break;
 
     case UIA_LocalizedControlTypePropertyId: {
+      // Always favor the explicitly set aria-roledescription value if there's
+      // one.
+      std::u16string role_description;
+      if (GetString16Attribute(ax::mojom::StringAttribute::kRoleDescription,
+                               &role_description)) {
+        result->vt = VT_BSTR;
+        result->bstrVal = SysAllocString(base::as_wcstr(role_description));
+        break;
+      }
+
       // UIA core handles Localized Control type for some built-in types and
       // also has a mapping for ARIA roles. To get these defaults, we need to
       // have returned VT_EMPTY.
diff --git a/ui/ozone/platform/drm/gpu/drm_framebuffer.cc b/ui/ozone/platform/drm/gpu/drm_framebuffer.cc
index 75a6f72..a08fa232 100644
--- a/ui/ozone/platform/drm/gpu/drm_framebuffer.cc
+++ b/ui/ozone/platform/drm/gpu/drm_framebuffer.cc
@@ -85,7 +85,7 @@
   return base::MakeRefCounted<DrmFramebuffer>(
       std::move(drm_device), framebuffer_id, drm_format, opaque_framebuffer_id,
       opaque_format, params.modifier, params.preferred_modifiers,
-      gfx::Size(params.width, params.height));
+      gfx::Size(params.width, params.height), params.is_original_buffer);
 }
 
 // static
@@ -93,7 +93,8 @@
     scoped_refptr<DrmDevice> drm,
     const GbmBuffer* buffer,
     const gfx::Size& framebuffer_size,
-    std::vector<uint64_t> preferred_modifiers) {
+    std::vector<uint64_t> preferred_modifiers,
+    bool is_original_buffer) {
   DCHECK(gfx::Rect(buffer->GetSize()).Contains(gfx::Rect(framebuffer_size)));
   AddFramebufferParams params;
   params.format = buffer->GetFormat();
@@ -101,6 +102,7 @@
   params.width = framebuffer_size.width();
   params.height = framebuffer_size.height();
   params.num_planes = buffer->GetNumPlanes();
+  params.is_original_buffer = is_original_buffer;
   params.preferred_modifiers = preferred_modifiers;
   for (size_t i = 0; i < params.num_planes; ++i) {
     params.handles[i] = buffer->GetPlaneHandle(i);
@@ -128,13 +130,15 @@
                                uint32_t opaque_framebuffer_pixel_format,
                                uint64_t format_modifier,
                                std::vector<uint64_t> modifiers,
-                               const gfx::Size& size)
+                               const gfx::Size& size,
+                               bool is_original_buffer)
     : drm_device_(std::move(drm_device)),
       framebuffer_id_(framebuffer_id),
       framebuffer_pixel_format_(framebuffer_pixel_format),
       opaque_framebuffer_id_(opaque_framebuffer_id),
       opaque_framebuffer_pixel_format_(opaque_framebuffer_pixel_format),
       format_modifier_(format_modifier),
+      is_original_buffer_(is_original_buffer),
       preferred_modifiers_(modifiers),
       size_(size),
       modeset_sequence_id_at_allocation_(drm_device_->modeset_sequence_id()) {}
diff --git a/ui/ozone/platform/drm/gpu/drm_framebuffer.h b/ui/ozone/platform/drm/gpu/drm_framebuffer.h
index 2057f23..bc00ae1f 100644
--- a/ui/ozone/platform/drm/gpu/drm_framebuffer.h
+++ b/ui/ozone/platform/drm/gpu/drm_framebuffer.h
@@ -30,6 +30,7 @@
     uint32_t format = DRM_FORMAT_XRGB8888;
     uint64_t modifier = DRM_FORMAT_MOD_INVALID;
     std::vector<uint64_t> preferred_modifiers;
+    bool is_original_buffer = true;
     uint32_t width = 0;
     uint32_t height = 0;
     size_t num_planes = 0;
@@ -46,7 +47,8 @@
       scoped_refptr<DrmDevice> drm_device,
       const GbmBuffer* buffer,
       const gfx::Size& framebuffer_size,
-      std::vector<uint64_t> preferred_modifiers = std::vector<uint64_t>());
+      std::vector<uint64_t> preferred_modifiers = std::vector<uint64_t>(),
+      bool is_original_buffer = true);
 
   DrmFramebuffer(scoped_refptr<DrmDevice> drm_device,
                  uint32_t framebuffer_id,
@@ -55,7 +57,8 @@
                  uint32_t opaque_framebuffer_pixel_format,
                  uint64_t format_modifier,
                  std::vector<uint64_t> preferred_modifiers,
-                 const gfx::Size& size);
+                 const gfx::Size& size,
+                 bool is_original_buffer);
 
   // ID allocated by the KMS API when the buffer is registered (via the handle).
   uint32_t framebuffer_id() const { return framebuffer_id_; }
@@ -73,6 +76,12 @@
     return framebuffer_pixel_format_;
   }
 
+  // Returns true if this was the original buffer and false if this is a buffer
+  // specifically created for DRM testing. The original buffer will have the
+  // original modifiers which makes the testing more accurate than a buffer that
+  // was created with default modifiers.
+  uint32_t is_original_buffer() const { return is_original_buffer_; }
+
   // Returns FourCC format that should be used to schedule this buffer for
   // scanout when used as an opaque buffer.
   uint32_t opaque_framebuffer_pixel_format() const {
@@ -109,6 +118,8 @@
   const uint32_t opaque_framebuffer_id_;
   const uint32_t opaque_framebuffer_pixel_format_;
   const uint64_t format_modifier_;
+
+  bool is_original_buffer_ = true;
   // List of modifiers passed at the creation of a bo with modifiers. If the bo
   // was created without modifiers, the vector is empty.
   const std::vector<uint64_t> preferred_modifiers_;
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
index d93c0b4..2b3ffec 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc
@@ -69,9 +69,11 @@
   if (!buffer)
     return nullptr;
 
+  constexpr bool kIsOriginalBuffer = false;
   scoped_refptr<DrmFramebuffer> drm_framebuffer =
       DrmFramebuffer::AddFramebuffer(drm_device, buffer.get(),
-                                     buffer->GetSize(), modifiers);
+                                     buffer->GetSize(), modifiers,
+                                     kIsOriginalBuffer);
   if (!drm_framebuffer)
     return nullptr;
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
index c812582..450d9294 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.cc
@@ -44,32 +44,20 @@
 // linear formats cannot. Atomic tests currently ignore modifiers, so there
 // isn't a way of determining if the rotation is supported.
 // TODO(https://b/172210707): Atomic tests should work if we are using
-// kUseRealBuffersForPageFlipTest, so this should be revisited and tested more
-// broadly with a condition on that.
-// NOTE: This is enabled for TGL+ and NV12/P010 formats for 90/270 rotation
-// currently since we always allocate those formats as Y-tiled and 90/270
-// rotation is supported by the HW in that case. This is needed for protected
-// content that requires overlays.
+// the original buffers as they have the correct modifiers. See
+// kUseRealBuffersForPageFlipTest and the 'GetBufferForPageFlipTest' function.
+// Intel driver reference on rotated and flipped buffers with modifiers:
+// https://code.woboq.org/linux/linux/drivers/gpu/drm/i915/intel_sprite.c.html#1471
 bool IsRotationTransformSupported(gfx::OverlayTransform transform,
-                                  uint32_t format_fourcc) {
-#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  const bool enable_more_rotations =
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kLacrosUseChromeosProtectedMedia);
-#else
-  const bool enable_more_rotations = true;
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-  if (enable_more_rotations &&
-      (format_fourcc == DRM_FORMAT_NV12 || format_fourcc == DRM_FORMAT_P010) &&
-      (transform == gfx::OVERLAY_TRANSFORM_ROTATE_90 ||
-       transform == gfx::OVERLAY_TRANSFORM_ROTATE_270)) {
-    return true;
-  }
-#endif
+                                  uint32_t format_fourcc,
+                                  bool is_original_buffer) {
   if ((transform == gfx::OVERLAY_TRANSFORM_ROTATE_90) ||
       (transform == gfx::OVERLAY_TRANSFORM_ROTATE_270) ||
       (transform == gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL)) {
+    if (is_original_buffer && (format_fourcc == DRM_FORMAT_NV12 ||
+                               format_fourcc == DRM_FORMAT_P010)) {
+      return true;
+    }
     return false;
   }
 
@@ -111,11 +99,13 @@
     const gfx::Rect& src_rect,
     const gfx::OverlayTransform transform,
     int in_fence_fd,
-    uint32_t format_fourcc) {
+    uint32_t format_fourcc,
+    bool is_original_buffer) {
   if (transform != gfx::OVERLAY_TRANSFORM_NONE && !properties_.rotation.id)
     return false;
 
-  if (!IsRotationTransformSupported(transform, format_fourcc))
+  if (!IsRotationTransformSupported(transform, format_fourcc,
+                                    is_original_buffer))
     return false;
 
   // Make a copy of properties to get the props IDs for the new intermediate
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
index dea207ce..fec8cd4 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h
@@ -37,7 +37,8 @@
                                 const gfx::Rect& src_rect,
                                 const gfx::OverlayTransform transform,
                                 int in_fence_fd,
-                                uint32_t format_fourcc);
+                                uint32_t format_fourcc,
+                                bool is_original_buffer);
   // Sets the props on |property_set| for commit.
   bool SetPlaneProps(drmModeAtomicReq* property_set);
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
index f1ccbc63..a838add 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
@@ -216,7 +216,7 @@
           static_cast<HardwareDisplayPlaneAtomic*>(plane);
       atomic_plane->AssignPlaneProps(
           0, 0, gfx::Rect(), gfx::Rect(), gfx::OVERLAY_TRANSFORM_NONE,
-          base::kInvalidPlatformFile, DRM_FORMAT_INVALID);
+          base::kInvalidPlatformFile, DRM_FORMAT_INVALID, false);
       atomic_plane->SetPlaneProps(atomic_request);
     }
   }
@@ -325,7 +325,7 @@
           static_cast<HardwareDisplayPlaneAtomic*>(plane);
       atomic_plane->AssignPlaneProps(
           0, 0, gfx::Rect(), gfx::Rect(), gfx::OVERLAY_TRANSFORM_NONE,
-          base::kInvalidPlatformFile, DRM_FORMAT_INVALID);
+          base::kInvalidPlatformFile, DRM_FORMAT_INVALID, false);
       atomic_plane->SetPlaneProps(plane_list->atomic_property_set.get());
     }
     ret = drm_->CommitProperties(plane_list->atomic_property_set.get(),
@@ -398,7 +398,8 @@
   if (!atomic_plane->AssignPlaneProps(
           crtc_id, framebuffer_id, overlay.display_bounds, src_rect,
           overlay.plane_transform, fence_fd,
-          overlay.buffer->framebuffer_pixel_format())) {
+          overlay.buffer->framebuffer_pixel_format(),
+          overlay.buffer->is_original_buffer())) {
     return false;
   }
   return true;
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
index 061a4a7..8c5b615 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
@@ -20,6 +20,7 @@
 #include "ui/gfx/linux/drm_util_linux.h"
 #include "ui/gfx/linux/gbm_buffer.h"
 #include "ui/gfx/linux/test/mock_gbm_device.h"
+#include "ui/gfx/overlay_transform.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
 #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
@@ -49,6 +50,7 @@
 constexpr uint32_t kTypePropId = 3010;
 constexpr uint32_t kInFormatsPropId = 3011;
 constexpr uint32_t kPlaneCtmId = 3012;
+constexpr uint32_t kRotationPropId = 3013;
 
 constexpr uint32_t kInFormatsBlobPropId = 400;
 
@@ -155,6 +157,7 @@
       // Defines some optional properties we use for convenience.
       {kTypePropId, "type"},
       {kInFormatsPropId, "IN_FORMATS"},
+      {kRotationPropId, "rotation"},
   };
 
   // Always add an additional cursor plane.
@@ -1414,6 +1417,58 @@
   EXPECT_TRUE(callback_called);
 }
 
+TEST_P(HardwareDisplayPlaneManagerAtomicTest, OriginalModifiersSupportOnly) {
+  fake_drm_->SetPropertyBlob(ui::MockDrmDevice::AllocateInFormatsBlob(
+      kInFormatsBlobPropId, {DRM_FORMAT_NV12}, {}));
+
+  InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1);
+  fake_drm_->InitializeState(crtc_properties_, connector_properties_,
+                             plane_properties_, property_names_, use_atomic_);
+
+  {
+    ui::DrmOverlayPlaneList assigns;
+    // Create as NV12 since this is required for rotation support.
+    std::unique_ptr<ui::GbmBuffer> buffer =
+        fake_drm_->gbm_device()->CreateBuffer(
+            DRM_FORMAT_NV12, kDefaultBufferSize, GBM_BO_USE_SCANOUT);
+    scoped_refptr<ui::DrmFramebuffer> framebuffer_original =
+        ui::DrmFramebuffer::AddFramebuffer(fake_drm_, buffer.get(),
+                                           kDefaultBufferSize, {}, true);
+    assigns.push_back(ui::DrmOverlayPlane(framebuffer_original, nullptr));
+    assigns.back().plane_transform = gfx::OVERLAY_TRANSFORM_ROTATE_270;
+
+    fake_drm_->plane_manager()->BeginFrame(&state_);
+    // Rotation should be supported for this buffer as it is the original buffer
+    // with the original modifiers.
+    EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
+        &state_, assigns, crtc_properties_[0].id));
+
+    gfx::GpuFenceHandle release_fence;
+    scoped_refptr<ui::PageFlipRequest> page_flip_request =
+        base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta());
+    EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request,
+                                                   &release_fence));
+  }
+
+  {
+    ui::DrmOverlayPlaneList assigns;
+    assigns.clear();
+    fake_drm_->plane_manager()->BeginFrame(&state_);
+    // The test buffer would not have accurate modifiers and therefore should
+    // fail rotation.
+    std::unique_ptr<ui::GbmBuffer> buffer =
+        fake_drm_->gbm_device()->CreateBuffer(
+            DRM_FORMAT_NV12, kDefaultBufferSize, GBM_BO_USE_SCANOUT);
+    scoped_refptr<ui::DrmFramebuffer> framebuffer_non_original =
+        ui::DrmFramebuffer::AddFramebuffer(fake_drm_, buffer.get(),
+                                           kDefaultBufferSize, {}, false);
+    assigns.push_back(ui::DrmOverlayPlane(framebuffer_non_original, nullptr));
+    assigns.back().plane_transform = gfx::OVERLAY_TRANSFORM_ROTATE_270;
+    EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes(
+        &state_, assigns, crtc_properties_[0].id));
+  }
+}
+
 TEST_P(HardwareDisplayPlaneManagerAtomicTest, OverlaySourceCrop) {
   InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1);
   fake_drm_->InitializeState(crtc_properties_, connector_properties_,
@@ -1491,7 +1546,8 @@
                         const gfx::Rect& src_rect,
                         const gfx::OverlayTransform transform,
                         int in_fence_fd,
-                        uint32_t format_fourcc) override {
+                        uint32_t format_fourcc,
+                        bool is_original_buffer) override {
     framebuffer_ = framebuffer;
     return true;
   }
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index 62361a6..0125db35 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -232,7 +232,7 @@
     "cr_components/chromeos/smb_shares/smb_browser_proxy.d.ts",
     "cr_components/chromeos/traffic_counters/traffic_counters.d.ts",
     "cr_components/chromeos/traffic_counters/traffic_counters_adapter.d.ts",
-    "cr_components/chromeos/quick_unlock/pin_keyboard.m.d.ts",
+    "cr_components/chromeos/quick_unlock/pin_keyboard.d.ts",
     "cr_elements/cr_container_shadow_behavior.d.ts",
     "js/list_property_update_behavior.d.ts",
   ]
@@ -253,7 +253,6 @@
   "js/cr/event_target.m.js",
   "js/cr.m.js",
   "js/cr/ui.m.js",
-  "js/cr/ui/focus_outline_manager.js",
   "js/cr/ui/focus_row.js",
   "js/cr/ui/keyboard_shortcut_list.js",
   "js/event_tracker.m.js",
@@ -291,9 +290,9 @@
       "cr_components/chromeos/bluetooth/bluetooth_types.js",
       "cr_components/chromeos/bluetooth/bluetooth_utils.js",
       "cr_components/chromeos/bluetooth/cros_bluetooth_config.js",
-      "cr_components/chromeos/quick_unlock/lock_screen_constants.m.js",
-      "cr_components/chromeos/quick_unlock/pin_keyboard_icon.m.js",
-      "cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js",
+      "cr_components/chromeos/quick_unlock/lock_screen_constants.js",
+      "cr_components/chromeos/quick_unlock/pin_keyboard_icon.js",
+      "cr_components/chromeos/quick_unlock/setup_pin_keyboard.js",
       "cr_elements/chromeos/cros_color_overrides.m.js",
     ]
   }
@@ -316,8 +315,9 @@
   in_files = cr_elements_files + [
                "js/assert_ts.ts",
                "js/custom_element.ts",
-               "js/cr/ui/focus_grid.ts",
                "js/cr/ui/drag_wrapper.ts",
+               "js/cr/ui/focus_grid.ts",
+               "js/cr/ui/focus_outline_manager.ts",
                "js/cr/ui/store_ts.ts",
              ]
 
diff --git a/ui/webui/resources/cr_components/BUILD.gn b/ui/webui/resources/cr_components/BUILD.gn
index de36f40..238bfeb 100644
--- a/ui/webui/resources/cr_components/BUILD.gn
+++ b/ui/webui/resources/cr_components/BUILD.gn
@@ -95,6 +95,7 @@
     "chromeos/bluetooth/cros_bluetooth_config.js",
     "chromeos/network_health/mojo_interface_provider.js",
     "chromeos/network_health/network_diagnostics_types.js",
+    "chromeos/quick_unlock/lock_screen_constants.js",
     "chromeos/smb_shares/smb_browser_proxy.js",
   ]
 }
@@ -188,10 +189,9 @@
       "chromeos/network/network_siminfo.m.js",
       "chromeos/network/onc_mojo.m.js",
       "chromeos/network/sim_lock_dialogs.m.js",
-      "chromeos/quick_unlock/lock_screen_constants.m.js",
-      "chromeos/quick_unlock/pin_keyboard_icon.m.js",
-      "chromeos/quick_unlock/pin_keyboard.m.js",
-      "chromeos/quick_unlock/setup_pin_keyboard.m.js",
+      "chromeos/quick_unlock/pin_keyboard_icon.js",
+      "chromeos/quick_unlock/pin_keyboard.js",
+      "chromeos/quick_unlock/setup_pin_keyboard.js",
       "chromeos/smb_shares/add_smb_share_dialog.js",
       "chromeos/traffic_counters/traffic_counters.js",
     ]
diff --git a/ui/webui/resources/cr_components/chromeos/BUILD.gn b/ui/webui/resources/cr_components/chromeos/BUILD.gn
index e999f837..b5a84b2 100644
--- a/ui/webui/resources/cr_components/chromeos/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/BUILD.gn
@@ -26,7 +26,7 @@
     "multidevice_setup:polymer3_elements",
     "network:polymer3_elements",
     "network_health:web_components",
-    "quick_unlock:polymer3_elements",
+    "quick_unlock:web_components",
     "smb_shares:web_components",
     "traffic_counters:web_components",
   ]
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn b/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn
index d440a5a..9ade96a3 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn
@@ -3,87 +3,50 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
-import("//tools/polymer/polymer.gni")
-import("//ui/webui/resources/tools/js_modulizer.gni")
+import("//tools/polymer/html_to_js.gni")
 import("../os_cr_components.gni")
 
 js_type_check("closure_compile_module") {
   is_polymer3 = true
   deps = [
-    ":lock_screen_constants.m",
-    ":pin_keyboard.m",
-    ":setup_pin_keyboard.m",
+    ":lock_screen_constants",
+    ":pin_keyboard",
+    ":setup_pin_keyboard",
   ]
 }
 
-js_library("lock_screen_constants.m") {
-  sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js" ]
+js_library("lock_screen_constants") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/js:cr.m",
   ]
-  extra_deps = [ ":modulize" ]
 }
 
-js_library("pin_keyboard.m") {
-  sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.m.js" ]
+js_library("pin_keyboard") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
   externs_list =
       [ "//ui/webui/resources/cr_elements/cr_input/cr_input_externs.js" ]
-  extra_deps = [ ":pin_keyboard_module" ]
 }
 
-js_library("setup_pin_keyboard.m") {
-  sources = [ "$root_gen_dir/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js" ]
+js_library("setup_pin_keyboard") {
   deps = [
-    ":lock_screen_constants.m",
-    ":pin_keyboard.m",
+    ":lock_screen_constants",
+    ":pin_keyboard",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants.m",
+    "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
   externs_list = [ "$externs_path/quick_unlock_private.js" ]
   extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ]
-  extra_deps = [ ":setup_pin_keyboard_module" ]
 }
 
-group("polymer3_elements") {
-  public_deps = [
-    ":modulize",
-    ":pin_keyboard_icon_module",
-    ":pin_keyboard_module",
-    ":setup_pin_keyboard_module",
+html_to_js("web_components") {
+  js_files = [
+    "pin_keyboard.js",
+    "pin_keyboard_icon.js",
+    "setup_pin_keyboard.js",
   ]
 }
-
-polymer_modulizer("pin_keyboard") {
-  js_file = "pin_keyboard.js"
-  html_file = "pin_keyboard.html"
-  html_type = "dom-module"
-  namespace_rewrites = cr_components_chromeos_namespace_rewrites
-  migrated_imports = cr_components_migrated_imports
-  auto_imports = cr_components_chromeos_auto_imports
-}
-
-polymer_modulizer("setup_pin_keyboard") {
-  js_file = "setup_pin_keyboard.js"
-  html_file = "setup_pin_keyboard.html"
-  html_type = "dom-module"
-  namespace_rewrites = cr_components_chromeos_namespace_rewrites
-  migrated_imports = cr_components_migrated_imports
-  auto_imports = cr_components_chromeos_auto_imports
-}
-
-js_modulizer("modulize") {
-  input_files = [ "lock_screen_constants.js" ]
-  namespace_rewrites = cr_components_chromeos_namespace_rewrites
-}
-
-polymer_modulizer("pin_keyboard_icon") {
-  js_file = "pin_keyboard_icon.m.js"
-  html_file = "pin_keyboard_icon.html"
-  html_type = "iron-iconset"
-}
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html
deleted file mode 100644
index be24319..0000000
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<link rel="import" href="../../../html/cr.html">
-
-<script src="lock_screen_constants.js"></script>
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js
index 7cc0d46b..d08885e 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js
@@ -5,47 +5,41 @@
 /**
  * @fileoverview Constants used for logging the pin unlock setup uma.
  */
-cr.define('settings', function() {
-  /**
-   * Name of the pin unlock setup uma histogram.
-   * @type {string}
-   */
-  const PinUnlockUmaHistogramName = 'Settings.PinUnlockSetup';
 
-  /**
-   * Stages the user can enter while setting up pin unlock.
-   * @enum {number}
-   */
-  /* #export */ const LockScreenProgress = {
-    START_SCREEN_LOCK: 0,
-    ENTER_PASSWORD_CORRECTLY: 1,
-    CHOOSE_PIN_OR_PASSWORD: 2,
-    ENTER_PIN: 3,
-    CONFIRM_PIN: 4,
-    MAX_BUCKET: 5,
-  };
-  /**
-   * Helper function to send the progress of the pin setup to be recorded in the
-   * histogram.
-   * @param {settings.LockScreenProgress} currentProgress
-   */
-  /* #export */ const recordLockScreenProgress = function(currentProgress) {
-    if (currentProgress >= LockScreenProgress.MAX_BUCKET) {
-      console.error(
-          'Expected a enumeration value of ' + LockScreenProgress.MAX_BUCKET +
-          ' or lower: Received ' + currentProgress + '.');
-      return;
-    }
-    chrome.send('metricsHandler:recordInHistogram', [
-      PinUnlockUmaHistogramName,
-      currentProgress,
-      LockScreenProgress.MAX_BUCKET,
-    ]);
-  };
+/**
+ * Name of the pin unlock setup uma histogram.
+ * @type {string}
+ */
+const PinUnlockUmaHistogramName = 'Settings.PinUnlockSetup';
 
-  // #cr_define_end
-  return {
-    recordLockScreenProgress: recordLockScreenProgress,
-    LockScreenProgress: LockScreenProgress,
-  };
-});
+/**
+ * Stages the user can enter while setting up pin unlock.
+ * @enum {number}
+ */
+export const LockScreenProgress = {
+  START_SCREEN_LOCK: 0,
+  ENTER_PASSWORD_CORRECTLY: 1,
+  CHOOSE_PIN_OR_PASSWORD: 2,
+  ENTER_PIN: 3,
+  CONFIRM_PIN: 4,
+  MAX_BUCKET: 5,
+};
+
+/**
+ * Helper function to send the progress of the pin setup to be recorded in the
+ * histogram.
+ * @param {LockScreenProgress} currentProgress
+ */
+export const recordLockScreenProgress = function(currentProgress) {
+  if (currentProgress >= LockScreenProgress.MAX_BUCKET) {
+    console.error(
+        'Expected a enumeration value of ' + LockScreenProgress.MAX_BUCKET +
+        ' or lower: Received ' + currentProgress + '.');
+    return;
+  }
+  chrome.send('metricsHandler:recordInHistogram', [
+    PinUnlockUmaHistogramName,
+    currentProgress,
+    LockScreenProgress.MAX_BUCKET,
+  ]);
+};
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.m.d.ts b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.d.ts
similarity index 100%
rename from ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.m.d.ts
rename to ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.d.ts
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
index db6d54c61..bedf3a0 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
@@ -1,272 +1,255 @@
-<link rel="import" href="../../../html/polymer.html">
+<style include="cr-shared-style">
+  /**
+    * It's important that buttons are square (have same height and width) so
+    * paper-ripple looks nice
+    */
+  :host {
+    --pin-button-horizontal-margin: 12px;
+    --pin-button-size: 48px;
+    outline: none;
+  }
 
-<link rel="import" href="../../../cr_elements/cr_button/cr_button.html">
-<link rel="import" href="../../../cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="../../../cr_elements/cr_input/cr_input.html">
-<link rel="import" href="../../../cr_elements/icons.html">
-<link rel="import" href="../../../cr_elements/shared_vars_css.html">
-<link rel="import" href="../../../html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="pin_keyboard_icon.html">
+  #root {
+    align-items: center;
+    display: flex;
+    flex-direction: column;
+    min-height: 0;
+  }
 
-<dom-module id="pin-keyboard">
-  <template>
-    <style include="cr-shared-style">
-      /**
-       * It's important that buttons are square (have same height and width) so
-       * paper-ripple looks nice
-       */
-      :host {
-        --pin-button-horizontal-margin: 12px;
-        --pin-button-size: 48px;
-        outline: none;
-      }
+  #rowsContainer {
+    direction: ltr;
+    display: flex;
+    flex-direction: column;
+  }
 
-      #root {
-        align-items: center;
-        display: flex;
-        flex-direction: column;
-        min-height: 0;
-      }
+  .row {
+    align-items: center;
+    display: flex;
+    flex-direction: row;
+    margin-bottom: 8px;
+    min-height: 0;
+  }
 
-      #rowsContainer {
-        direction: ltr;
-        display: flex;
-        flex-direction: column;
-      }
+  :host([enable-password]) #pinInputDiv {
+    display: none;
+  }
 
-      .row {
-        align-items: center;
-        display: flex;
-        flex-direction: row;
-        margin-bottom: 8px;
-        min-height: 0;
-      }
+  .bottom-row {
+    margin-bottom: 0;
+  }
 
-      :host([enable-password]) #pinInputDiv {
-        display: none;
-      }
+  #backspaceButton {
+    --cr-icon-button-fill-color: var(--pin-keyboard-backspace-color,
+                                      var(--cros-icon-color-primary));
+    --cr-icon-button-size: 48px;
+    --cr-icon-button-transition: none;
+    height: var(--pin-button-size);
+    margin: 0 var(--pin-button-horizontal-margin);
+    opacity: var(--pin-keyboard-backspace-opacity, --dark-primary-opacity);
+    width: var(--pin-button-size);
+  }
 
-      .bottom-row {
-        margin-bottom: 0;
-      }
+  #backspaceButton[disabled] {
+    opacity: 0.34;
+  }
 
-      #backspaceButton {
-        --cr-icon-button-fill-color: var(--pin-keyboard-backspace-color,
-                                         var(--cros-icon-color-primary));
-        --cr-icon-button-size: 48px;
-        --cr-icon-button-transition: none;
-        height: var(--pin-button-size);
-        margin: 0 var(--pin-button-horizontal-margin);
-        opacity: var(--pin-keyboard-backspace-opacity, --dark-primary-opacity);
-        width: var(--pin-button-size);
-      }
+  .digit-button {
+    align-items: center;
+    border: 0;
+    box-shadow: none;
+    box-sizing: border-box;
+    display: flex;
+    flex-direction: column;
+    font-weight: 400;
+    height: var(--pin-button-size);
+    margin: 0 var(--pin-button-horizontal-margin);
+    min-height: 0;
+    min-width: 0;
+    opacity: 0.87;
+    padding: 0;
+    width: var(--pin-button-size);
 
-      #backspaceButton[disabled] {
-        opacity: 0.34;
-      }
+    --ink-color: var(--cros-ripple-color);
+    --paper-ripple-opacity: 1;
+  }
 
-      .digit-button {
-        align-items: center;
-        border: 0;
-        box-shadow: none;
-        box-sizing: border-box;
-        display: flex;
-        flex-direction: column;
-        font-weight: 400;
-        height: var(--pin-button-size);
-        margin: 0 var(--pin-button-horizontal-margin);
-        min-height: 0;
-        min-width: 0;
-        opacity: 0.87;
-        padding: 0;
-        width: var(--pin-button-size);
+  :host-context(.focus-outline-visible) .digit-button:focus {
+    font-weight: 500;
+  }
 
-        --ink-color: var(--cros-ripple-color);
-        --paper-ripple-opacity: 1;
-      }
+  .digit-button inner-text {
+    font-family: 'Roboto';
+  }
 
-      :host-context(.focus-outline-visible) .digit-button:focus {
-        font-weight: 500;
-      }
+  /* TODO(https://crbug.com/1307255): Figure out how to use variables of the
+                                      cr-button component to add hovering
+                                      effect */
+  cr-button {
+    background-color: var(--cros-icon-button-background-color);
+    border-radius: 50%;
+  }
 
-      .digit-button inner-text {
-        font-family: 'Roboto';
-      }
+  cr-button:hover {
+    background-color: var(--cros-highlight-color-focus);
+  }
 
-      /* TODO(https://crbug.com/1307255): Figure out how to use variables of the
-                                          cr-button component to add hovering
-                                          effect */
-      cr-button {
-        background-color: var(--cros-icon-button-background-color);
-        border-radius: 50%;
-      }
+  cr-icon-button {
+    background-color: var(--cros-icon-button-background-color);
+    border-radius: 50%;
+  }
 
-      cr-button:hover {
-        background-color: var(--cros-highlight-color-focus);
-      }
+  cr-icon-button:hover {
+    background-color: var(--cros-highlight-color-focus);
+  }
 
-      cr-icon-button {
-        background-color: var(--cros-icon-button-background-color);
-        border-radius: 50%;
-      }
+  inner-text.letter {
+    color: var(--pin-keyboard-letter-color,
+                var(--cros-text-color-secondary));
+    font-size: 12px;
+  }
 
-      cr-icon-button:hover {
-        background-color: var(--cros-highlight-color-focus);
-      }
+  /**
+    * This is needed to align button "1" without text similar to the
+    * buttons with text
+    */
+  inner-text.letter.empty {
+    visibility: hidden;
+  }
 
-      inner-text.letter {
-        color: var(--pin-keyboard-letter-color,
-                   var(--cros-text-color-secondary));
-        font-size: 12px;
-      }
+  .number {
+    color: var(--pin-keyboard-number-color, var(--cros-text-color-primary));
+    font-size: 20px;
+  }
 
-      /**
-       * This is needed to align button "1" without text similar to the
-       * buttons with text
-       */
-      inner-text.letter.empty {
-        visibility: hidden;
-      }
+  #pinInput::part(input) {
+    font-size: 18px;
+  }
 
-      .number {
-        color: var(--pin-keyboard-number-color, var(--cros-text-color-primary));
-        font-size: 20px;
-      }
+  #pinInput {
+    --cr-input-error-display: none;
+    --cr-input-letter-spacing: var(--pin-keyboard-input-letter-spacing,
+                                    18px);
+    --cr-input-padding-end: 0;
+    --cr-input-padding-start: 0;
+    --cr-search-field-placeholder-color:
+        var(--cros-textfield-input-color-disabled);
+    background-color: var(--cros-textfield-background-color);
+    border: 0;
+    box-sizing: border-box;
+    color: var(--cros-textfield-input-color);
+    font-face: Roboto-Regular;
+    font-size: 13px;
+    left: 0;
+    opacity: var(--dark-secondary-opacity);
+    outline: 0;
+    position: relative;
+    text-align: center;
+    width: 200px;
 
-      #pinInput::part(input) {
-        font-size: 18px;
-      }
+    @apply --pin-keyboard-pin-input-style;
+  }
 
-      #pinInput {
-        --cr-input-error-display: none;
-        --cr-input-letter-spacing: var(--pin-keyboard-input-letter-spacing,
-                                       18px);
-        --cr-input-padding-end: 0;
-        --cr-input-padding-start: 0;
-        --cr-search-field-placeholder-color:
-            var(--cros-textfield-input-color-disabled);
-        background-color: var(--cros-textfield-background-color);
-        border: 0;
-        box-sizing: border-box;
-        color: var(--cros-textfield-input-color);
-        font-face: Roboto-Regular;
-        font-size: 13px;
-        left: 0;
-        opacity: var(--dark-secondary-opacity);
-        outline: 0;
-        position: relative;
-        text-align: center;
-        width: 200px;
+  #pinInput[make-contrast] {
+    opacity: var(--dark-primary-opacity);
+  }
 
-        @apply --pin-keyboard-pin-input-style;
-      }
+  #pinInput[is-input-rtl] {
+    direction: rtl;
+  }
 
-      #pinInput[make-contrast] {
-        opacity: var(--dark-primary-opacity);
-      }
+  /* Ensure that all children of cr-button do not consume events. This
+    * simplifies the event handler code. */
+  cr-button * {
+    pointer-events: none;
+  }
+</style>
 
-      #pinInput[is-input-rtl] {
-        direction: rtl;
-      }
-
-      /* Ensure that all children of cr-button do not consume events. This
-       * simplifies the event handler code. */
-      cr-button * {
-        pointer-events: none;
-      }
-    </style>
-
-    <div id="root" on-click="onRootTap_">
-      <div id="pinInputDiv">
-        <cr-input id="pinInput" type="password" value="{{value}}"
-            is-input-rtl$="[[isInputRtl_(value)]]" aria-label="[[ariaLabel]]"
-            make-contrast$="[[hasInputOrFocus_(value, focused_)]]"
-            invalid="[[hasError]]"
-            placeholder="[[getInputPlaceholder_(enablePassword,
-                enablePlaceholder)]]" inputmode="none"
-            on-keydown="onInputKeyDown_" force-underline="true"
-            disabled="[[disabled]]">
-        </cr-input>
-        <slot select="[problem]"></slot>
-      </div>
-      <div id="rowsContainer">
-        <div class="row">
-          <cr-button class="digit-button" on-click="onNumberTap_" value="1"
-              id="digitButton1" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard1')]]</inner-text>
-          </cr-button>
-          <cr-button class="digit-button" on-click="onNumberTap_" value="2"
-              id="digitButton2" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard2')]]</inner-text>
-          </cr-button>
-          <cr-button class="digit-button" on-click="onNumberTap_" value="3"
-              id="digitButton3" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard3')]]</inner-text>
-          </cr-button>
-        </div>
-        <div class="row">
-          <cr-button class="digit-button" on-click="onNumberTap_" value="4"
-              id="digitButton4" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard4')]]</inner-text>
-          </cr-button>
-          <cr-button class="digit-button" on-click="onNumberTap_" value="5"
-              id="digitButton5" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard5')]]</inner-text>
-          </cr-button>
-          <cr-button class="digit-button" on-click="onNumberTap_" value="6"
-              id="digitButton6" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard6')]]</inner-text>
-          </cr-button>
-        </div>
-        <div class="row">
-          <cr-button class="digit-button" on-click="onNumberTap_" value="7"
-              id="digitButton7" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard7')]]</inner-text>
-          </cr-button>
-          <cr-button class="digit-button" on-click="onNumberTap_" value="8"
-              id="digitButton8" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard8')]]</inner-text>
-          </cr-button>
-          <cr-button class="digit-button" on-click="onNumberTap_" value="9"
-              id="digitButton9" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard9')]]</inner-text>
-          </cr-button>
-        </div>
-        <div class="row bottom-row">
-          <cr-icon-button id="backspaceButton"
-              disabled$="[[!hasInput_(value)]]"
-              iron-icon="pin-keyboard:backspace"
-              on-click="onBackspaceTap_"
-              on-pointerdown="onBackspacePointerDown_"
-              on-pointerout="clearAndReset_"
-              on-pointerup="onBackspacePointerUp_"
-              on-contextmenu="onBackspaceContextMenu_"
-              title="[[i18n('pinKeyboardDeleteAccessibleName')]]"
-              custom-tab-index="-1">
-          </cr-icon-button>
-          <cr-button class="digit-button" on-click="onNumberTap_" value="0"
-              id="digitButton0" disabled="[[disabled]]" circle-ripple
-              custom-tab-index="-1">
-            <inner-text class="number">[[i18n('pinKeyboard0')]]</inner-text>
-          </cr-button>
-          <div class="digit-button"></div>
-        </div>
-      </div>
+<div id="root" on-click="onRootTap_">
+  <div id="pinInputDiv">
+    <cr-input id="pinInput" type="password" value="{{value}}"
+        is-input-rtl$="[[isInputRtl_(value)]]" aria-label="[[ariaLabel]]"
+        make-contrast$="[[hasInputOrFocus_(value, focused_)]]"
+        invalid="[[hasError]]"
+        placeholder="[[getInputPlaceholder_(enablePassword,
+            enablePlaceholder)]]" inputmode="none"
+        on-keydown="onInputKeyDown_" force-underline="true"
+        disabled="[[disabled]]">
+    </cr-input>
+    <slot select="[problem]"></slot>
+  </div>
+  <div id="rowsContainer">
+    <div class="row">
+      <cr-button class="digit-button" on-click="onNumberTap_" value="1"
+          id="digitButton1" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard1')]]</inner-text>
+      </cr-button>
+      <cr-button class="digit-button" on-click="onNumberTap_" value="2"
+          id="digitButton2" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard2')]]</inner-text>
+      </cr-button>
+      <cr-button class="digit-button" on-click="onNumberTap_" value="3"
+          id="digitButton3" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard3')]]</inner-text>
+      </cr-button>
     </div>
-  </template>
-  <script src="pin_keyboard.js"></script>
-</dom-module>
+    <div class="row">
+      <cr-button class="digit-button" on-click="onNumberTap_" value="4"
+          id="digitButton4" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard4')]]</inner-text>
+      </cr-button>
+      <cr-button class="digit-button" on-click="onNumberTap_" value="5"
+          id="digitButton5" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard5')]]</inner-text>
+      </cr-button>
+      <cr-button class="digit-button" on-click="onNumberTap_" value="6"
+          id="digitButton6" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard6')]]</inner-text>
+      </cr-button>
+    </div>
+    <div class="row">
+      <cr-button class="digit-button" on-click="onNumberTap_" value="7"
+          id="digitButton7" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard7')]]</inner-text>
+      </cr-button>
+      <cr-button class="digit-button" on-click="onNumberTap_" value="8"
+          id="digitButton8" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard8')]]</inner-text>
+      </cr-button>
+      <cr-button class="digit-button" on-click="onNumberTap_" value="9"
+          id="digitButton9" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard9')]]</inner-text>
+      </cr-button>
+    </div>
+    <div class="row bottom-row">
+      <cr-icon-button id="backspaceButton"
+          disabled$="[[!hasInput_(value)]]"
+          iron-icon="pin-keyboard:backspace"
+          on-click="onBackspaceTap_"
+          on-pointerdown="onBackspacePointerDown_"
+          on-pointerout="clearAndReset_"
+          on-pointerup="onBackspacePointerUp_"
+          on-contextmenu="onBackspaceContextMenu_"
+          title="[[i18n('pinKeyboardDeleteAccessibleName')]]"
+          custom-tab-index="-1">
+      </cr-icon-button>
+      <cr-button class="digit-button" on-click="onNumberTap_" value="0"
+          id="digitButton0" disabled="[[disabled]]" circle-ripple
+          custom-tab-index="-1">
+        <inner-text class="number">[[i18n('pinKeyboard0')]]</inner-text>
+      </cr-button>
+      <div class="digit-button"></div>
+    </div>
+  </div>
+</div>
 
 <!-- TODO(crbug.com/603217): Use i18n instead of string literals. Figure out
                              what i18n to use for keypad, ie, does 1 ABC make
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
index 627021a..c8c81d0 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
@@ -22,7 +22,18 @@
  *    </pin-keyboard>
  */
 
-(function() {
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.js';
+import 'chrome://resources/cr_elements/icons.m.js';
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+import 'chrome://resources/polymer/v3_0/paper-styles/color.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+import './pin_keyboard_icon.js';
+
+import {html, Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {I18nBehavior} from '../../../js/i18n_behavior.m.js';
 
 /**
  * Once auto backspace starts, the time between individual backspaces.
@@ -76,6 +87,7 @@
 }
 
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'pin-keyboard',
 
   behaviors: [
@@ -558,4 +570,3 @@
         (/** @type {CrInputElement} */ (this.$.pinInput)).inputElement;
   },
 });
-})();
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard_icon.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard_icon.html
index 7dfb928..a974ccce 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard_icon.html
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard_icon.html
@@ -1,7 +1,3 @@
-<link rel="import" href="../../../html/polymer.html">
-
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
-
 <iron-iconset-svg name="pin-keyboard" size="24">
   <svg>
     <defs>
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard_icon.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard_icon.js
new file mode 100644
index 0000000..13e2c691
--- /dev/null
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard_icon.js
@@ -0,0 +1,10 @@
+// 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.
+
+import 'chrome://resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
+
+import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+const template = html`{__html_template__}`;
+document.head.appendChild(template.content);
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html
index 84238eb..aed80f45 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html
@@ -1,11 +1,3 @@
-<link rel="import" href="../../../html/polymer.html">
-
-<link rel="import" href="lock_screen_constants.html">
-<link rel="import" href="../../../cr_elements/shared_vars_css.html">
-<link rel="import" href="../../../html/assert.html">
-<link rel="import" href="../../../html/i18n_behavior.html">
-<link rel="import" href="pin_keyboard.html">
-
 <!--
 
 This module is a "pin setup" keyboard + pin display element.
@@ -39,48 +31,43 @@
 
 -->
 
-<dom-module id="setup-pin-keyboard">
-  <template>
-    <style include="settings-shared">
-      .error {
-        color: var(--cros-text-color-alert);
-        display: inline-block;
-      }
+<style include="settings-shared">
+  .error {
+    color: var(--cros-text-color-alert);
+    display: inline-block;
+  }
 
-      .warning {
-        color: var(--cr-secondary-text-color);
-      }
+  .warning {
+    color: var(--cr-secondary-text-color);
+  }
 
-      #problemDiv {
-        align-items: center;
-        display: flex;
-        flex-direction: row;
-        font-size: 10px;
-        height: 32px;
-        min-height: 0;
-        padding-bottom: 8px;
-      }
+  #problemDiv {
+    align-items: center;
+    display: flex;
+    flex-direction: row;
+    font-size: 10px;
+    height: 32px;
+    min-height: 0;
+    padding-bottom: 8px;
+  }
 
-      /* Hide this using visibility: hidden instead of hidden so that the
-         dialog does not resize when there are no problems to display. */
-      #problemDiv[invisible] {
-        visibility: hidden;
-      }
-    </style>
-    <pin-keyboard id="pinKeyboard" on-pin-change="onPinChange_"
-        on-submit="onPinSubmit_" value="{{pinKeyboardValue_}}"
-        has-error="[[hasError_(problemMessageId_, problemClass_)]]"
-        disabled="[[isSetModesCallPending_]]"
-        enable-placeholder="[[enablePlaceholder]]">
-      <!-- Warning/error; only shown if title is hidden. -->
-      <div id="problemDiv" class$="[[problemClass_]]"
-          invisible$="[[!problemMessageId_]]" problem>
-        <span aria-live="assertive">
-          [[formatProblemMessage_(locale, problemMessageId_,
-                problemMessageParameters_)]]
-        </span>
-      </div>
-    </pin-keyboard>
-  </template>
-  <script src="setup_pin_keyboard.js"></script>
-</dom-module>
+  /* Hide this using visibility: hidden instead of hidden so that the
+      dialog does not resize when there are no problems to display. */
+  #problemDiv[invisible] {
+    visibility: hidden;
+  }
+</style>
+<pin-keyboard id="pinKeyboard" on-pin-change="onPinChange_"
+    on-submit="onPinSubmit_" value="{{pinKeyboardValue_}}"
+    has-error="[[hasError_(problemMessageId_, problemClass_)]]"
+    disabled="[[isSetModesCallPending_]]"
+    enable-placeholder="[[enablePlaceholder]]">
+  <!-- Warning/error; only shown if title is hidden. -->
+  <div id="problemDiv" class$="[[problemClass_]]"
+      invisible$="[[!problemMessageId_]]" problem>
+    <span aria-live="assertive">
+      [[formatProblemMessage_(locale, problemMessageId_,
+            problemMessageParameters_)]]
+    </span>
+  </div>
+</pin-keyboard>
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
index 8d0fd164..8271990 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
@@ -10,11 +10,20 @@
  *
  */
 
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+import './pin_keyboard.js';
+
+import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {LockScreenProgress} from './lock_screen_constants.js';
+
 /**
  * Keep in sync with the string keys provided by settings.
  * @enum {string}
  */
-/* #export */ const MessageType = {
+export const MessageType = {
   TOO_SHORT: 'configurePinTooShort',
   TOO_LONG: 'configurePinTooLong',
   TOO_WEAK: 'configurePinWeakPin',
@@ -23,12 +32,13 @@
 };
 
 /** @enum {string} */
-/* #export */ const ProblemType = {
+export const ProblemType = {
   WARNING: 'warning',
   ERROR: 'error',
 };
 
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'setup-pin-keyboard',
 
   behaviors: [I18nBehavior],
@@ -92,7 +102,7 @@
     /**
      * writeUma is a function that handles writing uma stats.
      *
-     * @type {function(settings.LockScreenProgress)}
+     * @type {function(LockScreenProgress)}
      */
     writeUma: {
       type: Object,
@@ -338,7 +348,7 @@
       this.onPinChange_(new CustomEvent(
           'pin-change', {detail: {pin: this.pinKeyboardValue_}}));
       this.$.pinKeyboard.focusInput();
-      this.writeUma(settings.LockScreenProgress.ENTER_PIN);
+      this.writeUma(LockScreenProgress.ENTER_PIN);
       return;
     }
     // onPinSubmit gets called if the user hits enter on the PIN keyboard.
@@ -357,7 +367,7 @@
     this.setModes.call(
         null, [chrome.quickUnlockPrivate.QuickUnlockMode.PIN],
         [this.pinKeyboardValue_], this.onSetModesCompleted_.bind(this));
-    this.writeUma(settings.LockScreenProgress.CONFIRM_PIN);
+    this.writeUma(LockScreenProgress.CONFIRM_PIN);
   },
 
   /**
diff --git a/ui/webui/resources/js/cr/ui/BUILD.gn b/ui/webui/resources/js/cr/ui/BUILD.gn
index 59ea2c3..036342e 100644
--- a/ui/webui/resources/js/cr/ui/BUILD.gn
+++ b/ui/webui/resources/js/cr/ui/BUILD.gn
@@ -34,6 +34,7 @@
   in_files = [
     "drag_wrapper.ts",
     "focus_grid.ts",
+    "focus_outline_manager.ts",
     "store_ts.ts",
   ]
 }
@@ -46,7 +47,6 @@
   in_files = [
     "keyboard_shortcut_list.js",
     "focus_row.js",
-    "focus_outline_manager.js",
   ]
 
   if (!is_android) {
@@ -80,7 +80,6 @@
 js_type_check("ui_resources_modules") {
   is_polymer3 = true
   deps = [
-    ":focus_outline_manager",
     ":focus_row",
     ":focus_row_behavior",
     ":focus_without_ink",
@@ -125,9 +124,6 @@
   }
 }
 
-js_library("focus_outline_manager") {
-}
-
 js_library("focus_row") {
   deps = [
     "../..:assert.m",
diff --git a/ui/webui/resources/js/cr/ui/focus_outline_manager.js b/ui/webui/resources/js/cr/ui/focus_outline_manager.js
deleted file mode 100644
index 3889e5a1e..0000000
--- a/ui/webui/resources/js/cr/ui/focus_outline_manager.js
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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.
-
-  /**
-   * The class name to set on the document element.
-   * @const
-   */
-  const CLASS_NAME = 'focus-outline-visible';
-
-  /** @type {!Map<!Document, !FocusOutlineManager>} */
-  const docsToManager = new Map();
-
-  /**
-   * This class sets a CSS class name on the HTML element of |doc| when the user
-   * presses a key. It removes the class name when the user clicks anywhere.
-   *
-   * This allows you to write CSS like this:
-   *
-   * html.focus-outline-visible my-element:focus {
-   *   outline: 5px auto -webkit-focus-ring-color;
-   * }
-   *
-   * And the outline will only be shown if the user uses the keyboard to get to
-   * it.
-   *
-   */
-  export class FocusOutlineManager {
-    /**
-     * @param {!Document} doc The document to attach the focus outline manager
-     *     to.
-     */
-    constructor(doc) {
-      /**
-       * Whether focus change is triggered by a keyboard event.
-       * @private {boolean}
-       */
-      this.focusByKeyboard_ = true;
-
-      this.classList_ = doc.documentElement.classList;
-
-      const onEvent = function(focusByKeyboard, e) {
-        if (this.focusByKeyboard_ === focusByKeyboard) {
-          return;
-        }
-        this.focusByKeyboard_ = focusByKeyboard;
-        this.updateVisibility();
-      };
-
-      doc.addEventListener('keydown', onEvent.bind(this, true), true);
-      doc.addEventListener('mousedown', onEvent.bind(this, false), true);
-
-      this.updateVisibility();
-    }
-
-    updateVisibility() {
-      this.visible = this.focusByKeyboard_;
-    }
-
-    /**
-     * Whether the focus outline should be visible.
-     * @type {boolean}
-     */
-    set visible(visible) {
-      this.classList_.toggle(CLASS_NAME, visible);
-    }
-
-    get visible() {
-      return this.classList_.contains(CLASS_NAME);
-    }
-
-    /**
-     * Gets a per document singleton focus outline manager.
-     * @param {!Document} doc The document to get the |FocusOutlineManager| for.
-     * @return {!FocusOutlineManager} The per document singleton focus
-     *     outline manager.
-     */
-    static forDocument(doc) {
-      let manager = docsToManager.get(doc);
-      if (!manager) {
-        manager = new FocusOutlineManager(doc);
-        docsToManager.set(doc, manager);
-      }
-      return manager;
-    }
-  }
-
diff --git a/ui/webui/resources/js/cr/ui/focus_outline_manager.ts b/ui/webui/resources/js/cr/ui/focus_outline_manager.ts
new file mode 100644
index 0000000..8a8d10b1
--- /dev/null
+++ b/ui/webui/resources/js/cr/ui/focus_outline_manager.ts
@@ -0,0 +1,79 @@
+// 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.
+
+/**
+ * The class name to set on the document element.
+ */
+const CLASS_NAME = 'focus-outline-visible';
+
+const docsToManager: Map<Document, FocusOutlineManager> = new Map();
+
+/**
+ * This class sets a CSS class name on the HTML element of |doc| when the user
+ * presses a key. It removes the class name when the user clicks anywhere.
+ *
+ * This allows you to write CSS like this:
+ *
+ * html.focus-outline-visible my-element:focus {
+ *   outline: 5px auto -webkit-focus-ring-color;
+ * }
+ *
+ * And the outline will only be shown if the user uses the keyboard to get to
+ * it.
+ *
+ */
+export class FocusOutlineManager {
+  // Whether focus change is triggered by a keyboard event.
+  private focusByKeyboard_: boolean = true;
+  private classList_: DOMTokenList;
+
+  /**
+   * @param doc The document to attach the focus outline manager to.
+   */
+  constructor(doc: Document) {
+    this.classList_ = doc.documentElement.classList;
+
+    doc.addEventListener('keydown', () => this.onEvent_(true), true);
+    doc.addEventListener('mousedown', () => this.onEvent_(false), true);
+
+    this.updateVisibility();
+  }
+
+  private onEvent_(focusByKeyboard: boolean) {
+    if (this.focusByKeyboard_ === focusByKeyboard) {
+      return;
+    }
+    this.focusByKeyboard_ = focusByKeyboard;
+    this.updateVisibility();
+  }
+
+  updateVisibility() {
+    this.visible = this.focusByKeyboard_;
+  }
+
+  /**
+   * Whether the focus outline should be visible.
+   */
+  set visible(visible: boolean) {
+    this.classList_.toggle(CLASS_NAME, visible);
+  }
+
+  get visible(): boolean {
+    return this.classList_.contains(CLASS_NAME);
+  }
+
+  /**
+   * Gets a per document singleton focus outline manager.
+   * @param doc The document to get the |FocusOutlineManager| for.
+   * @return The per document singleton focus outline manager.
+   */
+  static forDocument(doc: Document): FocusOutlineManager {
+    let manager = docsToManager.get(doc);
+    if (!manager) {
+      manager = new FocusOutlineManager(doc);
+      docsToManager.set(doc, manager);
+    }
+    return manager;
+  }
+}