diff --git a/BUILD.gn b/BUILD.gn
index d62cd51..a3cf0a49 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -676,8 +676,7 @@
     deps += [ "//third_party/breakpad:symupload($host_toolchain)" ]
   }
 
-  # TODO(crbug.com/1330636): Remove the Fuchsia `is_chromecast` condition.
-  if (is_cast_android || is_castos || (is_fuchsia && is_chromecast)) {
+  if (is_cast_android || is_castos || (is_fuchsia && enable_cast_receiver)) {
     deps += [ "//chromecast:cast_test_lists" ]
   }
 
diff --git a/DEPS b/DEPS
index 353b879..4e5c4d3 100644
--- a/DEPS
+++ b/DEPS
@@ -294,11 +294,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '43c262f54f69ea547666f3ee8740170f79299784',
+  'skia_revision': '07769b152a6930226af39f0a012ddb18002fb79f',
   # 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': '79505e36825784d7bd501ea02062a0e817b04c6c',
+  'v8_revision': '778b4528d133b4733e42e44df5ad6fa7ff1e6e0b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -373,7 +373,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'f6f2e8b7643a7fe996b031968052c9c775611333',
+  'devtools_frontend_revision': '395fc4d181fbc91fca8f371489546ee64e59df80',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -409,7 +409,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'fc85619d770e00e72ca541cd2957a9e3ac6c016b',
+  'dawn_revision': '6c098baedfecb2049ca818ff6083bcbbd6210682',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1124,7 +1124,7 @@
   },
 
   'src/third_party/cast_core/public/src':
-    Var('chromium_git') + '/cast_core/public' + '@' + '73f0f19136b7617bb893a98e7dd6d17893ff55ba',
+    Var('chromium_git') + '/cast_core/public' + '@' + '8ba5ff47563d0ca8233e8fa009377ed14a560cf4',
 
   'src/third_party/catapult':
     Var('chromium_git') + '/catapult.git' + '@' + Var('catapult_revision'),
@@ -1564,7 +1564,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'fac04ceb3e966f613ed17e98178e9d690280bba6',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'cd61d5d54ff63d98f5f0db4faf16cda863714e59',
+    Var('chromium_git') + '/openscreen' + '@' + '6be6b78224a276e908b8272542d125e133c40f3f',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + 'bf21ccb1007bb531b45d9978919a56ea5059c245',
@@ -1581,7 +1581,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'da6e55d8b227cff3a88c26db23781790fad56c49',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'bed4b810cd8b4049d93220a9279fc56d95f3289d',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1712,7 +1712,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@3cc80b7988b64231617ce87582c4d3d01e61503a',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@f8ca6e516bbaa728b076f9176a52a968dbf67bef',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1824,7 +1824,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@10e53579b11c64ddfc57dc7c22d98bb6f0b96027',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d2a3e62b535cdd826ea79bbe587bf304cef32905',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
index cdada6d..d34e38ea 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
@@ -207,7 +207,7 @@
 
         navigation.didFinish(gurl, isErrorPage, true /* hasCommitted */, isFragmentNavigation,
                 false /* isDownload */, false /* isValidSearchFormUrl */, transition,
-                0 /* errorCode*/, 200 /* httpStatusCode*/);
+                0 /* errorCode*/, 200 /* httpStatusCode*/, false /* isExternalProtocol */);
         mWebContentsObserver.didFinishNavigation(navigation);
     }
 }
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwNonembeddedUmaRecorder.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwNonembeddedUmaRecorder.java
index 348f7a0..f8cf091 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwNonembeddedUmaRecorder.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/AwNonembeddedUmaRecorder.java
@@ -20,6 +20,7 @@
 import org.chromium.android_webview.proto.MetricsBridgeRecords.HistogramRecord;
 import org.chromium.android_webview.proto.MetricsBridgeRecords.HistogramRecord.Metadata;
 import org.chromium.android_webview.proto.MetricsBridgeRecords.HistogramRecord.RecordType;
+import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.metrics.UmaRecorder;
@@ -163,6 +164,16 @@
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public void addUserActionCallbackForTesting(Callback<String> callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeUserActionCallbackForTesting(Callback<String> callback) {
+        throw new UnsupportedOperationException();
+    }
+
     private final Object mLock = new Object();
     // Service stub object
     @GuardedBy("mLock")
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 1df40a5..53e6b95d 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -340,6 +340,9 @@
   registry->RegisterBooleanPref(
       prefs::kLauncherContinueSectionHidden, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
+  registry->RegisterTimePref(prefs::kLauncherLastContinueRequestTime,
+                             base::Time());
+  registry->RegisterBooleanPref(prefs::kLauncherUseLongContinueDelay, false);
   AppListNudgeController::RegisterProfilePrefs(registry);
 }
 
diff --git a/ash/components/arc/BUILD.gn b/ash/components/arc/BUILD.gn
index 8a8f14f..91df467 100644
--- a/ash/components/arc/BUILD.gn
+++ b/ash/components/arc/BUILD.gn
@@ -125,6 +125,7 @@
     "//ash/keyboard/ui",
     "//ash/public/cpp",
     "//ash/public/cpp/external_arc:external_arc",
+    "//chromeos/ash/components/dbus/arc",
     "//chromeos/ash/components/dbus/concierge",
     "//chromeos/ash/components/dbus/concierge:concierge_proto",
     "//chromeos/ash/components/dbus/patchpanel",
@@ -448,6 +449,7 @@
     "//base",
     "//base/test:test_support",
     "//chromeos",
+    "//chromeos/ash/components/dbus/arc",
     "//chromeos/ash/components/dbus/concierge",
     "//chromeos/ash/components/dbus/patchpanel",
     "//chromeos/ash/components/dbus/patchpanel:patchpanel_proto",
diff --git a/ash/components/arc/appfuse/arc_appfuse_bridge.cc b/ash/components/arc/appfuse/arc_appfuse_bridge.cc
index 0195798c..102b283 100644
--- a/ash/components/arc/appfuse/arc_appfuse_bridge.cc
+++ b/ash/components/arc/appfuse/arc_appfuse_bridge.cc
@@ -12,7 +12,7 @@
 #include "ash/components/arc/session/arc_bridge_service.h"
 #include "base/bind.h"
 #include "base/memory/singleton.h"
-#include "chromeos/dbus/arc/arc_appfuse_provider_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
 namespace arc {
@@ -83,15 +83,15 @@
                              int32_t mount_id,
                              MountCallback callback) {
   // This is safe because ArcAppfuseProviderClient outlives ArcServiceLauncher.
-  chromeos::ArcAppfuseProviderClient::Get()->Mount(
+  ash::ArcAppfuseProviderClient::Get()->Mount(
       uid, mount_id, base::BindOnce(&RunWithScopedHandle, std::move(callback)));
 }
 
 void ArcAppfuseBridge::Unmount(uint32_t uid,
                                int32_t mount_id,
                                UnmountCallback callback) {
-  chromeos::ArcAppfuseProviderClient::Get()->Unmount(uid, mount_id,
-                                                     std::move(callback));
+  ash::ArcAppfuseProviderClient::Get()->Unmount(uid, mount_id,
+                                                std::move(callback));
 }
 
 void ArcAppfuseBridge::OpenFile(uint32_t uid,
@@ -99,7 +99,7 @@
                                 int32_t file_id,
                                 int32_t flags,
                                 OpenFileCallback callback) {
-  chromeos::ArcAppfuseProviderClient::Get()->OpenFile(
+  ash::ArcAppfuseProviderClient::Get()->OpenFile(
       uid, mount_id, file_id, flags,
       base::BindOnce(&RunWithScopedHandle, std::move(callback)));
 }
diff --git a/ash/components/arc/camera/arc_camera_bridge.cc b/ash/components/arc/camera/arc_camera_bridge.cc
index 1a9e56da..ee15248 100644
--- a/ash/components/arc/camera/arc_camera_bridge.cc
+++ b/ash/components/arc/camera/arc_camera_bridge.cc
@@ -13,7 +13,7 @@
 #include "base/memory/singleton.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
-#include "chromeos/dbus/arc/arc_camera_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_camera_client.h"
 #include "crypto/random.h"
 #include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -141,7 +141,7 @@
   base::ScopedFD fd =
       channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD();
 
-  chromeos::ArcCameraClient::Get()->StartService(
+  ash::ArcCameraClient::Get()->StartService(
       fd.get(), token, base::BindOnce([](bool success) {}));
 }
 
diff --git a/ash/components/arc/enterprise/BUILD.gn b/ash/components/arc/enterprise/BUILD.gn
index 396ba6bb..781c888 100644
--- a/ash/components/arc/enterprise/BUILD.gn
+++ b/ash/components/arc/enterprise/BUILD.gn
@@ -30,9 +30,9 @@
     "//ash/components/policy",
     "//ash/constants",
     "//base",
+    "//chromeos/ash/components/dbus/arc",
     "//chromeos/ash/components/dbus/upstart:upstart",
     "//chromeos/dbus:dbus",
-    "//chromeos/dbus/arc:arc",
     "//chromeos/dbus/power",
     "//components/prefs",
     "//components/session_manager/core",
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_bridge.cc b/ash/components/arc/enterprise/arc_data_snapshotd_bridge.cc
index 3cb300c..d25efe8 100644
--- a/ash/components/arc/enterprise/arc_data_snapshotd_bridge.cc
+++ b/ash/components/arc/enterprise/arc_data_snapshotd_bridge.cc
@@ -9,7 +9,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/dbus/arc/arc_data_snapshotd_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.h"
 
 namespace arc {
 namespace data_snapshotd {
@@ -55,7 +55,7 @@
 
   dbus_waiting_weak_ptr_factory_.InvalidateWeakPtrs();
 
-  chromeos::ArcDataSnapshotdClient::Get()->WaitForServiceToBeAvailable(
+  ash::ArcDataSnapshotdClient::Get()->WaitForServiceToBeAvailable(
       base::BindOnce(&ArcDataSnapshotdBridge::OnWaitedForDBusService,
                      dbus_waiting_weak_ptr_factory_.GetWeakPtr()));
   ScheduleWaitingForDBusService();
@@ -91,7 +91,7 @@
   }
 
   VLOG(1) << "GenerateKeyPair via D-Bus";
-  chromeos::ArcDataSnapshotdClient::Get()->GenerateKeyPair(std::move(callback));
+  ash::ArcDataSnapshotdClient::Get()->GenerateKeyPair(std::move(callback));
 }
 
 void ArcDataSnapshotdBridge::ClearSnapshot(
@@ -103,8 +103,7 @@
     return;
   }
   VLOG(1) << "ClearSnapshot via D-Bus";
-  chromeos::ArcDataSnapshotdClient::Get()->ClearSnapshot(last,
-                                                         std::move(callback));
+  ash::ArcDataSnapshotdClient::Get()->ClearSnapshot(last, std::move(callback));
 }
 
 void ArcDataSnapshotdBridge::TakeSnapshot(
@@ -116,8 +115,8 @@
     return;
   }
   VLOG(1) << "TakeSnapshot via D-Bus";
-  chromeos::ArcDataSnapshotdClient::Get()->TakeSnapshot(account_id,
-                                                        std::move(callback));
+  ash::ArcDataSnapshotdClient::Get()->TakeSnapshot(account_id,
+                                                   std::move(callback));
 }
 
 void ArcDataSnapshotdBridge::LoadSnapshot(
@@ -129,8 +128,8 @@
     return;
   }
   VLOG(1) << "LoadSnapshot via D-Bus";
-  chromeos::ArcDataSnapshotdClient::Get()->LoadSnapshot(account_id,
-                                                        std::move(callback));
+  ash::ArcDataSnapshotdClient::Get()->LoadSnapshot(account_id,
+                                                   std::move(callback));
 }
 
 void ArcDataSnapshotdBridge::Update(int percent,
@@ -141,7 +140,7 @@
     return;
   }
   VLOG(1) << "Update via D-Bus";
-  chromeos::ArcDataSnapshotdClient::Get()->Update(percent, std::move(callback));
+  ash::ArcDataSnapshotdClient::Get()->Update(percent, std::move(callback));
 }
 
 void ArcDataSnapshotdBridge::ConnectToUiCancelledSignal(
@@ -152,7 +151,7 @@
     return;
   }
   VLOG(1) << "Connect to UiCancelled D-Bus signal.";
-  chromeos::ArcDataSnapshotdClient::Get()->ConnectToUiCancelledSignal(
+  ash::ArcDataSnapshotdClient::Get()->ConnectToUiCancelledSignal(
       std::move(signal_callback),
       base::BindOnce(
           &ArcDataSnapshotdBridge::OnUiCancelledSignalConnectedCallback,
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_bridge_unittest.cc b/ash/components/arc/enterprise/arc_data_snapshotd_bridge_unittest.cc
index f653528..c4a8a44 100644
--- a/ash/components/arc/enterprise/arc_data_snapshotd_bridge_unittest.cc
+++ b/ash/components/arc/enterprise/arc_data_snapshotd_bridge_unittest.cc
@@ -10,8 +10,8 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
-#include "chromeos/dbus/arc/arc_data_snapshotd_client.h"
-#include "chromeos/dbus/arc/fake_arc_data_snapshotd_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -94,18 +94,18 @@
   ArcDataSnapshotdBridgeTest() {
     chromeos::DBusThreadManager::Initialize();
     EXPECT_TRUE(chromeos::DBusThreadManager::Get()->IsUsingFakes());
-    chromeos::ArcDataSnapshotdClient::InitializeFake();
+    ash::ArcDataSnapshotdClient::InitializeFake();
   }
 
   ~ArcDataSnapshotdBridgeTest() override {
-    chromeos::ArcDataSnapshotdClient::Shutdown();
+    ash::ArcDataSnapshotdClient::Shutdown();
     chromeos::DBusThreadManager::Shutdown();
   }
 
-  chromeos::FakeArcDataSnapshotdClient* dbus_client() {
-    auto* client = chromeos::ArcDataSnapshotdClient::Get();
+  ash::FakeArcDataSnapshotdClient* dbus_client() {
+    auto* client = ash::ArcDataSnapshotdClient::Get();
     DCHECK(client);
-    return static_cast<chromeos::FakeArcDataSnapshotdClient*>(client);
+    return static_cast<ash::FakeArcDataSnapshotdClient*>(client);
   }
 
   void FastForwardAttempt() {
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc b/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
index e3bd01d4..88bf84b 100644
--- a/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
+++ b/ash/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
@@ -20,8 +20,8 @@
 #include "base/system/sys_info.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.h"
 #include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
-#include "chromeos/dbus/arc/fake_arc_data_snapshotd_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/session_manager/core/session_manager.h"
@@ -166,7 +166,7 @@
     // Initialize fake D-Bus client.
     chromeos::DBusThreadManager::Initialize();
     EXPECT_TRUE(chromeos::DBusThreadManager::Get()->IsUsingFakes());
-    chromeos::ArcDataSnapshotdClient::InitializeFake();
+    ash::ArcDataSnapshotdClient::InitializeFake();
 
     fake_user_manager_ = new user_manager::FakeUserManager();
     scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
@@ -193,7 +193,7 @@
   }
 
   ~ArcDataSnapshotdManagerBasicTest() override {
-    chromeos::ArcDataSnapshotdClient::Shutdown();
+    ash::ArcDataSnapshotdClient::Shutdown();
     chromeos::DBusThreadManager::Shutdown();
   }
 
@@ -220,8 +220,8 @@
   }
 
   void SetDBusClientAvailability(bool is_available) {
-    auto* client = static_cast<chromeos::FakeArcDataSnapshotdClient*>(
-        chromeos::ArcDataSnapshotdClient::Get());
+    auto* client = static_cast<ash::FakeArcDataSnapshotdClient*>(
+        ash::ArcDataSnapshotdClient::Get());
     DCHECK(client);
     client->set_available(is_available);
   }
@@ -313,9 +313,9 @@
     return session_controller_;
   }
 
-  chromeos::FakeArcDataSnapshotdClient* client() const {
-    return static_cast<chromeos::FakeArcDataSnapshotdClient*>(
-        chromeos::ArcDataSnapshotdClient::Get());
+  ash::FakeArcDataSnapshotdClient* client() const {
+    return static_cast<ash::FakeArcDataSnapshotdClient*>(
+        ash::ArcDataSnapshotdClient::Get());
   }
 
  protected:
diff --git a/ash/components/arc/midis/arc_midis_bridge.cc b/ash/components/arc/midis/arc_midis_bridge.cc
index 0914c248..3072f783 100644
--- a/ash/components/arc/midis/arc_midis_bridge.cc
+++ b/ash/components/arc/midis/arc_midis_bridge.cc
@@ -10,7 +10,7 @@
 #include "ash/components/arc/session/arc_bridge_service.h"
 #include "base/bind.h"
 #include "base/memory/singleton.h"
-#include "chromeos/dbus/arc/arc_midis_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_midis_client.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/system/invitation.h"
@@ -102,7 +102,7 @@
 
   midis_host_remote_.set_disconnect_handler(base::BindOnce(
       &ArcMidisBridge::OnMojoConnectionError, weak_factory_.GetWeakPtr()));
-  chromeos::ArcMidisClient::Get()->BootstrapMojoConnection(
+  ash::ArcMidisClient::Get()->BootstrapMojoConnection(
       channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD(),
       base::BindOnce(&ArcMidisBridge::OnBootstrapMojoConnection,
                      weak_factory_.GetWeakPtr(), std::move(receiver),
diff --git a/ash/components/arc/obb_mounter/arc_obb_mounter_bridge.cc b/ash/components/arc/obb_mounter/arc_obb_mounter_bridge.cc
index ac8a2e4..7d959e1 100644
--- a/ash/components/arc/obb_mounter/arc_obb_mounter_bridge.cc
+++ b/ash/components/arc/obb_mounter/arc_obb_mounter_bridge.cc
@@ -10,7 +10,7 @@
 #include "ash/components/arc/session/arc_bridge_service.h"
 #include "base/bind.h"
 #include "base/memory/singleton.h"
-#include "chromeos/dbus/arc/arc_obb_mounter_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_obb_mounter_client.h"
 
 namespace arc {
 
@@ -57,14 +57,13 @@
                                    const std::string& target_path,
                                    int32_t owner_gid,
                                    MountObbCallback callback) {
-  chromeos::ArcObbMounterClient::Get()->MountObb(
-      obb_file, target_path, owner_gid, std::move(callback));
+  ash::ArcObbMounterClient::Get()->MountObb(obb_file, target_path, owner_gid,
+                                            std::move(callback));
 }
 
 void ArcObbMounterBridge::UnmountObb(const std::string& target_path,
                                      UnmountObbCallback callback) {
-  chromeos::ArcObbMounterClient::Get()->UnmountObb(target_path,
-                                                   std::move(callback));
+  ash::ArcObbMounterClient::Get()->UnmountObb(target_path, std::move(callback));
 }
 
 }  // namespace arc
diff --git a/ash/components/arc/sensor/arc_sensor_bridge.cc b/ash/components/arc/sensor/arc_sensor_bridge.cc
index 17905089..160002a 100644
--- a/ash/components/arc/sensor/arc_sensor_bridge.cc
+++ b/ash/components/arc/sensor/arc_sensor_bridge.cc
@@ -13,7 +13,7 @@
 #include "base/memory/singleton.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "chromeos/dbus/arc/arc_sensor_service_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_sensor_service_client.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/system/invitation.h"
 #include "mojo/public/cpp/system/platform_handle.h"
@@ -78,7 +78,7 @@
   }
   base::ScopedFD fd =
       channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD();
-  chromeos::ArcSensorServiceClient::Get()->BootstrapMojoConnection(
+  ash::ArcSensorServiceClient::Get()->BootstrapMojoConnection(
       fd.get(), token, base::BindOnce([](bool success) {}));
 }
 
diff --git a/ash/components/disks/disk_mount_manager.cc b/ash/components/disks/disk_mount_manager.cc
index 119785472..8074da89 100644
--- a/ash/components/disks/disk_mount_manager.cc
+++ b/ash/components/disks/disk_mount_manager.cc
@@ -27,7 +27,6 @@
 #include "base/observer_list.h"
 #include "base/strings/string_util.h"
 #include "chromeos/dbus/cros_disks/cros_disks_client.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 
 namespace ash::disks {
 namespace {
@@ -70,8 +69,7 @@
                              public CrosDisksClient::Observer {
  public:
   DiskMountManagerImpl() {
-    DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
-    cros_disks_client_ = dbus_thread_manager->GetCrosDisksClient();
+    cros_disks_client_ = chromeos::CrosDisksClient::Get();
     suspend_unmount_manager_ = std::make_unique<SuspendUnmountManager>(this);
 
     cros_disks_client_->AddObserver(this);
diff --git a/ash/components/disks/disk_mount_manager_unittest.cc b/ash/components/disks/disk_mount_manager_unittest.cc
index 88084401..4d74362a 100644
--- a/ash/components/disks/disk_mount_manager_unittest.cc
+++ b/ash/components/disks/disk_mount_manager_unittest.cc
@@ -526,10 +526,10 @@
   // Initializes disk mount manager disks and mount points.
   // Adds a test observer to the disk mount manager.
   void SetUp() override {
-    fake_cros_disks_client_ = new FakeCrosDisksClient;
     DBusThreadManager::Initialize();
-    DBusThreadManager::GetSetterForTesting()->SetCrosDisksClient(
-        std::unique_ptr<CrosDisksClient>(fake_cros_disks_client_));
+    CrosDisksClient::InitializeFake();
+    fake_cros_disks_client_ =
+        static_cast<FakeCrosDisksClient*>(CrosDisksClient::Get());
     PowerManagerClient::InitializeFake();
 
     DiskMountManager::Initialize();
@@ -546,6 +546,7 @@
     DiskMountManager::GetInstance()->RemoveObserver(observer_.get());
     DiskMountManager::Shutdown();
     PowerManagerClient::Shutdown();
+    CrosDisksClient::Shutdown();
     DBusThreadManager::Shutdown();
   }
 
diff --git a/ash/components/drivefs/fake_drivefs.cc b/ash/components/drivefs/fake_drivefs.cc
index 4084844..225c738a 100644
--- a/ash/components/drivefs/fake_drivefs.cc
+++ b/ash/components/drivefs/fake_drivefs.cc
@@ -23,8 +23,8 @@
 #include "base/strings/string_util.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_restrictions.h"
+#include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/dbus/cros_disks/fake_cros_disks_client.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
@@ -267,10 +267,7 @@
 
 void FakeDriveFs::RegisterMountingForAccountId(
     base::RepeatingCallback<std::string()> account_id_getter) {
-  chromeos::DBusThreadManager* dbus_thread_manager =
-      chromeos::DBusThreadManager::Get();
-  static_cast<chromeos::FakeCrosDisksClient*>(
-      dbus_thread_manager->GetCrosDisksClient())
+  static_cast<chromeos::FakeCrosDisksClient*>(chromeos::CrosDisksClient::Get())
       ->AddCustomMountPointCallback(base::BindRepeating(&MaybeMountDriveFs));
 
   GetRegisteredFakeDriveFsIntances().emplace_back(std::move(account_id_getter),
diff --git a/ash/components/drivefs/fake_drivefs_launcher_client.cc b/ash/components/drivefs/fake_drivefs_launcher_client.cc
index bae1fca3..7fc82741 100644
--- a/ash/components/drivefs/fake_drivefs_launcher_client.cc
+++ b/ash/components/drivefs/fake_drivefs_launcher_client.cc
@@ -14,8 +14,8 @@
 #include "base/system/sys_info.h"
 #include "base/task/thread_pool.h"
 #include "chromeos/components/mojo_bootstrap/pending_connection_manager.h"
+#include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/dbus/cros_disks/fake_cros_disks_client.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
@@ -64,10 +64,7 @@
       base::BindOnce(&ConnectAsync, launcher_.BindNewPipeAndPassReceiver(),
                      socket_path_.value()));
 
-  chromeos::DBusThreadManager* dbus_thread_manager =
-      chromeos::DBusThreadManager::Get();
-  static_cast<chromeos::FakeCrosDisksClient*>(
-      dbus_thread_manager->GetCrosDisksClient())
+  static_cast<chromeos::FakeCrosDisksClient*>(chromeos::CrosDisksClient::Get())
       ->AddCustomMountPointCallback(
           base::BindRepeating(&FakeDriveFsLauncherClient::MaybeMountDriveFs,
                               base::Unretained(this)));
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index f6e0310..a174e53 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -53,6 +53,11 @@
 const base::Feature kAllowRepeatedUpdates{"AllowRepeatedUpdates",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Always reinstall system web apps, instead of only doing so after version
+// upgrade or locale changes.
+const base::Feature kAlwaysReinstallSystemWebApps{
+    "ReinstallSystemWebApps", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Shows settings for adjusting scroll acceleration/sensitivity for
 // mouse/touchpad.
 const base::Feature kAllowScrollSettings{"AllowScrollSettings",
@@ -635,6 +640,11 @@
 const base::Feature kEnableSavedDesks{"EnableSavedDesks",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables all registered system web apps, regardless of their respective
+// feature flags.
+const base::Feature kEnableAllSystemWebApps{"EnableAllSystemWebApps",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If enabled, touchpad cards will be shown in the diagnostics app's input
 // section.
 const base::Feature kEnableTouchpadsInDiagnosticsApp{
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 414613f..1477e50 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -29,6 +29,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kAllowRepeatedUpdates;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kAllowScrollSettings;
+COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kAlwaysReinstallSystemWebApps;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kAmbientModeFeature;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::FeatureParam<bool> kAmbientModeCapturedOnPixelAlbumEnabled;
@@ -273,6 +275,8 @@
 extern const base::Feature kEnableSamlReauthenticationOnLockscreen;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEnableSavedDesks;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kEnableAllSystemWebApps;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kEnableTouchpadsInDiagnosticsApp;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kEnableTouchscreensInDiagnosticsApp;
diff --git a/ash/constants/ash_pref_names.cc b/ash/constants/ash_pref_names.cc
index d19c7d5..1545e61 100644
--- a/ash/constants/ash_pref_names.cc
+++ b/ash/constants/ash_pref_names.cc
@@ -176,6 +176,11 @@
 const char kLauncherFeedbackOnContinueSectionSent[] =
     "ash.launcher.continue_section_removal_feedback_sent";
 
+// A time pref indicating the last time a request was made to update the
+// Continue section.
+const char kLauncherLastContinueRequestTime[] =
+    "launcher.last_continue_request_time";
+
 // Boolean pref recording whether a search result has ever been launched from
 // the Chrome OS launcher.
 const char kLauncherResultEverLaunched[] = "launcher.result_ever_launched";
@@ -185,6 +190,9 @@
 const char kLauncherSearchNormalizerParameters[] =
     "launcher.search_normalizer_parameters";
 
+// Whether or not to use a long delay for Continue section requests.
+const char kLauncherUseLongContinueDelay[] = "launcher.use_long_continue_delay";
+
 // Boolean pref indicating whether system-wide trace collection using the
 // Perfetto system tracing service is allowed.
 const char kDeviceSystemWideTracingEnabled[] =
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index e9535cf4..298f628d 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -75,10 +75,14 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kLauncherFeedbackOnContinueSectionSent[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kLauncherLastContinueRequestTime[];
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kLauncherResultEverLaunched[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kLauncherSearchNormalizerParameters[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kLauncherUseLongContinueDelay[];
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kDeviceSystemWideTracingEnabled[];
 
 COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
index 662849f..c7e49c1 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
@@ -198,6 +198,10 @@
     "SubsequentPairingProtocol";
 const char kSavedDeviceGetDevicesResult[] =
     "Bluetooth.ChromeOS.FastPair.SavedDevices.GetSavedDevices.Result";
+const char kSavedDevicesTotalUxLoadTime[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.TotalUxLoadTime";
+const char kSavedDevicesCount[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.DeviceCount";
 
 }  // namespace
 
@@ -577,5 +581,14 @@
   base::UmaHistogramBoolean(kSavedDeviceGetDevicesResult, success);
 }
 
+void RecordSavedDevicesTotalUxLoadTime(base::TimeDelta total_load_time) {
+  base::UmaHistogramCustomTimes(kSavedDevicesTotalUxLoadTime, total_load_time,
+                                base::Milliseconds(1), base::Seconds(25), 50);
+}
+
+void RecordSavedDevicesCount(int num_devices) {
+  base::UmaHistogramCounts100(kSavedDevicesCount, num_devices);
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
index e3c6eda..40dbca11 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
@@ -270,6 +270,12 @@
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 void RecordGetSavedDevicesResult(bool success);
 
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordSavedDevicesTotalUxLoadTime(base::TimeDelta total_load_time);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordSavedDevicesCount(int num_devices);
+
 }  // namespace quick_pair
 }  // namespace ash
 
diff --git a/ash/system/keyboard_brightness/keyboard_backlight_color_controller.cc b/ash/system/keyboard_brightness/keyboard_backlight_color_controller.cc
index 1f0ae0af..ad8301fc 100644
--- a/ash/system/keyboard_brightness/keyboard_backlight_color_controller.cc
+++ b/ash/system/keyboard_brightness/keyboard_backlight_color_controller.cc
@@ -86,6 +86,11 @@
 void KeyboardBacklightColorController::OnActiveUserPrefServiceChanged(
     PrefService* pref_service) {
   const auto backlight_color = GetBacklightColor(GetActiveAccountId());
+  // Wallpaper extracted color may not be available at this state. Instead of
+  // setting wallpaper color here, let |OnWallpaperColorsChanged| handles it
+  // when the color is available.
+  if (backlight_color == personalization_app::mojom::BacklightColor::kWallpaper)
+    return;
   DisplayBacklightColor(backlight_color);
 }
 
diff --git a/ash/system/keyboard_brightness/keyboard_backlight_color_controller_unittest.cc b/ash/system/keyboard_brightness/keyboard_backlight_color_controller_unittest.cc
index 2015d7cf9..de9242f69 100644
--- a/ash/system/keyboard_brightness/keyboard_backlight_color_controller_unittest.cc
+++ b/ash/system/keyboard_brightness/keyboard_backlight_color_controller_unittest.cc
@@ -114,24 +114,27 @@
   SimulateUserLogin(account_id_1);
   EXPECT_EQ(personalization_app::mojom::BacklightColor::kWallpaper,
             controller_->GetBacklightColor(account_id_1));
-  // Expect the Wallpaper color to be set after user signs in.
+  // Expect the Wallpaper color to be not set.
   histogram_tester().ExpectBucketCount(
-      "Ash.Personalization.KeyboardBacklight.WallpaperColor.Valid", false, 1);
-  histogram_tester().ExpectTotalCount(
-      "Ash.Personalization.KeyboardBacklight.WallpaperColor.Valid", 1);
+      "Ash.Personalization.KeyboardBacklight.WallpaperColor.Valid", false, 0);
+  EXPECT_EQ(SK_ColorTRANSPARENT, displayed_color());
+
   controller_->SetBacklightColor(
-      personalization_app::mojom::BacklightColor::kRainbow, account_id_1);
-  EXPECT_EQ(personalization_app::mojom::BacklightColor::kRainbow,
+      personalization_app::mojom::BacklightColor::kBlue, account_id_1);
+  ClearLogin();
+
+  // Simulate re-login for user1 and expect blue color to be set.
+  SimulateUserLogin(account_id_1);
+  EXPECT_EQ(ConvertBacklightColorToSkColor(
+                personalization_app::mojom::BacklightColor::kBlue),
+            displayed_color());
+  EXPECT_EQ(personalization_app::mojom::BacklightColor::kBlue,
             controller_->GetBacklightColor(account_id_1));
 
   // Simulate login for user2.
   SimulateUserLogin(account_id_2);
   EXPECT_EQ(personalization_app::mojom::BacklightColor::kWallpaper,
             controller_->GetBacklightColor(account_id_2));
-
-  SimulateUserLogin(account_id_1);
-  EXPECT_EQ(personalization_app::mojom::BacklightColor::kRainbow,
-            controller_->GetBacklightColor(account_id_1));
 }
 
 TEST_F(KeyboardBacklightColorControllerTest,
diff --git a/ash/system/network/managed_sim_lock_notifier.cc b/ash/system/network/managed_sim_lock_notifier.cc
index 7535c65d..68f93cc 100644
--- a/ash/system/network/managed_sim_lock_notifier.cc
+++ b/ash/system/network/managed_sim_lock_notifier.cc
@@ -165,10 +165,6 @@
   // When clicked, open the SIM Unlock dialog in Cellular settings if
   // we can open WebUI settings, otherwise do nothing.
   if (TrayPopupUtils::CanOpenWebUISettings()) {
-    // TODO(b/228093904): Using GUID of network, take user to cellular
-    // details page with dialog open. Change dialog logic so that if
-    // the SIM is currently locked, entering the PIN will unlock the
-    // SIM and disable the PIN lock setting.
     Shell::Get()->system_tray_model()->client()->ShowSettingsSimUnlock();
   } else {
     LOG(WARNING) << "Cannot open Cellular settings since it's not "
diff --git a/ash/system/time/calendar_month_view.cc b/ash/system/time/calendar_month_view.cc
index 167d66a..a70de8e 100644
--- a/ash/system/time/calendar_month_view.cc
+++ b/ash/system/time/calendar_month_view.cc
@@ -89,6 +89,7 @@
       grayed_out_(is_grayed_out_date),
       row_index_(row_index),
       is_fetched_(is_fetched),
+      is_today_(calendar_utils::IsToday(date)),
       time_difference_(time_difference),
       calendar_view_controller_(calendar_view_controller) {
   SetHorizontalAlignment(gfx::ALIGN_CENTER);
@@ -150,7 +151,7 @@
     canvas->DrawCircle(center, kBorderRadius, highlight_border);
   }
 
-  if (calendar_utils::IsToday(date_)) {
+  if (is_today_) {
     cc::PaintFlags highlight_background;
     highlight_background.setColor(bg_color);
     highlight_background.setStyle(cc::PaintFlags::kFill_Style);
@@ -254,6 +255,8 @@
       return;
 
     event_number_ = event_number;
+    if (is_today_)
+      calendar_view_controller_->OnTodaysEventFetchComplete();
   }
 
   is_fetched_ = is_fetched;
@@ -272,7 +275,7 @@
     return;
 
   const AshColorProvider* color_provider = AshColorProvider::Get();
-  if (calendar_utils::IsToday(date_)) {
+  if (is_today_) {
     const SkColor text_color = color_provider->GetContentLayerColor(
         AshColorProvider::ContentLayerType::kButtonLabelColorPrimary);
     SetEnabledTextColors(text_color);
@@ -317,11 +320,10 @@
     return;
 
   const SkColor indicator_color =
-      calendar_utils::IsToday(date_)
-          ? AshColorProvider::Get()->GetBaseLayerColor(
-                AshColorProvider::BaseLayerType::kTransparent90)
-          : AshColorProvider::Get()->GetControlsLayerColor(
-                AshColorProvider::ControlsLayerType::kFocusRingColor);
+      is_today_ ? AshColorProvider::Get()->GetBaseLayerColor(
+                      AshColorProvider::BaseLayerType::kTransparent90)
+                : AshColorProvider::Get()->GetControlsLayerColor(
+                      AshColorProvider::ControlsLayerType::kFocusRingColor);
 
   const float indicator_radius = is_selected_ ? kEventsPresentRoundedRadius * 2
                                               : kEventsPresentRoundedRadius;
@@ -404,13 +406,13 @@
     }
     // If this row has today, updates today's row number and replaces today to
     // the last element in the `focused_cells_`.
-    if (calendar_utils::IsToday(current_date)) {
+    if (cell->is_today()) {
       calendar_view_controller_->set_row_height(
           cell->GetPreferredSize().height());
       calendar_view_controller_->set_today_row(row_number);
       focused_cells_.back() = cell;
-      DCHECK(calendar_view_controller_->todays_date_cell_view() == nullptr);
       has_today_ = true;
+      DCHECK(calendar_view_controller_->todays_date_cell_view() == nullptr);
       calendar_view_controller_->set_todays_date_cell_view(cell);
     }
     MoveToNextDay(column, current_date, current_date_local,
diff --git a/ash/system/time/calendar_month_view.h b/ash/system/time/calendar_month_view.h
index 53ab0bb..c25a34b 100644
--- a/ash/system/time/calendar_month_view.h
+++ b/ash/system/time/calendar_month_view.h
@@ -66,6 +66,10 @@
   // The row index in the date's month view.
   int row_index() const { return row_index_; }
 
+  // Whether this CalendarDateCellView represents today, when the view was
+  // constructed.
+  bool is_today() const { return is_today_; }
+
  protected:
   // views::Button:
   void PaintButtonContents(gfx::Canvas* canvas) override;
@@ -97,6 +101,10 @@
   // If the current cell is selected.
   bool is_selected_ = false;
 
+  // Whether this CalendarDateCellView represented today when the view was
+  // constructed.
+  const bool is_today_;
+
   // The number of event for `date_`.
   int event_number_ = 0;
 
diff --git a/ash/system/time/calendar_view_controller.cc b/ash/system/time/calendar_view_controller.cc
index deeed1f..65dd2ae 100644
--- a/ash/system/time/calendar_view_controller.cc
+++ b/ash/system/time/calendar_view_controller.cc
@@ -216,6 +216,17 @@
   user_journey_time_recorded_ = true;
 }
 
+void CalendarViewController::OnTodaysEventFetchComplete() {
+  // Only record this once per lifetime of the CalendarView (and therefore the
+  // controller).
+  if (todays_date_cell_fetch_recorded_)
+    return;
+
+  UmaHistogramMediumTimes("Ash.Calendar.TimeToSeeTodaysEventDots",
+                          base::TimeTicks::Now() - calendar_open_time_);
+  todays_date_cell_fetch_recorded_ = true;
+}
+
 bool CalendarViewController::IsSelectedDateInCurrentMonth() {
   if (!selected_date_.has_value())
     return false;
diff --git a/ash/system/time/calendar_view_controller.h b/ash/system/time/calendar_view_controller.h
index 34425933..9d150a6 100644
--- a/ash/system/time/calendar_view_controller.h
+++ b/ash/system/time/calendar_view_controller.h
@@ -73,6 +73,9 @@
   // Called when a calendar event is about to launch. Used to record metrics.
   void OnCalendarEventWillLaunch();
 
+  // Called when the CalendarDateCellView representing today gets a fetch.
+  void OnTodaysEventFetchComplete();
+
   // If the selected date in the current month. This is used to inform the
   // `CalendarView` if the month should be updated when a date is selected.
   bool IsSelectedDateInCurrentMonth();
@@ -200,6 +203,10 @@
   // event is launched, or when this (which is owned by the view) is destroyed.
   bool user_journey_time_recorded_ = false;
 
+  // Whether the metric for recording time to show the user the first fetch of
+  // events has been recorded.
+  bool todays_date_cell_fetch_recorded_ = false;
+
   // The currently selected date.
   absl::optional<base::Time> selected_date_;
 
diff --git a/ash/system/tray/system_nudge.cc b/ash/system/tray/system_nudge.cc
index ae3d38c..2770f00 100644
--- a/ash/system/tray/system_nudge.cc
+++ b/ash/system/tray/system_nudge.cc
@@ -13,6 +13,7 @@
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
 #include "base/i18n/rtl.h"
+#include "base/memory/weak_ptr.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -87,11 +88,12 @@
 
 class SystemNudge::SystemNudgeView : public views::View {
  public:
-  explicit SystemNudgeView(const SystemNudge* nudge) : nudge_(nudge) {
+  explicit SystemNudgeView(base::WeakPtr<SystemNudge> nudge) {
+    DCHECK(nudge);
     auto layout = std::make_unique<views::BoxLayout>(
         views::BoxLayout::Orientation::kHorizontal,
-        /*inside_border_insect=*/gfx::Insets(nudge_->params_.nudge_padding),
-        /*between_child_spacing=*/nudge_->params_.icon_label_spacing);
+        /*inside_border_insect=*/gfx::Insets(nudge->params_.nudge_padding),
+        /*between_child_spacing=*/nudge->params_.icon_label_spacing);
     layout->set_cross_axis_alignment(
         views::BoxLayout::CrossAxisAlignment::kStart);
     SetLayoutManager(std::move(layout));
@@ -104,17 +106,21 @@
     icon_ = AddChildView(std::make_unique<views::ImageView>());
     icon_->SetPaintToLayer();
     icon_->layer()->SetFillsBoundsOpaquely(false);
-    icon_->SetSize({nudge_->params_.icon_size, nudge_->params_.icon_size});
+    icon_->SetSize({nudge->params_.icon_size, nudge->params_.icon_size});
     icon_->SetImage(ui::ImageModel::FromImageGenerator(
         base::BindRepeating(
-            [](const SystemNudge* nudge, const ui::ColorProvider*) {
+            [](base::WeakPtr<SystemNudge> nudge, const ui::ColorProvider*) {
+              // If `nudge` does not exist anymore, no image will be displayed.
+              if (!nudge)
+                return gfx::ImageSkia();
+
               return gfx::CreateVectorIcon(
                   nudge->GetIcon(),
                   AshColorProvider::Get()->GetContentLayerColor(
                       nudge->params_.icon_color_layer_type));
             },
-            nudge_),
-        gfx::Size(nudge_->params_.icon_size, nudge_->params_.icon_size)));
+            nudge),
+        gfx::Size(nudge->params_.icon_size, nudge->params_.icon_size)));
     label_ = AddChildView(nudge->CreateLabelView());
     label_->SetPaintToLayer();
     label_->layer()->SetFillsBoundsOpaquely(false);
@@ -128,7 +134,6 @@
     layer()->SetColor(ShelfConfig::Get()->GetDefaultShelfColor());
   }
 
-  const SystemNudge* const nudge_;
   views::View* label_ = nullptr;
   views::ImageView* icon_ = nullptr;
 };
@@ -186,7 +191,7 @@
   }
 
   nudge_view_ = widget_->SetContentsView(
-      std::make_unique<SystemNudgeView>(/*nudge=*/this));
+      std::make_unique<SystemNudgeView>(/*nudge=*/weak_factory_.GetWeakPtr()));
   CalculateAndSetWidgetBounds();
   widget_->Show();
 
diff --git a/ash/system/tray/system_nudge.h b/ash/system/tray/system_nudge.h
index 12fe822..d2a6789 100644
--- a/ash/system/tray/system_nudge.h
+++ b/ash/system/tray/system_nudge.h
@@ -15,6 +15,7 @@
 #include "ash/shell_observer.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/tray/system_nudge_label.h"
+#include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/widget/unique_widget_ptr.h"
@@ -117,6 +118,8 @@
                           &Shell::AddShellObserver,
                           &Shell::RemoveShellObserver>
       shell_observation_{this};
+
+  base::WeakPtrFactory<SystemNudge> weak_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts b/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts
index c76656c..e7c4d626 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts
@@ -72,7 +72,8 @@
     case WallpaperActionName.SET_COLLECTIONS:
       const {collections} = action;
       if (!isNonEmptyArray(collections)) {
-        return state || {message: loadTimeData.getString('networkError')};
+        return state ||
+            {message: loadTimeData.getString('wallpaperNetworkError')};
       }
       return state;
     case PersonalizationActionName.SET_ERROR:
diff --git a/ash/wm/desks/templates/saved_desk_unittest.cc b/ash/wm/desks/templates/saved_desk_unittest.cc
index 7cbe6c3..39e63519 100644
--- a/ash/wm/desks/templates/saved_desk_unittest.cc
+++ b/ash/wm/desks/templates/saved_desk_unittest.cc
@@ -20,9 +20,11 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/close_button.h"
 #include "ash/style/pill_button.h"
+#include "ash/wm/desks/desk_action_view.h"
 #include "ash/wm/desks/desk_mini_view.h"
 #include "ash/wm/desks/desk_name_view.h"
 #include "ash/wm/desks/desks_bar_view.h"
+#include "ash/wm/desks/desks_test_api.h"
 #include "ash/wm/desks/desks_test_util.h"
 #include "ash/wm/desks/expanded_desks_bar_button.h"
 #include "ash/wm/desks/templates/save_desk_template_button.h"
@@ -367,7 +369,7 @@
 
     auto* save_template_button = GetSaveDeskAsTemplateButtonForRoot(root);
     ASSERT_TRUE(
-        GetOverviewGridForRoot(root)->IsSaveDeskAsTemplateButtonVisible());
+        GetOverviewGridForRoot(root)->IsSaveDeskButtonContainerVisible());
     ClickOnView(save_template_button);
     WaitForDesksTemplatesUI();
     WaitForLibraryUI();
@@ -407,7 +409,9 @@
   // OverviewTestBase:
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures(
-        {features::kDesksTemplates, features::kEnableSavedDesks}, {});
+        {features::kDesksTemplates, features::kEnableSavedDesks,
+         features::kDesksCloseAll},
+        {});
     OverviewTestBase::SetUp();
 
     // The `FullRestoreSaveHandler` isn't setup during tests so every window we
@@ -728,7 +732,7 @@
   const auto* desks_bar_view = overview_grid->desks_bar_view();
   auto* mini_view =
       desks_bar_view->FindMiniViewForDesk(desks_controller->active_desk());
-  ClickOnView(mini_view->close_desk_button());
+  ClickOnView(mini_view->desk_action_view()->combine_desks_button());
 
   // Expect we stay in the templates grid.
   ASSERT_TRUE(overview_grid->IsShowingDesksTemplatesGrid());
@@ -1868,7 +1872,7 @@
   // Close one of the desks. Test that we remain in expanded state.
   auto* mini_view = desks_bar_view->FindMiniViewForDesk(
       DesksController::Get()->active_desk());
-  ClickOnView(mini_view->close_desk_button());
+  ClickOnView(mini_view->desk_action_view()->close_all_button());
   auto* expanded_new_desk_button =
       desks_bar_view->expanded_state_new_desk_button();
   auto* expanded_templates_button =
@@ -3867,6 +3871,56 @@
             item_view->GetVisibleBounds().size());
 }
 
+// Tests that the save desk button is hidden when an active desk with windows is
+// closed and a desk with no windows is activated. Then checks to see that the
+// visibility is restored for the button when desk removal is undone.
+TEST_F(SavedDeskTest,
+       CorrectlyUpdateSaveDeskButtonVisibilityOnActiveDeskClose) {
+  auto* controller = DesksController::Get();
+  const auto& desks = controller->desks();
+
+  // We create a new desk and add an app window to the first desk so that
+  // closing the first desk with its windows will result in us going from having
+  // app windows in overview to having no app windows in overview, which should
+  // cause an update to the visibility of the save desk buttons.
+  controller->NewDesk(DesksCreationRemovalSource::kKeyboard);
+  ASSERT_EQ(2u, desks.size());
+  Desk* active_desk = desks[0].get();
+  Desk* inactive_desk = desks[1].get();
+  ASSERT_TRUE(active_desk->is_active());
+  ASSERT_FALSE(active_desk->ContainsAppWindows());
+  ASSERT_FALSE(inactive_desk->ContainsAppWindows());
+  const auto& window = CreateAppWindow();
+  controller->SendToDeskAtIndex(window.get(), 0);
+  ASSERT_EQ(1u, active_desk->GetAllAppWindows().size());
+
+  ToggleOverview();
+  OverviewGrid* overview_grid = GetOverviewSession()->GetGridWithRootWindow(
+      Shell::GetPrimaryRootWindow());
+
+  // Pre-check whether the save desk button is in the correct state.
+  EXPECT_TRUE(overview_grid->IsSaveDeskButtonContainerVisible());
+
+  const DesksBarView* desks_bar_view = overview_grid->desks_bar_view();
+  ASSERT_EQ(2u, desks_bar_view->mini_views().size());
+  DeskMiniView* mini_view_to_be_removed =
+      desks_bar_view->FindMiniViewForDesk(active_desk);
+  ASSERT_TRUE(mini_view_to_be_removed);
+
+  // Close the active desk and check that the save desk button updates
+  // correctly.
+  ClickOnView(mini_view_to_be_removed->desk_action_view()->close_all_button());
+  EXPECT_FALSE(overview_grid->IsSaveDeskButtonContainerVisible());
+
+  // Try undoing desk close to see if the save desk button returns to the right
+  // state,
+  views::Button* undo_button =
+      DesksTestApi::GetCloseAllUndoToastDismissButton();
+  ASSERT_TRUE(undo_button);
+  ClickOnView(undo_button);
+  EXPECT_TRUE(overview_grid->IsSaveDeskButtonContainerVisible());
+}
+
 using DeskSaveAndRecallTest = SavedDeskTest;
 
 TEST_F(DeskSaveAndRecallTest, SaveDeskForLater) {
@@ -3998,43 +4052,4 @@
   histogram_tester.ExpectTotalCount(kDeleteSaveAndRecallHistogramName, 1);
 }
 
-// Tests that if we've been in the library, then switched to a different desk,
-// and then save the desk, that the desk is closed. Regression test for
-// https://crbug.com/1329350.
-TEST_F(DeskSaveAndRecallTest, ReEnterLibraryAndSaveDesk) {
-  UpdateDisplay("800x600");
-
-  auto* root = Shell::Get()->GetPrimaryRootWindow();
-
-  // Create a template that has a window. We can't use `AddEntry` here since we
-  // want a template that actually contains a window. The "Save desk for later"
-  // button is not enabled on empty desks.
-  auto test_window1 = CreateAppWindow();
-
-  OpenOverviewAndSaveTemplate(root);
-
-  DesksController* desks_controller = DesksController::Get();
-  EXPECT_EQ(1ul, desks_controller->desks().size());
-
-  // Click on the "Use template" button to launch the template.
-  SavedDeskItemView* item_view = GetItemViewFromTemplatesGrid(0);
-  ClickOnView(SavedDeskItemViewTestApi(item_view).launch_button());
-  WaitForDesksTemplatesUI();
-
-  // Verify that we're still in overview mode and that a new desk has been
-  // created and activated.
-  EXPECT_TRUE(InOverviewSession());
-  EXPECT_EQ(2ul, desks_controller->desks().size());
-  EXPECT_EQ(1, desks_controller->GetActiveDeskIndex());
-
-  // Now save the desk. This should close the desk.
-  auto* save_desk_button = GetSaveDeskForLaterButtonForRoot(root);
-  EXPECT_TRUE(save_desk_button);
-  ClickOnView(save_desk_button);
-  WaitForDesksTemplatesUI();
-
-  // Verify that we're back to one desk.
-  EXPECT_EQ(1ul, desks_controller->desks().size());
-}
-
 }  // namespace ash
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 840ba4d1..3a9bf962 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -1133,6 +1133,9 @@
   for (auto* root : Shell::GetAllRootWindows()) {
     activated->GetDeskContainerForRoot(root)->AddObserver(this);
     deactivated->GetDeskContainerForRoot(root)->RemoveObserver(this);
+
+    if (auto* overview_grid = GetGridWithRootWindow(root))
+      overview_grid->UpdateSaveDeskButtons();
   }
 }
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index e3adc4f..5b0f17f 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -508,7 +508,6 @@
     "native_library.cc",
     "native_library.h",
     "no_destructor.h",
-    "notreached.cc",
     "notreached.h",
     "observer_list.h",
     "observer_list_internal.cc",
@@ -562,7 +561,6 @@
     "profiler/metadata_recorder.h",
     "profiler/module_cache.cc",
     "profiler/module_cache.h",
-    "profiler/native_unwinder.h",
     "profiler/profile_builder.h",
     "profiler/register_context.h",
     "profiler/sample_metadata.cc",
@@ -1277,9 +1275,9 @@
       "process/process_iterator_mac.cc",
       "process/process_mac.cc",
       "process/process_metrics_mac.cc",
+      "profiler/frame_pointer_unwinder.cc",
+      "profiler/frame_pointer_unwinder.h",
       "profiler/module_cache_mac.cc",
-      "profiler/native_unwinder_apple.cc",
-      "profiler/native_unwinder_apple.h",
       "profiler/stack_sampler_mac.cc",
       "profiler/suspendable_thread_delegate_mac.cc",
       "profiler/suspendable_thread_delegate_mac.h",
@@ -1325,8 +1323,8 @@
 
     if (ios_stack_profiler_enabled) {
       sources += [
-        "profiler/native_unwinder_apple.cc",
-        "profiler/native_unwinder_apple.h",
+        "profiler/frame_pointer_unwinder.cc",
+        "profiler/frame_pointer_unwinder.h",
         "profiler/suspendable_thread_delegate_mac.cc",
         "profiler/suspendable_thread_delegate_mac.h",
       ]
@@ -1685,7 +1683,6 @@
       "android/reached_code_profiler.cc",
       "android/reached_code_profiler.h",
       "android/record_histogram.cc",
-      "android/record_user_action.cc",
       "android/remove_stale_data.cc",
       "android/remove_stale_data.h",
       "android/scoped_hardware_buffer_fence_sync.cc",
@@ -1775,6 +1772,12 @@
     if (is_chromeos_ash) {
       sources += [ "power_monitor/power_monitor_device_source_chromeos.cc" ]
     }
+    if (current_cpu == "x64") {
+      sources += [
+        "profiler/frame_pointer_unwinder.cc",
+        "profiler/frame_pointer_unwinder.h",
+      ]
+    }
   }
 
   # Fuchsia.
@@ -2756,7 +2759,7 @@
       "allocator/partition_allocator/partition_alloc_perftest.cc",
       "allocator/partition_allocator/partition_lock_perftest.cc",
     ]
-    deps += [ "allocator/partition_allocator:test_support" ]
+    deps += [ ":partition_alloc_test_support" ]
   }
 
   data_deps = [
@@ -2841,7 +2844,8 @@
 }
 
 if ((is_win && (current_cpu == "x64" || current_cpu == "arm64")) || is_mac ||
-    (is_android && (current_cpu == "arm" || current_cpu == "arm64"))) {
+    (is_android && (current_cpu == "arm" || current_cpu == "arm64")) ||
+    (is_chromeos && current_cpu == "x64")) {
   # Must be a loadable module so that it can be loaded/unloaded at runtime
   # during testing.
   loadable_module("base_profiler_test_support_library") {
@@ -3712,7 +3716,7 @@
     } else if (is_fuchsia) {
       sources += [ "allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging_pa_unittest.cc" ]
     }
-    deps += [ "allocator/partition_allocator:test_support" ]
+    deps += [ ":partition_alloc_test_support" ]
   }
 
   if (is_mac) {
@@ -3831,7 +3835,11 @@
     sources += [ "files/os_validation_win_unittest.cc" ]
   }
   if (is_apple) {
-    sources += [ "profiler/native_unwinder_apple_unittest.cc" ]
+    sources += [ "profiler/frame_pointer_unwinder_unittest.cc" ]
+  }
+  if (is_chromeos && current_cpu == "x64") {
+    sources += [ "profiler/frame_pointer_unwinder_unittest.cc" ]
+    deps += [ ":base_profiler_test_support_library" ]
   }
 
   if (use_allocator_shim) {
@@ -3996,7 +4004,6 @@
       "android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
       "android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java",
       "android/java/src/org/chromium/base/metrics/RecordHistogram.java",
-      "android/java/src/org/chromium/base/metrics/RecordUserAction.java",
       "android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
       "android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
       "android/java/src/org/chromium/base/task/PostTask.java",
@@ -4403,7 +4410,6 @@
     sources = [
       "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/task/test/ShadowAsyncTask.java",
       "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/task/test/ShadowAsyncTaskBridge.java",
-      "android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserAction.java",
       "test/android/junit/src/org/chromium/base/task/test/BackgroundShadowAsyncTask.java",
       "test/android/junit/src/org/chromium/base/task/test/CustomShadowAsyncTask.java",
       "test/android/junit/src/org/chromium/base/task/test/ShadowPostTask.java",
@@ -4451,7 +4457,6 @@
       "android/junit/src/org/chromium/base/library_loader/LinkerTest.java",
       "android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java",
       "android/junit/src/org/chromium/base/metrics/CachingUmaRecorderTest.java",
-      "android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserActionTest.java",
       "android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java",
       "android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java",
       "android/junit/src/org/chromium/base/supplier/ObservableSupplierImplTest.java",
@@ -4629,3 +4634,62 @@
     "//base/test:test_support",
   ]
 }
+
+# TODO(1151236): Temporarily move test_support from
+# //base/allocator/partition_allocator/BUILD.gn to //base/BUILD.gn. After
+# fixing partition_allocator tests issue, we will move test_support to
+# //base/allocator/partition_allocator/BUILD.gn again.
+source_set("partition_alloc_test_support") {
+  testonly = true
+
+  sources = [
+    "allocator/partition_allocator/extended_api.cc",
+    "allocator/partition_allocator/extended_api.h",
+    "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h",
+  ]
+  if (is_posix) {
+    sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix_for_testing.cc" ]
+  }
+  if (is_fuchsia) {
+    sources += [
+      "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_fuchsia_for_testing.cc",
+      "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix_for_testing.cc",
+    ]
+  }
+  if (is_win) {
+    sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_win_for_testing.cc" ]
+  }
+  if (is_mac || is_ios) {
+    sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_mac_for_testing.mm" ]
+  }
+  if (is_linux || is_chromeos) {
+    sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_linux_for_testing.cc" ]
+  }
+  if (is_android) {
+    sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_android_for_testing.cc" ]
+  }
+  public_deps = [
+    "allocator/partition_allocator:buildflags",
+    "//build:branding_buildflags",
+    "//build/config/compiler:compiler_buildflags",
+  ]
+  public_configs = []
+  if (is_fuchsia) {
+    public_deps += [
+      "//third_party/fuchsia-sdk/sdk/pkg/fit",
+      "//third_party/fuchsia-sdk/sdk/pkg/sync",
+      "//third_party/fuchsia-sdk/sdk/pkg/zx",
+    ]
+
+    # Needed for users of spinning_mutex.h, which for performance reasons,
+    # contains inlined calls to `libsync` inside the header file.
+    # It appends an entry to the "libs" section of the dependent target.
+    public_configs += [ ":fuchsia_sync_lib" ]
+  }
+  deps = [ ":base" ]
+
+  if (!is_debug) {
+    configs -= [ "//build/config/compiler:default_optimization" ]
+    configs += [ "//build/config/compiler:optimize_speed" ]
+  }
+}
diff --git a/base/allocator/partition_allocator/BUILD.gn b/base/allocator/partition_allocator/BUILD.gn
index 0679229d..01d5ba0 100644
--- a/base/allocator/partition_allocator/BUILD.gn
+++ b/base/allocator/partition_allocator/BUILD.gn
@@ -322,71 +322,6 @@
   }
 }
 
-source_set("test_support") {
-  sources = [
-    "extended_api.cc",
-    "extended_api.h",
-    "partition_alloc_base/threading/platform_thread_for_testing.h",
-  ]
-  if (is_posix) {
-    sources += [
-      "partition_alloc_base/threading/platform_thread_posix_for_testing.cc",
-    ]
-  }
-  if (is_fuchsia) {
-    sources += [
-      "partition_alloc_base/threading/platform_thread_fuchsia_for_testing.cc",
-      "partition_alloc_base/threading/platform_thread_posix_for_testing.cc",
-    ]
-  }
-  if (is_win) {
-    sources +=
-        [ "partition_alloc_base/threading/platform_thread_win_for_testing.cc" ]
-  }
-  if (is_mac || is_ios) {
-    sources +=
-        [ "partition_alloc_base/threading/platform_thread_mac_for_testing.mm" ]
-  }
-  if (is_linux || is_chromeos) {
-    sources += [
-      "partition_alloc_base/threading/platform_thread_linux_for_testing.cc",
-    ]
-  }
-  if (is_android) {
-    sources += [
-      "partition_alloc_base/threading/platform_thread_android_for_testing.cc",
-    ]
-  }
-  public_deps = [
-    ":chromecast_buildflags",
-    ":chromeos_buildflags",
-    ":debugging_buildflags",
-    ":logging_buildflags",
-    ":partition_alloc_buildflags",
-    "//build:branding_buildflags",
-    "//build/config/compiler:compiler_buildflags",
-  ]
-  public_configs = []
-  if (is_fuchsia) {
-    public_deps += [
-      "//third_party/fuchsia-sdk/sdk/pkg/fit",
-      "//third_party/fuchsia-sdk/sdk/pkg/sync",
-      "//third_party/fuchsia-sdk/sdk/pkg/zx",
-    ]
-
-    # Needed for users of spinning_mutex.h, which for performance reasons,
-    # contains inlined calls to `libsync` inside the header file.
-    # It appends an entry to the "libs" section of the dependent target.
-    public_configs += [ ":fuchsia_sync_lib" ]
-  }
-  deps = [ "//base:base" ]
-
-  if (!is_debug) {
-    configs -= [ "//build/config/compiler:default_optimization" ]
-    configs += [ "//build/config/compiler:optimize_speed" ]
-  }
-}
-
 buildflag_header("partition_alloc_buildflags") {
   header = "partition_alloc_buildflags.h"
 
diff --git a/base/android/java/src/org/chromium/base/metrics/CachingUmaRecorder.java b/base/android/java/src/org/chromium/base/metrics/CachingUmaRecorder.java
index 7fe8b56..4d6caea 100644
--- a/base/android/java/src/org/chromium/base/metrics/CachingUmaRecorder.java
+++ b/base/android/java/src/org/chromium/base/metrics/CachingUmaRecorder.java
@@ -4,10 +4,13 @@
 
 package org.chromium.base.metrics;
 
+import android.annotation.SuppressLint;
+
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.Callback;
 import org.chromium.base.Log;
 
 import java.lang.annotation.Retention;
@@ -248,6 +251,10 @@
     @Nullable
     private UmaRecorder mDelegate;
 
+    @GuardedBy("mRwLock")
+    @Nullable
+    private List<Callback<String>> mUserActionCallbacksForTesting;
+
     /**
      * Sets the current delegate to {@code recorder}. Forwards and clears all cached metrics if
      * {@code recorder} is not {@code null}.
@@ -266,6 +273,7 @@
         try {
             previous = mDelegate;
             mDelegate = recorder;
+            swapUserActionCallbacksForTesting(previous, recorder);
             if (recorder == null) {
                 return previous;
             }
@@ -537,6 +545,11 @@
                     assert false : "Too many user actions in cache";
                     mDroppedUserActionCount++;
                 }
+                if (mUserActionCallbacksForTesting != null) {
+                    for (int i = 0; i < mUserActionCallbacksForTesting.size(); i++) {
+                        mUserActionCallbacksForTesting.get(i).onResult(name);
+                    }
+                }
                 return; // Skip the lock downgrade.
             }
             // Downgrade by acquiring read lock before releasing write lock
@@ -592,4 +605,53 @@
             mRwLock.readLock().unlock();
         }
     }
+
+    @VisibleForTesting
+    @Override
+    public void addUserActionCallbackForTesting(Callback<String> callback) {
+        mRwLock.writeLock().lock();
+        try {
+            if (mUserActionCallbacksForTesting == null) {
+                mUserActionCallbacksForTesting = new ArrayList<>();
+            }
+            mUserActionCallbacksForTesting.add(callback);
+            if (mDelegate != null) mDelegate.addUserActionCallbackForTesting(callback);
+        } finally {
+            mRwLock.writeLock().unlock();
+        }
+    }
+
+    @VisibleForTesting
+    @Override
+    public void removeUserActionCallbackForTesting(Callback<String> callback) {
+        mRwLock.writeLock().lock();
+        try {
+            if (mUserActionCallbacksForTesting == null) {
+                assert false : "Attempting to remove a user action callback without previously "
+                               + "registering any.";
+                return;
+            }
+            mUserActionCallbacksForTesting.remove(callback);
+            if (mDelegate != null) mDelegate.removeUserActionCallbackForTesting(callback);
+        } finally {
+            mRwLock.writeLock().unlock();
+        }
+    }
+
+    @SuppressLint("VisibleForTests")
+    @GuardedBy("mRwLock")
+    private void swapUserActionCallbacksForTesting(
+            @Nullable UmaRecorder previousRecorder, @Nullable UmaRecorder newRecorder) {
+        if (mUserActionCallbacksForTesting == null) return;
+
+        for (int i = 0; i < mUserActionCallbacksForTesting.size(); i++) {
+            if (previousRecorder != null) {
+                previousRecorder.removeUserActionCallbackForTesting(
+                        mUserActionCallbacksForTesting.get(i));
+            }
+            if (newRecorder != null) {
+                newRecorder.addUserActionCallbackForTesting(mUserActionCallbacksForTesting.get(i));
+            }
+        }
+    }
 }
diff --git a/base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java b/base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java
index 18ba5fd..d2157c38 100644
--- a/base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java
+++ b/base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java
@@ -4,6 +4,7 @@
 
 package org.chromium.base.metrics;
 
+import org.chromium.base.Callback;
 import org.chromium.base.TimeUtils;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.MainDex;
@@ -30,6 +31,7 @@
      */
     private final Map<String, Long> mNativeHints =
             Collections.synchronizedMap(new HashMap<String, Long>());
+    private Map<Callback<String>, Long> mUserActionTestingCallbackNativePtrs;
 
     @Override
     public void recordBooleanHistogram(String name, boolean sample) {
@@ -79,6 +81,31 @@
         return NativeUmaRecorderJni.get().getHistogramTotalCountForTesting(name, 0);
     }
 
+    @Override
+    public void addUserActionCallbackForTesting(Callback<String> callback) {
+        long ptr = NativeUmaRecorderJni.get().addActionCallbackForTesting(callback);
+        if (mUserActionTestingCallbackNativePtrs == null) {
+            mUserActionTestingCallbackNativePtrs = Collections.synchronizedMap(new HashMap<>());
+        }
+        mUserActionTestingCallbackNativePtrs.put(callback, ptr);
+    }
+
+    @Override
+    public void removeUserActionCallbackForTesting(Callback<String> callback) {
+        if (mUserActionTestingCallbackNativePtrs == null) {
+            assert false
+                : "Attempting to remove a user action callback without previously registering any.";
+            return;
+        }
+        Long ptr = mUserActionTestingCallbackNativePtrs.remove(callback);
+        if (ptr == null) {
+            assert false : "Attempting to remove a user action callback that was never previously"
+                           + " registered.";
+            return;
+        }
+        NativeUmaRecorderJni.get().removeActionCallbackForTesting(ptr);
+    }
+
     private long getNativeHint(String name) {
         Long hint = mNativeHints.get(name);
         // Note: If key is null, we don't have it cached. In that case, pass 0
@@ -117,5 +144,8 @@
 
         int getHistogramValueCountForTesting(String name, int sample, long snapshotPtr);
         int getHistogramTotalCountForTesting(String name, long snapshotPtr);
+
+        long addActionCallbackForTesting(Callback<String> callback);
+        void removeActionCallbackForTesting(long callbackId);
     }
 }
diff --git a/base/android/java/src/org/chromium/base/metrics/NoopUmaRecorder.java b/base/android/java/src/org/chromium/base/metrics/NoopUmaRecorder.java
index 74bb57d8..0fa27e6 100644
--- a/base/android/java/src/org/chromium/base/metrics/NoopUmaRecorder.java
+++ b/base/android/java/src/org/chromium/base/metrics/NoopUmaRecorder.java
@@ -4,6 +4,8 @@
 
 package org.chromium.base.metrics;
 
+import org.chromium.base.Callback;
+
 /** An empty implementation of {@link UmaRecorder}. */
 /* package */ class NoopUmaRecorder implements UmaRecorder {
     @Override
@@ -31,4 +33,10 @@
     public int getHistogramTotalCountForTesting(String name) {
         return 0;
     }
+
+    @Override
+    public void addUserActionCallbackForTesting(Callback<String> callback) {}
+
+    @Override
+    public void removeUserActionCallbackForTesting(Callback<String> callback) {}
 }
diff --git a/base/android/java/src/org/chromium/base/metrics/RecordUserAction.java b/base/android/java/src/org/chromium/base/metrics/RecordUserAction.java
index ed68bf10..e0979cd 100644
--- a/base/android/java/src/org/chromium/base/metrics/RecordUserAction.java
+++ b/base/android/java/src/org/chromium/base/metrics/RecordUserAction.java
@@ -5,9 +5,6 @@
 package org.chromium.base.metrics;
 
 import org.chromium.base.TimeUtils;
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.base.annotations.NativeMethods;
 
 /**
  * Java API for recording UMA actions.
@@ -19,7 +16,6 @@
  * We use a script ({@code extract_actions.py{}) to scan the source code and extract actions. A
  * string literal (not a variable) must be passed to {@link #record(String)}.
  */
-@JNINamespace("base::android")
 public class RecordUserAction {
     /**
      * Similar to {@code base::RecordAction()} in C++.
@@ -29,39 +25,4 @@
     public static void record(final String action) {
         UmaRecorderHolder.get().recordUserAction(action, TimeUtils.elapsedRealtimeMillis());
     }
-
-    /**
-     * Interface to a class that receives a callback for each UserAction that is recorded.
-     */
-    public interface UserActionCallback {
-        @CalledByNative("UserActionCallback")
-        void onActionRecorded(String action);
-    }
-
-    private static long sNativeActionCallback;
-
-    /**
-     * Register a callback that is executed for each recorded UserAction.
-     * Only one callback can be registered at a time.
-     * The callback has to be unregistered using removeActionCallbackForTesting().
-     */
-    public static void setActionCallbackForTesting(UserActionCallback callback) {
-        assert sNativeActionCallback == 0;
-        sNativeActionCallback = RecordUserActionJni.get().addActionCallbackForTesting(callback);
-    }
-
-    /**
-     * Unregister the UserActionCallback.
-     */
-    public static void removeActionCallbackForTesting() {
-        assert sNativeActionCallback != 0;
-        RecordUserActionJni.get().removeActionCallbackForTesting(sNativeActionCallback);
-        sNativeActionCallback = 0;
-    }
-
-    @NativeMethods
-    interface Natives {
-        long addActionCallbackForTesting(UserActionCallback callback);
-        void removeActionCallbackForTesting(long callbackId);
-    }
 }
diff --git a/base/android/java/src/org/chromium/base/metrics/UmaRecorder.java b/base/android/java/src/org/chromium/base/metrics/UmaRecorder.java
index 70d6f7e9..1c935dd0 100644
--- a/base/android/java/src/org/chromium/base/metrics/UmaRecorder.java
+++ b/base/android/java/src/org/chromium/base/metrics/UmaRecorder.java
@@ -6,6 +6,8 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.Callback;
+
 /** Common interface for code recording UMA metrics. */
 public interface UmaRecorder {
     /** Records a single sample of a boolean histogram. */
@@ -78,4 +80,21 @@
      */
     @VisibleForTesting
     int getHistogramTotalCountForTesting(String name);
+
+    /**
+     * Adds a testing callback to be notified on all actions recorded through
+     * {@link RecordUserAction#record(String)}.
+     *
+     * @param callback The callback to be added.
+     */
+    @VisibleForTesting
+    void addUserActionCallbackForTesting(Callback<String> callback);
+
+    /**
+     * Removes a previously added testing user action callback.
+     *
+     * @param callback The callback to be removed.
+     */
+    @VisibleForTesting
+    void removeUserActionCallbackForTesting(Callback<String> callback);
 }
diff --git a/base/android/junit/src/org/chromium/base/metrics/CachingUmaRecorderTest.java b/base/android/junit/src/org/chromium/base/metrics/CachingUmaRecorderTest.java
index f5e9fb8..b9e73af8 100644
--- a/base/android/junit/src/org/chromium/base/metrics/CachingUmaRecorderTest.java
+++ b/base/android/junit/src/org/chromium/base/metrics/CachingUmaRecorderTest.java
@@ -16,16 +16,20 @@
 
 import androidx.test.filters.MediumTest;
 
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 import java.time.Duration;
 import java.time.Instant;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicIntegerArray;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -359,6 +363,16 @@
         public int getHistogramTotalCountForTesting(String name) {
             throw new UnsupportedOperationException();
         }
+
+        @Override
+        public void addUserActionCallbackForTesting(Callback<String> callback) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void removeUserActionCallbackForTesting(Callback<String> callback) {
+            throw new UnsupportedOperationException();
+        }
     }
 
     @Test
@@ -458,6 +472,16 @@
         public int getHistogramTotalCountForTesting(String name) {
             throw new UnsupportedOperationException();
         }
+
+        @Override
+        public void addUserActionCallbackForTesting(Callback<String> callback) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void removeUserActionCallbackForTesting(Callback<String> callback) {
+            throw new UnsupportedOperationException();
+        }
     }
 
     @Test
@@ -511,4 +535,58 @@
         thread.start();
         return thread;
     }
+
+    @Test
+    public void testUserActionCallbacksTriggeredWithoutDelegate() {
+        CachingUmaRecorder cachingRecorder = new CachingUmaRecorder();
+
+        AtomicInteger callbackCount = new AtomicInteger();
+        Callback<String> testCallback1 = (result) -> callbackCount.incrementAndGet();
+        cachingRecorder.addUserActionCallbackForTesting(testCallback1);
+
+        // Ensure that the callback is notified if attached to the caching recorder (will not
+        // be notified once a delegate is responsible).
+        Assert.assertEquals(0, callbackCount.get());
+        cachingRecorder.recordUserAction("TEST", 0);
+        Assert.assertEquals(1, callbackCount.get());
+        cachingRecorder.recordUserAction("TEST", 0);
+        Assert.assertEquals(2, callbackCount.get());
+
+        // Remove the callback and ensure it is no longer called.
+        cachingRecorder.removeUserActionCallbackForTesting(testCallback1);
+        Assert.assertEquals(2, callbackCount.get());
+        cachingRecorder.recordUserAction("TEST", 0);
+        Assert.assertEquals(2, callbackCount.get());
+    }
+
+    @Test
+    public void testUserActionCallbacksSwappedBetweenDelegates() {
+        CachingUmaRecorder cachingRecorder = new CachingUmaRecorder();
+
+        Callback<String> testCallback1 = (result) -> {};
+        Callback<String> testCallback2 = (result) -> {};
+        cachingRecorder.addUserActionCallbackForTesting(testCallback1);
+        cachingRecorder.addUserActionCallbackForTesting(testCallback2);
+
+        // Validate that previously added callbacks are passed to the new delegate.
+        UmaRecorder delegate1 = Mockito.mock(UmaRecorder.class);
+        cachingRecorder.setDelegate(delegate1);
+        verify(delegate1).addUserActionCallbackForTesting(testCallback1);
+        verify(delegate1).addUserActionCallbackForTesting(testCallback2);
+
+        // Validate that previously added callbacks are removed from the previous delegate, and
+        // passed to the new delegate.
+        UmaRecorder delegate2 = Mockito.mock(UmaRecorder.class);
+        cachingRecorder.setDelegate(delegate2);
+        verify(delegate1).removeUserActionCallbackForTesting(testCallback1);
+        verify(delegate1).removeUserActionCallbackForTesting(testCallback2);
+        verify(delegate2).addUserActionCallbackForTesting(testCallback1);
+        verify(delegate2).addUserActionCallbackForTesting(testCallback2);
+
+        // Ensure a callback added later is also added correctly.
+        Callback<String> testCallback3 = (result) -> {};
+        cachingRecorder.addUserActionCallbackForTesting(testCallback3);
+        verifyZeroInteractions(delegate1);
+        verify(delegate2).addUserActionCallbackForTesting(testCallback3);
+    }
 }
diff --git a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserAction.java b/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserAction.java
deleted file mode 100644
index 5c25fe4..0000000
--- a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserAction.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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.
-
-package org.chromium.base.metrics.test;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.base.metrics.RecordUserAction.UserActionCallback;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Queue;
-import java.util.concurrent.ArrayBlockingQueue;
-
-/**
- * Implementation of {@link RecordUserAction} which does not rely on native and still enables
- * testing of user action counts. {@link ShadowRecordUserAction#reset} should be called in the
- * {@link @Before} method of test cases using this class.
- *
- * <p>Note that this class uses {@link ArrayBlockingQueue} to store the samples, and it has a
- * capacity of 100 - an arbitrary number assuming most unit test does not record samples more than
- * that.</p>
- *
- * TODO(https://crbug.com/1211884): Remove this class once UmaRecorderHolder setup is available for
- * junit tests.
- */
-@Implements(RecordUserAction.class)
-public class ShadowRecordUserAction {
-    private static final Queue<String> sSamples = new ArrayBlockingQueue<>(100);
-
-    /**
-     * Get all the user action samples tracked by this class, organized in the order it is
-     * triggered.
-     */
-    public static List<String> getSamples() {
-        return new ArrayList<>(sSamples);
-    }
-
-    /** Clear all the samples tracked by the shadow. */
-    public static void reset() {
-        sSamples.clear();
-    }
-
-    @Implementation
-    protected static void record(final String action) {
-        sSamples.add(action);
-    }
-
-    @Implementation
-    protected static void setActionCallbackForTesting(UserActionCallback callback) {
-        assert false : "Should not be used in Robolectric tests.";
-    }
-
-    @Implementation
-    protected static void removeActionCallbackForTesting() {
-        assert false : "Should not be used in Robolectric tests.";
-    }
-}
diff --git a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserActionTest.java b/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserActionTest.java
deleted file mode 100644
index f162658..0000000
--- a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserActionTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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.
-
-package org.chromium.base.metrics.test;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.metrics.RecordUserAction;
-
-import java.util.List;
-
-/** Unit test for ShadowRecordUserAction. */
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowRecordUserAction.class)
-public class ShadowRecordUserActionTest {
-    @After
-    public void tearDown() {
-        ShadowRecordUserAction.reset();
-    }
-
-    @Test
-    public void testRecord() {
-        RecordUserAction.record("action1");
-        RecordUserAction.record("action2");
-        RecordUserAction.record("action3");
-        RecordUserAction.record("action4");
-
-        List<String> actions = ShadowRecordUserAction.getSamples();
-
-        Assert.assertEquals("Size of the samples is different.", 4, actions.size());
-        for (int i = 0; i < 4; ++i) {
-            Assert.assertEquals("Sample order is different.", "action" + (i + 1), actions.get(i));
-        }
-
-        ShadowRecordUserAction.reset();
-        Assert.assertTrue("Use actions should be cleared after reset.",
-                ShadowRecordUserAction.getSamples().isEmpty());
-    }
-
-    @Test(expected = AssertionError.class)
-    public void testSetActionCallbackForTesting() {
-        RecordUserAction.setActionCallbackForTesting(action -> {});
-    }
-
-    @Test(expected = AssertionError.class)
-    public void testRemoveActionCallbackForTesting() {
-        RecordUserAction.removeActionCallbackForTesting();
-    }
-}
diff --git a/base/android/native_uma_recorder.cc b/base/android/native_uma_recorder.cc
index e3ed4550..d5d70d8f 100644
--- a/base/android/native_uma_recorder.cc
+++ b/base/android/native_uma_recorder.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/android/callback_android.h"
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/base_jni_headers/NativeUmaRecorder_jni.h"
@@ -157,6 +158,16 @@
 
 LazyInstance<HistogramCache>::Leaky g_histograms;
 
+struct ActionCallbackWrapper {
+  base::ActionCallback action_callback;
+};
+
+static void OnActionRecorded(const JavaRef<jobject>& callback,
+                             const std::string& action,
+                             TimeTicks action_time) {
+  RunStringCallbackAndroid(callback, action);
+}
+
 }  // namespace
 
 jlong JNI_NativeUmaRecorder_RecordBooleanHistogram(
@@ -270,5 +281,25 @@
   return actual_count;
 }
 
+static jlong JNI_NativeUmaRecorder_AddActionCallbackForTesting(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& callback) {
+  // Create a wrapper for the ActionCallback, so it can life on the heap until
+  // RemoveActionCallbackForTesting() is called.
+  auto* wrapper = new ActionCallbackWrapper{base::BindRepeating(
+      &OnActionRecorded, ScopedJavaGlobalRef<jobject>(env, callback))};
+  base::AddActionCallback(wrapper->action_callback);
+  return reinterpret_cast<intptr_t>(wrapper);
+}
+
+static void JNI_NativeUmaRecorder_RemoveActionCallbackForTesting(
+    JNIEnv* env,
+    jlong callback_id) {
+  DCHECK(callback_id);
+  auto* wrapper = reinterpret_cast<ActionCallbackWrapper*>(callback_id);
+  base::RemoveActionCallback(wrapper->action_callback);
+  delete wrapper;
+}
+
 }  // namespace android
 }  // namespace base
diff --git a/base/android/record_user_action.cc b/base/android/record_user_action.cc
deleted file mode 100644
index 6cc0a6f..0000000
--- a/base/android/record_user_action.cc
+++ /dev/null
@@ -1,50 +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.
-
-#include "base/android/jni_string.h"
-#include "base/base_jni_headers/RecordUserAction_jni.h"
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/metrics/user_metrics.h"
-#include "base/time/time.h"
-
-namespace {
-struct ActionCallbackWrapper {
-  base::ActionCallback action_callback;
-};
-}  // namespace
-
-namespace base {
-namespace android {
-
-static void OnActionRecorded(const JavaRef<jobject>& callback,
-                             const std::string& action,
-                             TimeTicks action_time) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_UserActionCallback_onActionRecorded(
-      env, callback, ConvertUTF8ToJavaString(env, action));
-}
-
-static jlong JNI_RecordUserAction_AddActionCallbackForTesting(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& callback) {
-  // Create a wrapper for the ActionCallback, so it can life on the heap until
-  // RemoveActionCallbackForTesting() is called.
-  auto* wrapper = new ActionCallbackWrapper{base::BindRepeating(
-      &OnActionRecorded, ScopedJavaGlobalRef<jobject>(env, callback))};
-  base::AddActionCallback(wrapper->action_callback);
-  return reinterpret_cast<intptr_t>(wrapper);
-}
-
-static void JNI_RecordUserAction_RemoveActionCallbackForTesting(
-    JNIEnv* env,
-    jlong callback_id) {
-  DCHECK(callback_id);
-  auto* wrapper = reinterpret_cast<ActionCallbackWrapper*>(callback_id);
-  base::RemoveActionCallback(wrapper->action_callback);
-  delete wrapper;
-}
-
-}  // namespace android
-}  // namespace base
diff --git a/base/check.cc b/base/check.cc
index f340ff6..fb19c25 100644
--- a/base/check.cc
+++ b/base/check.cc
@@ -30,7 +30,9 @@
 
 namespace {
 
-#if defined(DCHECK_IS_CONFIGURABLE)
+// DCHECK_IS_CONFIGURABLE and ENABLE_LOG_ERROR_NOT_REACHED are both interested
+// in non-FATAL DCHECK()/NOTREACHED() reports.
+#if defined(DCHECK_IS_CONFIGURABLE) || BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
 void DCheckDumpOnceWithoutCrashing(LogMessage* log_message) {
   // Best-effort gate to prevent multiple DCHECKs from being dumped. This will
   // race if multiple threads DCHECK at the same time, but we'll eventually stop
@@ -56,6 +58,21 @@
   }
 }
 
+class NotReachedLogMessage : public LogMessage {
+ public:
+  using LogMessage::LogMessage;
+  ~NotReachedLogMessage() override {
+    if (severity() != logging::LOGGING_FATAL)
+      DCheckDumpOnceWithoutCrashing(this);
+  }
+};
+#else
+using NotReachedLogMessage = LogMessage;
+#endif  // defined(DCHECK_IS_CONFIGURABLE) ||
+        // BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
+
+#if defined(DCHECK_IS_CONFIGURABLE)
+
 class DCheckLogMessage : public LogMessage {
  public:
   using LogMessage::LogMessage;
@@ -86,11 +103,11 @@
 #endif  // BUILDFLAG(IS_WIN)
 #else
 static_assert(logging::LOGGING_DCHECK == logging::LOGGING_FATAL);
-typedef LogMessage DCheckLogMessage;
+using DCheckLogMessage = LogMessage;
 #if BUILDFLAG(IS_WIN)
-typedef Win32ErrorLogMessage DCheckWin32ErrorLogMessage;
+using DCheckWin32ErrorLogMessage = Win32ErrorLogMessage;
 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
-typedef ErrnoLogMessage DCheckErrnoLogMessage;
+using DCheckErrnoLogMessage = ErrnoLogMessage;
 #endif  // BUILDFLAG(IS_WIN)
 #endif  // defined(DCHECK_IS_CONFIGURABLE)
 
@@ -174,6 +191,17 @@
   return CheckError(log_message);
 }
 
+CheckError CheckError::NotReached(const char* file, int line) {
+  // Outside DCHECK builds NOTREACHED() should not be FATAL. For now.
+  const LogSeverity severity = DCHECK_IS_ON() ? LOGGING_DCHECK : LOGGING_ERROR;
+  auto* const log_message = new NotReachedLogMessage(file, line, severity);
+
+  // TODO(pbos): Consider a better message for NotReached(), this is here to
+  // match existing behavior + test expectations.
+  log_message->stream() << "Check failed: false. ";
+  return CheckError(log_message);
+}
+
 std::ostream& CheckError::stream() {
   return log_message_->stream();
 }
diff --git a/base/check.h b/base/check.h
index 7561cd3..b2db7d2f 100644
--- a/base/check.h
+++ b/base/check.h
@@ -82,6 +82,8 @@
                                    int line,
                                    const char* function);
 
+  static CheckError NotReached(const char* file, int line);
+
   // Stream for adding optional details to the error message.
   std::ostream& stream();
 
diff --git a/base/check_unittest.cc b/base/check_unittest.cc
index 9da7b702..82993ae 100644
--- a/base/check_unittest.cc
+++ b/base/check_unittest.cc
@@ -407,7 +407,7 @@
                CHECK_EQ(g, h));
 }
 
-#define EXPECT_LOG_ERROR(msg, expr, expected_line)                             \
+#define EXPECT_LOG_ERROR(expected_line, msg, expr)                             \
   do {                                                                         \
     static bool got_log_message = false;                                       \
     ASSERT_EQ(logging::GetLogMessageHandler(), nullptr);                       \
@@ -441,9 +441,10 @@
   } while (0)
 
 TEST_F(CheckTest, NotReached) {
-#if BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
-  // Expect LOG(ERROR) without the streamed params.
-  EXPECT_LOG_ERROR("NOTREACHED() hit.\n", NOTREACHED() << "foo", __LINE__);
+#if BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED) && !DCHECK_IS_ON()
+  // Expect LOG(ERROR) that looks like CHECK(false) with streamed params intact.
+  EXPECT_LOG_ERROR(__LINE__, "Check failed: false. foo\n",
+                   NOTREACHED() << "foo");
 #else
   // Expect a DCHECK with streamed params intact.
   EXPECT_DCHECK("Check failed: false. foo", NOTREACHED() << "foo");
@@ -456,7 +457,7 @@
 
 #if DCHECK_IS_ON()
   // Expect LOG(ERROR) with streamed params intact.
-  EXPECT_LOG_ERROR(expected_msg + "foo\n", NOTIMPLEMENTED() << "foo", __LINE__);
+  EXPECT_LOG_ERROR(__LINE__, expected_msg + "foo\n", NOTIMPLEMENTED() << "foo");
 #else
   // Expect nothing.
   EXPECT_NO_LOG(NOTIMPLEMENTED() << "foo");
@@ -473,7 +474,7 @@
       "Not implemented reached in void (anonymous namespace)::NiLogOnce()\n";
 
 #if DCHECK_IS_ON()
-  EXPECT_LOG_ERROR(expected_msg, NiLogOnce(), __LINE__ - 8);
+  EXPECT_LOG_ERROR(__LINE__ - 8, expected_msg, NiLogOnce());
   EXPECT_NO_LOG(NiLogOnce());
 #else
   EXPECT_NO_LOG(NiLogOnce());
diff --git a/base/json/json_reader_fuzzer.cc b/base/json/json_reader_fuzzer.cc
index 3cde253..3eb7b44 100644
--- a/base/json/json_reader_fuzzer.cc
+++ b/base/json/json_reader_fuzzer.cc
@@ -35,7 +35,7 @@
     absl::optional<Value> deserialized =
         JSONReader::Read(StringPiece(serialized));
     CHECK(deserialized);
-    CHECK(value.Equals(&deserialized.value()));
+    CHECK_EQ(value, deserialized.value());
   }
 
   return 0;
diff --git a/base/notreached.cc b/base/notreached.cc
deleted file mode 100644
index 734da14..0000000
--- a/base/notreached.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2020 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 "base/notreached.h"
-#include "base/base_export.h"
-
-// This is a widely included header and its size has significant impact on
-// build time. Try not to raise this limit unless absolutely necessary. See
-// https://chromium.googlesource.com/chromium/src/+/HEAD/docs/wmax_tokens.md
-#ifndef NACL_TC_REV
-#pragma clang max_tokens_here 17000
-#endif
-
-#include "base/logging.h"
-
-namespace logging {
-
-BASE_EXPORT void LogErrorNotReached(const char* file, int line) {
-  LogMessage(file, line, LOG_ERROR).stream() << "NOTREACHED() hit.";
-}
-
-}  // namespace logging
diff --git a/base/notreached.h b/base/notreached.h
index 293451a2..044e7067 100644
--- a/base/notreached.h
+++ b/base/notreached.h
@@ -12,14 +12,14 @@
 
 namespace logging {
 
-#if BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
-void BASE_EXPORT LogErrorNotReached(const char* file, int line);
-#define NOTREACHED()                                       \
-  true ? ::logging::LogErrorNotReached(__FILE__, __LINE__) \
-       : EAT_CHECK_STREAM_PARAMS()
+// Under these conditions NOTREACHED() will effectively either log or DCHECK.
+#if BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED) || DCHECK_IS_ON()
+#define NOTREACHED() \
+  LAZY_CHECK_STREAM( \
+      ::logging::CheckError::NotReached(__FILE__, __LINE__).stream(), true)
 #else
-#define NOTREACHED() DCHECK(false)
-#endif
+#define NOTREACHED() EAT_CHECK_STREAM_PARAMS()
+#endif  // BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED) || DCHECK_IS_ON()
 
 // The NOTIMPLEMENTED() macro annotates codepaths which have not been
 // implemented yet. If output spam is a serious concern,
diff --git a/base/profiler/chrome_unwinder_android.cc b/base/profiler/chrome_unwinder_android.cc
index 87064c65..1d88624 100644
--- a/base/profiler/chrome_unwinder_android.cc
+++ b/base/profiler/chrome_unwinder_android.cc
@@ -6,7 +6,6 @@
 
 #include "base/numerics/checked_math.h"
 #include "base/profiler/module_cache.h"
-#include "base/profiler/native_unwinder.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/profiler/native_unwinder_apple.cc b/base/profiler/frame_pointer_unwinder.cc
similarity index 85%
rename from base/profiler/native_unwinder_apple.cc
rename to base/profiler/frame_pointer_unwinder.cc
index d698c66..0068c92 100644
--- a/base/profiler/native_unwinder_apple.cc
+++ b/base/profiler/frame_pointer_unwinder.cc
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/profiler/native_unwinder_apple.h"
-
-#include <pthread/stack_np.h>
+#include "base/profiler/frame_pointer_unwinder.h"
 
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "base/numerics/clamped_math.h"
 #include "base/profiler/module_cache.h"
-#include "base/profiler/native_unwinder.h"
 #include "build/build_config.h"
 
+#if BUILDFLAG(IS_APPLE)
+#include <pthread/stack_np.h>
+#endif
+
 namespace {
 
 // Given a frame pointer, returns the frame pointer of the calling stack
@@ -22,8 +23,10 @@
 // NB: The caller *must* ensure that there are 2+ uintptr_t's worth of memory at
 // `frame_pointer`.
 uintptr_t DecodeFrame(uintptr_t frame_pointer, uintptr_t* return_address) {
+#if BUILDFLAG(IS_APPLE)
   if (__builtin_available(macOS 10.14, iOS 12, *))
     return pthread_stack_frame_decode_np(frame_pointer, return_address);
+#endif
   const uintptr_t* fp = reinterpret_cast<uintptr_t*>(frame_pointer);
   uintptr_t next_frame = *fp;
   *return_address = *(fp + 1);
@@ -34,15 +37,15 @@
 
 namespace base {
 
-NativeUnwinderApple::NativeUnwinderApple() = default;
+FramePointerUnwinder::FramePointerUnwinder() = default;
 
-bool NativeUnwinderApple::CanUnwindFrom(const Frame& current_frame) const {
+bool FramePointerUnwinder::CanUnwindFrom(const Frame& current_frame) const {
   return current_frame.module && current_frame.module->IsNative();
 }
 
-UnwindResult NativeUnwinderApple::TryUnwind(RegisterContext* thread_context,
-                                            uintptr_t stack_top,
-                                            std::vector<Frame>* stack) const {
+UnwindResult FramePointerUnwinder::TryUnwind(RegisterContext* thread_context,
+                                             uintptr_t stack_top,
+                                             std::vector<Frame>* stack) const {
   // We expect the frame corresponding to the |thread_context| register state to
   // exist within |stack|.
   DCHECK_GT(stack->size(), 0u);
@@ -101,8 +104,4 @@
   return UnwindResult::kCompleted;
 }
 
-std::unique_ptr<Unwinder> CreateNativeUnwinder(ModuleCache* module_cache) {
-  return std::make_unique<NativeUnwinderApple>();
-}
-
 }  // namespace base
diff --git a/base/profiler/frame_pointer_unwinder.h b/base/profiler/frame_pointer_unwinder.h
new file mode 100644
index 0000000..106b49b
--- /dev/null
+++ b/base/profiler/frame_pointer_unwinder.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef BASE_PROFILER_FRAME_POINTER_UNWINDER_H_
+#define BASE_PROFILER_FRAME_POINTER_UNWINDER_H_
+
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/profiler/unwinder.h"
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_APPLE)
+#include <os/availability.h>
+#endif
+
+namespace base {
+
+// Native unwinder implementation for platforms that have frame pointers:
+//  * iOS, ARM64 and X86_64,
+//  * macOS 10.14+.
+//  * ChromeOS X86_64
+class BASE_EXPORT
+#if BUILDFLAG(IS_APPLE)
+API_AVAILABLE(ios(12))
+#endif
+    FramePointerUnwinder : public Unwinder {
+ public:
+  FramePointerUnwinder();
+
+  FramePointerUnwinder(const FramePointerUnwinder&) = delete;
+  FramePointerUnwinder& operator=(const FramePointerUnwinder&) = delete;
+
+  // Unwinder:
+  bool CanUnwindFrom(const Frame& current_frame) const override;
+  UnwindResult TryUnwind(RegisterContext* thread_context,
+                         uintptr_t stack_top,
+                         std::vector<Frame>* stack) const override;
+};
+
+}  // namespace base
+
+#endif  // BASE_PROFILER_FRAME_POINTER_UNWINDER_H_
diff --git a/base/profiler/native_unwinder_apple_unittest.cc b/base/profiler/frame_pointer_unwinder_unittest.cc
similarity index 93%
rename from base/profiler/native_unwinder_apple_unittest.cc
rename to base/profiler/frame_pointer_unwinder_unittest.cc
index 9ba585c..d806c105 100644
--- a/base/profiler/native_unwinder_apple_unittest.cc
+++ b/base/profiler/frame_pointer_unwinder_unittest.cc
@@ -2,16 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/profiler/native_unwinder_apple.h"
+#include "base/profiler/frame_pointer_unwinder.h"
 
-#include "base/mac/mac_util.h"
+#include <memory>
+
 #include "base/profiler/module_cache.h"
 #include "base/profiler/stack_sampling_profiler_test_util.h"
 #include "base/profiler/unwinder.h"
 #include "build/buildflag.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#include <memory>
+#if BUILDFLAG(IS_APPLE)
+#include "base/mac/mac_util.h"
+#endif
 
 namespace base {
 
@@ -57,11 +60,15 @@
 
 }  // namespace
 
-class NativeUnwinderAppleTest : public testing::Test {
+class FramePointerUnwinderTest : public testing::Test {
  protected:
-  NativeUnwinderAppleTest() {
+  FramePointerUnwinderTest() {
+#if BUILDFLAG(IS_APPLE)
     if (__builtin_available(iOS 12, *)) {
-      unwinder_ = std::make_unique<NativeUnwinderApple>();
+#else
+    {
+#endif
+      unwinder_ = std::make_unique<FramePointerUnwinder>();
 
       auto test_module =
           std::make_unique<TestModule>(kModuleStart, kModuleSize);
@@ -90,7 +97,7 @@
   raw_ptr<ModuleCache::Module> non_native_module_;
 };
 
-TEST_F(NativeUnwinderAppleTest, FPPointsOutsideOfStack) {
+TEST_F(FramePointerUnwinderTest, FPPointsOutsideOfStack) {
   InputStack input({
       {false, 0x1000},
       {false, 0x1000},
@@ -121,7 +128,7 @@
   EXPECT_EQ(std::vector<Frame>({{kModuleStart, module()}}), stack);
 }
 
-TEST_F(NativeUnwinderAppleTest, FPPointsToSelf) {
+TEST_F(FramePointerUnwinderTest, FPPointsToSelf) {
   InputStack input({
       {true, 0},
       {false, kModuleStart + 0x10},
@@ -148,7 +155,7 @@
 
 // Tests that two frame pointers that point to each other can't create an
 // infinite loop
-TEST_F(NativeUnwinderAppleTest, FPCycle) {
+TEST_F(FramePointerUnwinderTest, FPCycle) {
   InputStack input({
       {true, 2},
       {false, kModuleStart + 0x10},
@@ -176,7 +183,7 @@
             stack);
 }
 
-TEST_F(NativeUnwinderAppleTest, NoModuleForIP) {
+TEST_F(FramePointerUnwinderTest, NoModuleForIP) {
   uintptr_t not_in_module = kModuleStart - 0x10;
   InputStack input({
       {true, 2},
@@ -203,7 +210,7 @@
 
 // Tests that testing that checking if there's space to read two values from the
 // stack doesn't overflow.
-TEST_F(NativeUnwinderAppleTest, FPAdditionOverflows) {
+TEST_F(FramePointerUnwinderTest, FPAdditionOverflows) {
   uintptr_t will_overflow = std::numeric_limits<uintptr_t>::max() - 1;
   InputStack input({
       {true, 2},
@@ -228,7 +235,7 @@
 }
 
 // Tests the happy path: a successful unwind with no non-native modules.
-TEST_F(NativeUnwinderAppleTest, RegularUnwind) {
+TEST_F(FramePointerUnwinderTest, RegularUnwind) {
   InputStack input({
       {true, 4},                     // fp of frame 1
       {false, kModuleStart + 0x20},  // ip of frame 1
@@ -261,7 +268,7 @@
 
 // Tests that if a V8 frame is encountered, unwinding stops and
 // kUnrecognizedFrame is returned to facilitate continuing with the V8 unwinder.
-TEST_F(NativeUnwinderAppleTest, NonNativeFrame) {
+TEST_F(FramePointerUnwinderTest, NonNativeFrame) {
   InputStack input({
       {true, 4},                     // fp of frame 1
       {false, kModuleStart + 0x20},  // ip of frame 1
@@ -298,7 +305,7 @@
 
 // Tests that a V8 frame with an unaligned frame pointer correctly returns
 // kUnrecognizedFrame and not kAborted.
-TEST_F(NativeUnwinderAppleTest, NonNativeUnaligned) {
+TEST_F(FramePointerUnwinderTest, NonNativeUnaligned) {
   InputStack input({
       {true, 4},                     // fp of frame 1
       {false, kModuleStart + 0x20},  // ip of frame 1
diff --git a/base/profiler/native_unwinder.h b/base/profiler/native_unwinder.h
deleted file mode 100644
index f26c5ad2..0000000
--- a/base/profiler/native_unwinder.h
+++ /dev/null
@@ -1,20 +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.
-
-#ifndef BASE_PROFILER_NATIVE_UNWINDER_H_
-#define BASE_PROFILER_NATIVE_UNWINDER_H_
-
-#include <memory>
-
-namespace base {
-
-class ModuleCache;
-class Unwinder;
-
-// Creates the native unwinder for the platform.
-std::unique_ptr<Unwinder> CreateNativeUnwinder(ModuleCache* module_cache);
-
-}  // namespace base
-
-#endif  // BASE_PROFILER_NATIVE_UNWINDER_H_
diff --git a/base/profiler/native_unwinder_android.cc b/base/profiler/native_unwinder_android.cc
index 67c28b9..a2e04a21 100644
--- a/base/profiler/native_unwinder_android.cc
+++ b/base/profiler/native_unwinder_android.cc
@@ -16,7 +16,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
 #include "base/profiler/module_cache.h"
-#include "base/profiler/native_unwinder.h"
 #include "base/profiler/profile_builder.h"
 #include "build/build_config.h"
 
diff --git a/base/profiler/native_unwinder_apple.h b/base/profiler/native_unwinder_apple.h
deleted file mode 100644
index 97a2ed4..0000000
--- a/base/profiler/native_unwinder_apple.h
+++ /dev/null
@@ -1,34 +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.
-
-#ifndef BASE_PROFILER_NATIVE_UNWINDER_APPLE_H_
-#define BASE_PROFILER_NATIVE_UNWINDER_APPLE_H_
-
-#include <vector>
-
-#include <os/availability.h>
-
-#include "base/base_export.h"
-#include "base/profiler/unwinder.h"
-
-namespace base {
-
-// Native unwinder implementation for iOS, ARM64 and X86_64, and macOS 10.14+.
-class BASE_EXPORT API_AVAILABLE(ios(12)) NativeUnwinderApple : public Unwinder {
- public:
-  NativeUnwinderApple();
-
-  NativeUnwinderApple(const NativeUnwinderApple&) = delete;
-  NativeUnwinderApple& operator=(const NativeUnwinderApple&) = delete;
-
-  // Unwinder:
-  bool CanUnwindFrom(const Frame& current_frame) const override;
-  UnwindResult TryUnwind(RegisterContext* thread_context,
-                         uintptr_t stack_top,
-                         std::vector<Frame>* stack) const override;
-};
-
-}  // namespace base
-
-#endif  // BASE_PROFILER_NATIVE_UNWINDER_APPLE_H_
diff --git a/base/profiler/native_unwinder_win.cc b/base/profiler/native_unwinder_win.cc
index 0c6c4dad..68b5e45 100644
--- a/base/profiler/native_unwinder_win.cc
+++ b/base/profiler/native_unwinder_win.cc
@@ -8,7 +8,6 @@
 
 #include "base/check_op.h"
 #include "base/notreached.h"
-#include "base/profiler/native_unwinder.h"
 #include "base/profiler/win32_stack_frame_unwinder.h"
 #include "build/build_config.h"
 
@@ -93,8 +92,4 @@
   return UnwindResult::kCompleted;
 }
 
-std::unique_ptr<Unwinder> CreateNativeUnwinder(ModuleCache* module_cache) {
-  return std::make_unique<NativeUnwinderWin>();
-}
-
 }  // namespace base
diff --git a/base/profiler/stack_sampler_ios.cc b/base/profiler/stack_sampler_ios.cc
index 26e96ce..3d05021 100644
--- a/base/profiler/stack_sampler_ios.cc
+++ b/base/profiler/stack_sampler_ios.cc
@@ -10,7 +10,7 @@
 #if BUILDFLAG(IOS_STACK_PROFILER_ENABLED)
 #include "base/bind.h"
 #include "base/check.h"
-#include "base/profiler/native_unwinder_apple.h"
+#include "base/profiler/frame_pointer_unwinder.h"
 #include "base/profiler/stack_copier_suspend.h"
 #include "base/profiler/stack_sampler_impl.h"
 #include "base/profiler/suspendable_thread_delegate_mac.h"
@@ -24,7 +24,7 @@
 std::vector<std::unique_ptr<Unwinder>> CreateUnwinders() {
   std::vector<std::unique_ptr<Unwinder>> unwinders;
   if (__builtin_available(iOS 12.0, *)) {
-    unwinders.push_back(std::make_unique<NativeUnwinderApple>());
+    unwinders.push_back(std::make_unique<FramePointerUnwinder>());
   }
   return unwinders;
 }
diff --git a/base/profiler/stack_sampler_mac.cc b/base/profiler/stack_sampler_mac.cc
index 69667a3..892515e 100644
--- a/base/profiler/stack_sampler_mac.cc
+++ b/base/profiler/stack_sampler_mac.cc
@@ -4,9 +4,11 @@
 
 #include "base/profiler/stack_sampler.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/check.h"
-#include "base/profiler/native_unwinder.h"
+#include "base/profiler/frame_pointer_unwinder.h"
 #include "base/profiler/stack_copier_suspend.h"
 #include "base/profiler/stack_sampler_impl.h"
 #include "base/profiler/suspendable_thread_delegate_mac.h"
@@ -16,10 +18,9 @@
 
 namespace {
 
-std::vector<std::unique_ptr<Unwinder>> CreateUnwinders(
-    ModuleCache* module_cache) {
+std::vector<std::unique_ptr<Unwinder>> CreateUnwinders() {
   std::vector<std::unique_ptr<Unwinder>> unwinders;
-  unwinders.push_back(CreateNativeUnwinder(module_cache));
+  unwinders.push_back(std::make_unique<FramePointerUnwinder>());
   return unwinders;
 }
 
@@ -36,7 +37,7 @@
   return std::make_unique<StackSamplerImpl>(
       std::make_unique<StackCopierSuspend>(
           std::make_unique<SuspendableThreadDelegateMac>(thread_token)),
-      BindOnce(&CreateUnwinders, Unretained(module_cache)), module_cache,
+      BindOnce(&CreateUnwinders), module_cache,
       std::move(record_sample_callback), test_delegate);
 }
 
diff --git a/base/profiler/stack_sampler_posix.cc b/base/profiler/stack_sampler_posix.cc
index be47a3b..c7b74a5 100644
--- a/base/profiler/stack_sampler_posix.cc
+++ b/base/profiler/stack_sampler_posix.cc
@@ -6,18 +6,51 @@
 
 #include <pthread.h>
 
+#include <memory>
+
 #include "base/threading/platform_thread.h"
 #include "build/build_config.h"
 
+#if BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_X86_64)
+#include "base/bind.h"
+#include "base/check.h"
+#include "base/profiler/frame_pointer_unwinder.h"
+#include "base/profiler/stack_copier_signal.h"
+#include "base/profiler/stack_sampler_impl.h"
+#include "base/profiler/thread_delegate_posix.h"
+#include "base/profiler/unwinder.h"
+#endif
+
 namespace base {
 
+namespace {
+
+#if BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_X86_64)
+std::vector<std::unique_ptr<Unwinder>> CreateUnwinders() {
+  std::vector<std::unique_ptr<Unwinder>> unwinders;
+  unwinders.push_back(std::make_unique<FramePointerUnwinder>());
+  return unwinders;
+}
+#endif
+
+}  // namespace
+
 std::unique_ptr<StackSampler> StackSampler::Create(
     SamplingProfilerThreadToken thread_token,
     ModuleCache* module_cache,
     UnwindersFactory core_unwinders_factory,
     RepeatingClosure record_sample_callback,
     StackSamplerTestDelegate* test_delegate) {
+#if BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_X86_64)
+  DCHECK(!core_unwinders_factory);
+  return std::make_unique<StackSamplerImpl>(
+      std::make_unique<StackCopierSignal>(
+          ThreadDelegatePosix::Create(thread_token)),
+      BindOnce(&CreateUnwinders), module_cache,
+      std::move(record_sample_callback), test_delegate);
+#else
   return nullptr;
+#endif
 }
 
 size_t StackSampler::GetStackBufferSize() {
diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc
index 6f7cad5..c0db1d1 100644
--- a/base/profiler/stack_sampling_profiler_unittest.cc
+++ b/base/profiler/stack_sampling_profiler_unittest.cc
@@ -46,11 +46,16 @@
 #endif
 
 // STACK_SAMPLING_PROFILER_SUPPORTED is used to conditionally enable the tests
-// below for supported platforms (currently Win x64, Mac x64 and iOS 64).
-#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_64)) ||  \
-    (BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64)) ||  \
-    (BUILDFLAG(IS_IOS) && defined(ARCH_CPU_64_BITS)) || \
-    (BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE))
+// below for supported platforms (currently Win x64, Mac x64, iOS 64, some
+// Android, and ChromeOS x64).
+// ChromeOS: These don't run under MSan because parts of the stack aren't
+// initialized.
+#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_64)) ||            \
+    (BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64)) ||            \
+    (BUILDFLAG(IS_IOS) && defined(ARCH_CPU_64_BITS)) ||           \
+    (BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)) || \
+    (BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_X86_64) &&        \
+     !defined(MEMORY_SANITIZER))
 #define STACK_SAMPLING_PROFILER_SUPPORTED 1
 #endif
 
@@ -190,7 +195,7 @@
          ::GetLastError() != ERROR_MOD_NOT_FOUND) {
     PlatformThread::Sleep(Milliseconds(1));
   }
-#elif BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID)
+#elif BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
 // Unloading a library on Mac and Android is synchronous.
 #else
   NOTIMPLEMENTED();
@@ -433,8 +438,13 @@
 //
 // TODO(https://crbug.com/1100175): Enable this test again for Android with
 // ASAN. This is now disabled because the android-asan bot fails.
-#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || \
-    (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_ANDROID))
+//
+// If we're running the ChromeOS unit tests on Linux, this test will never pass
+// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
+// ChromeOS device.
+#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) ||   \
+    (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_ANDROID)) || \
+    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
 #define MAYBE_Basic DISABLED_Basic
 #else
 #define MAYBE_Basic Basic
@@ -488,7 +498,12 @@
 // macOS ASAN is not yet supported - crbug.com/718628.
 // Android is not supported since Chrome unwind tables don't support dynamic
 // frames.
-#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || BUILDFLAG(IS_ANDROID)
+// If we're running the ChromeOS unit tests on Linux, this test will never pass
+// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
+// ChromeOS device.
+#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || \
+    BUILDFLAG(IS_ANDROID) ||                               \
+    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
 #define MAYBE_Alloca DISABLED_Alloca
 #else
 #define MAYBE_Alloca Alloca
@@ -511,10 +526,14 @@
 // have unwind tables.
 // TODO(https://crbug.com/1100175): Enable this test again for Android with
 // ASAN. This is now disabled because the android-asan bot fails.
+// If we're running the ChromeOS unit tests on Linux, this test will never pass
+// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
+// ChromeOS device.
 #if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) ||         \
     BUILDFLAG(IS_IOS) ||                                           \
     (BUILDFLAG(IS_ANDROID) && BUILDFLAG(EXCLUDE_UNWIND_TABLES)) || \
-    (BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER))
+    (BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)) ||       \
+    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
 #define MAYBE_OtherLibrary DISABLED_OtherLibrary
 #else
 #define MAYBE_OtherLibrary OtherLibrary
@@ -538,9 +557,13 @@
 // have unwind tables.
 // TODO(https://crbug.com/1100175): Enable this test again for Android with
 // ASAN. This is now disabled because the android-asan bot fails.
+// If we're running the ChromeOS unit tests on Linux, this test will never pass
+// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
+// ChromeOS device.
 #if BUILDFLAG(IS_APPLE) ||                                         \
     (BUILDFLAG(IS_ANDROID) && BUILDFLAG(EXCLUDE_UNWIND_TABLES)) || \
-    (BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER))
+    (BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)) ||       \
+    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
 #define MAYBE_UnloadingLibrary DISABLED_UnloadingLibrary
 #else
 #define MAYBE_UnloadingLibrary UnloadingLibrary
@@ -553,8 +576,12 @@
 // produces a stack, and doesn't crash.
 // macOS ASAN is not yet supported - crbug.com/718628.
 // Android is not supported since modules are found before unwinding.
+// If we're running the ChromeOS unit tests on Linux, this test will never pass
+// because Ubuntu's libc isn't compiled with frame pointers. Skip if not a real
+// ChromeOS device.
 #if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || \
-    BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+    BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) ||          \
+    (BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
 #define MAYBE_UnloadedLibrary DISABLED_UnloadedLibrary
 #else
 #define MAYBE_UnloadedLibrary UnloadedLibrary
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/UserActionTester.java b/base/test/android/javatests/src/org/chromium/base/test/util/UserActionTester.java
index 74b33dab..668d676 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/UserActionTester.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/UserActionTester.java
@@ -6,8 +6,9 @@
 
 import androidx.annotation.GuardedBy;
 
+import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
-import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.base.metrics.UmaRecorderHolder;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -15,7 +16,7 @@
 /**
  * A util class that records UserActions.
  */
-public class UserActionTester implements RecordUserAction.UserActionCallback {
+public class UserActionTester implements Callback<String> {
     @GuardedBy("mActions")
     private List<String> mActions;
 
@@ -24,7 +25,7 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                RecordUserAction.setActionCallbackForTesting(UserActionTester.this);
+                UmaRecorderHolder.get().addUserActionCallbackForTesting(UserActionTester.this);
             }
         });
     }
@@ -33,13 +34,13 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                RecordUserAction.removeActionCallbackForTesting();
+                UmaRecorderHolder.get().removeUserActionCallbackForTesting(UserActionTester.this);
             }
         });
     }
 
     @Override
-    public void onActionRecorded(String action) {
+    public void onResult(String action) {
         synchronized (mActions) {
             mActions.add(action);
         }
diff --git a/base/values.cc b/base/values.cc
index 6496c24..178ca34 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -919,6 +919,22 @@
                         base::to_address(storage_.end()));
 }
 
+Value::List::iterator Value::List::erase(iterator first, iterator last) {
+  auto next_it = storage_.erase(storage_.begin() + (first - begin()),
+                                storage_.begin() + (last - begin()));
+  return iterator(base::to_address(storage_.begin()), base::to_address(next_it),
+                  base::to_address(storage_.end()));
+}
+
+Value::List::const_iterator Value::List::erase(const_iterator first,
+                                               const_iterator last) {
+  auto next_it = storage_.erase(storage_.begin() + (first - begin()),
+                                storage_.begin() + (last - begin()));
+  return const_iterator(base::to_address(storage_.begin()),
+                        base::to_address(next_it),
+                        base::to_address(storage_.end()));
+}
+
 Value::List Value::List::Clone() const {
   return List(storage_);
 }
@@ -1458,11 +1474,6 @@
   return lhs.is_list() && lhs.GetList() == rhs;
 }
 
-bool Value::Equals(const Value* other) const {
-  DCHECK(other);
-  return *this == *other;
-}
-
 size_t Value::EstimateMemoryUsage() const {
   switch (type()) {
 #if BUILDFLAG(ENABLE_BASE_TRACING)
diff --git a/base/values.h b/base/values.h
index 2ce0121..73b91b4 100644
--- a/base/values.h
+++ b/base/values.h
@@ -630,6 +630,12 @@
     iterator erase(iterator pos);
     const_iterator erase(const_iterator pos);
 
+    // Remove the values in the range [`first`, `last`). Returns iterator to the
+    // first value following the removed range, which is `last`. If `first` ==
+    // `last`, removes nothing and returns `last`.
+    iterator erase(iterator first, iterator last);
+    const_iterator erase(const_iterator first, const_iterator last);
+
     // Creates a deep copy of this dictionary.
     List Clone() const;
 
@@ -1138,13 +1144,6 @@
     return !(lhs == rhs);
   }
 
-  // Compares if two Value objects have equal contents.
-  // DEPRECATED, use `operator==(const Value& lhs, const Value& rhs)` instead.
-  // TODO(crbug.com/646113): Delete this and migrate callsites.
-  //
-  // DEPRECATED: prefer direct use of the equality operator instead.
-  bool Equals(const Value* other) const;
-
   // Estimates dynamic memory usage. Requires tracing support
   // (enable_base_tracing gn flag), otherwise always returns 0. See
   // base/trace_event/memory_usage_estimator.h for more info.
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index 184fc09d..e158ee8 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -618,6 +618,39 @@
   EXPECT_EQ(next_it + 1, list.end());
 }
 
+TEST(ValuesTest, ListEraseRange) {
+  Value::List list;
+  list.Append(1);
+  list.Append(2);
+  list.Append(3);
+  list.Append(4);
+
+  auto next_it = list.erase(list.begin() + 1, list.begin() + 3);
+  ASSERT_EQ(2u, list.size());
+  EXPECT_EQ(list[0], Value(1));
+  EXPECT_EQ(list[1], Value(4));
+  EXPECT_EQ(*next_it, Value(4));
+  EXPECT_EQ(next_it + 1, list.end());
+
+  next_it = list.erase(list.begin() + 1, list.begin() + 1);
+  ASSERT_EQ(2u, list.size());
+  EXPECT_EQ(list[0], Value(1));
+  EXPECT_EQ(list[1], Value(4));
+  EXPECT_EQ(*next_it, Value(4));
+  EXPECT_EQ(next_it + 1, list.end());
+
+  next_it = list.erase(list.begin() + 1, list.end());
+  ASSERT_EQ(1u, list.size());
+  EXPECT_EQ(list[0], Value(1));
+  EXPECT_EQ(next_it, list.end());
+
+  list.clear();
+  next_it = list.erase(list.begin(), list.begin());
+  ASSERT_EQ(0u, list.size());
+  EXPECT_EQ(next_it, list.begin());
+  EXPECT_EQ(next_it, list.end());
+}
+
 TEST(ValuesTest, EraseListIter) {
   ListValue value;
   value.Append(1);
diff --git a/build/android/java/templates/BuildConfig.template b/build/android/java/templates/BuildConfig.template
index c24f76b..b64f6f0 100644
--- a/build/android/java/templates/BuildConfig.template
+++ b/build/android/java/templates/BuildConfig.template
@@ -86,4 +86,10 @@
 #else
     public static MAYBE_FINAL boolean ISOLATED_SPLITS_ENABLED MAYBE_FALSE;
 #endif
+
+#if defined(_IS_FOR_TEST)
+    public static MAYBE_FINAL boolean IS_FOR_TEST = true;
+#else
+    public static MAYBE_FINAL boolean IS_FOR_TEST MAYBE_FALSE;
+#endif
 }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 0345489..1b65065 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2169,6 +2169,10 @@
           ]
         }
       }
+
+      if (defined(testonly) && testonly) {
+        defines += [ "_IS_FOR_TEST" ]
+      }
     }
   }
 
diff --git a/build/config/chromecast_build.gni b/build/config/chromecast_build.gni
index d78a8e7..e06dba9 100644
--- a/build/config/chromecast_build.gni
+++ b/build/config/chromecast_build.gni
@@ -132,8 +132,8 @@
 assert(enable_cast_receiver || !is_cast_audio_only,
        "is_cast_audio_only = true requires enable_cast_receiver = true.")
 
-assert(enable_cast_receiver == is_chromecast,
-       "enable_cast_receiver and is_chromecast must be set to the same value.")
+assert(enable_cast_receiver || !is_chromecast,
+       "is_chromecast = true requires enable_cast_receiver = true.")
 
 assert(enable_cast_receiver || !is_castos,
        "is_castos = true requires enable_cast_receiver = true.")
@@ -143,3 +143,6 @@
        "is_cast_android = true requires enable_cast_receiver = true.")
 assert(is_android || !is_cast_android,
        "is_cast_android = true requires is_android = true.")
+
+assert(!is_fuchsia || !is_chromecast,
+       "is_chromecast is no longer supported on Fuchsia.")
diff --git a/build/fuchsia/ffx_session.py b/build/fuchsia/ffx_session.py
index 282ea64..4d998e1 100755
--- a/build/fuchsia/ffx_session.py
+++ b/build/fuchsia/ffx_session.py
@@ -23,6 +23,10 @@
 import common
 import log_manager
 
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                             'test')))
+from compatible_utils import parse_host_port
+
 
 def get_ffx_path():
   """Returns the full path to `ffx`."""
@@ -30,27 +34,6 @@
                       common.GetHostArchFromPlatform(), 'ffx')
 
 
-def parse_host_port(host_port_pair):
-  """Parses a host name or IP address and a port number from a string of any of
-  the following forms:
-  - hostname:port
-  - IPv4addy:port
-  - [IPv6addy]:port
-
-  Returns:
-    A tuple of the string host name/address and integer port number.
-
-  Raises:
-    ValueError if `host_port_pair` does not contain a colon or if the substring
-      following the last colon cannot be converted to an int.
-  """
-  host, port = host_port_pair.rsplit(':', 1)
-  # Strip the brackets if the host looks like an IPv6 address.
-  if len(host) > 2 and host[0] == '[' and host[-1] == ']':
-    host = host[1:-1]
-  return (host, int(port))
-
-
 def format_host_port(host, port):
   """Formats a host name or IP address and port number into a host:port string.
   """
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 54815e0..34d797b 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220714.1.1
+8.20220714.3.1
diff --git a/build/fuchsia/test/common.py b/build/fuchsia/test/common.py
index 229673f9..7aa074a 100644
--- a/build/fuchsia/test/common.py
+++ b/build/fuchsia/test/common.py
@@ -12,6 +12,8 @@
 from argparse import ArgumentParser
 from typing import Iterable, List, Optional
 
+from compatible_utils import parse_host_port
+
 DIR_SRC_ROOT = os.path.abspath(
     os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
 REPO_ALIAS = 'fuchsia.com'
@@ -168,8 +170,27 @@
 
         run_ffx_command([
             'component', 'create', f'/core/ffx-laboratory:{package}',
-            f'fuchsia-pkg://{REPO_ALIAS}/{package}#meta/{package}.cm'
+            f'fuchsia-pkg://{REPO_ALIAS}/{package}#meta/{package}.cmx'
         ], target_id)
         run_ffx_command(
             ['component', 'resolve', f'/core/ffx-laboratory:{package}'],
             target_id)
+
+
+# TODO(crbug.com/1342460): Remove when Telemetry tests are using CFv2 packages.
+def resolve_v1_packages(packages: List[str], target_id: Optional[str]) -> None:
+    """Ensure that all cfv1 packages are installed on a device."""
+
+    ssh_address = run_ffx_command(('target', 'get-ssh-address'),
+                                  target_id,
+                                  capture_output=True).stdout.strip()
+    address, port = parse_host_port(ssh_address)
+
+    for package in packages:
+        subprocess.run([
+            'ssh', '-F',
+            os.path.expanduser('~/.fuchsia/sshconfig'), address, '-p',
+            str(port), '--', 'pkgctl', 'resolve',
+            'fuchsia-pkg://%s/%s' % (REPO_ALIAS, package)
+        ],
+                       check=True)
diff --git a/build/fuchsia/test/compatible_utils.py b/build/fuchsia/test/compatible_utils.py
index bb5fb4f..15004e9 100644
--- a/build/fuchsia/test/compatible_utils.py
+++ b/build/fuchsia/test/compatible_utils.py
@@ -3,9 +3,34 @@
 # found in the LICENSE file.
 """Functions used in both v1 and v2 scripts."""
 
+from typing import Tuple
+
 _FILTER_DIR = 'testing/buildbot/filters'
 
 
+def parse_host_port(host_port_pair: str) -> Tuple[str, int]:
+    """Parses a host name or IP address and a port number from a string of
+    any of the following forms:
+    - hostname:port
+    - IPv4addy:port
+    - [IPv6addy]:port
+
+    Returns:
+        A tuple of the string host name/address and integer port number.
+
+    Raises:
+        ValueError if `host_port_pair` does not contain a colon or if the
+        substring following the last colon cannot be converted to an int.
+    """
+
+    host, port = host_port_pair.rsplit(':', 1)
+
+    # Strip the brackets if the host looks like an IPv6 address.
+    if len(host) >= 4 and host[0] == '[' and host[-1] == ']':
+        host = host[1:-1]
+    return (host, int(port))
+
+
 # TODO(crbug.com/1279803): Until one can send files to the device when running
 # a test, filter files must be read from the test package.
 def map_filter_file_to_package_file(filter_file: str) -> str:
diff --git a/build/fuchsia/test/run_gpu_test.py b/build/fuchsia/test/run_gpu_test.py
new file mode 100644
index 0000000..6e0cec4
--- /dev/null
+++ b/build/fuchsia/test/run_gpu_test.py
@@ -0,0 +1,42 @@
+# 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.
+"""Implements commands for running GPU tests."""
+
+import argparse
+import os
+import subprocess
+
+from typing import List, Optional
+
+from common import DIR_SRC_ROOT
+from test_runner import TestRunner
+
+_GPU_TEST_SCRIPT = os.path.join(DIR_SRC_ROOT, 'content', 'test', 'gpu',
+                                'run_gpu_integration_test.py')
+
+
+class GPUTestRunner(TestRunner):
+    """Test runner for running GPU tests."""
+
+    def __init__(self, out_dir: str, test_args: List[str],
+                 target_id: Optional[str]) -> None:
+        parser = argparse.ArgumentParser()
+        parser.add_argument(
+            '--browser', help='The browser to use for Telemetry based tests.')
+        args, _ = parser.parse_known_args(test_args)
+        if args.browser == 'web-engine-shell':
+            packages = ['web_engine_with_webui', 'web_engine_shell']
+        elif args.browser == 'fuchsia-chrome':
+            packages = ['chrome']
+        else:
+            raise Exception('Unknown browser %s' % args.browser)
+        super().__init__(out_dir, test_args, packages, target_id)
+
+    def run_test(self):
+        test_cmd = [_GPU_TEST_SCRIPT]
+        if self._test_args:
+            test_cmd.extend(self._test_args)
+        test_cmd.extend(['--chromium-output-directory', self._out_dir])
+        test_cmd.extend(['--fuchsia-target-id', self._target_id])
+        return subprocess.run(test_cmd, check=True)
diff --git a/build/fuchsia/test/run_test.py b/build/fuchsia/test/run_test.py
index 921c5b3..3e56e0b 100755
--- a/build/fuchsia/test/run_test.py
+++ b/build/fuchsia/test/run_test.py
@@ -12,12 +12,13 @@
 from typing import List
 
 from common import register_common_args, register_device_args, \
-                   register_log_args, resolve_packages
+                   register_log_args, resolve_packages, resolve_v1_packages
 from ffx_integration import test_connection
 from log_manager import LogManager, start_system_log
 from publish_package import publish_packages, register_package_args
 from run_blink_test import BlinkTestRunner
 from run_executable_test import create_executable_test_runner
+from run_gpu_test import GPUTestRunner
 from serve_repo import register_serve_args, serve_repository
 from start_emulator import create_emulator_from_args, register_emulator_args
 from test_runner import TestRunner
@@ -26,9 +27,13 @@
 def _get_test_runner(runner_args: argparse.Namespace,
                      test_args: List[str]) -> TestRunner:
     """Initialize a suitable TestRunner class."""
+
     if runner_args.test_type == 'blink':
         return BlinkTestRunner(runner_args.out_dir, test_args,
                                runner_args.target_id)
+    if runner_args.test_type == 'gpu':
+        return GPUTestRunner(runner_args.out_dir, test_args,
+                             runner_args.target_id)
     return create_executable_test_runner(runner_args, test_args)
 
 
@@ -37,7 +42,7 @@
     parser = argparse.ArgumentParser()
     parser.add_argument(
         'test_type',
-        help='The type of test to run. Options include \'blink\''
+        help='The type of test to run. Options include \'blink\', \'gpu\''
         'or in the case of gtests, the gtest name.')
     parser.add_argument('--device',
                         '-d',
@@ -87,7 +92,14 @@
                          not runner_args.no_repo_init)
 
         with serve_repository(runner_args):
-            resolve_packages(test_runner.packages, runner_args.target_id)
+
+            # TODO(crbug.com/1342460): Remove when Telemetry and blink_web_tests
+            # are using CFv2 packages.
+            if runner_args.test_type in ['blink', 'gpu']:
+                resolve_v1_packages(test_runner.packages,
+                                    runner_args.target_id)
+            else:
+                resolve_packages(test_runner.packages, runner_args.target_id)
             return test_runner.run_test().returncode
 
 
diff --git a/build/rust/rs_bindings_from_cc.gni b/build/rust/rs_bindings_from_cc.gni
index 18bb725c..f98c79f 100644
--- a/build/rust/rs_bindings_from_cc.gni
+++ b/build/rust/rs_bindings_from_cc.gni
@@ -152,14 +152,8 @@
     deps = _bindings_deps
     deps += [
       ":${_gen_bindings_target_name}",
+      "//third_party/crubit:deps_of_rs_api_impl",
       _bindings_target,
-
-      # TODO(crbug.com/1297592): Declare the dependencies (the generated
-      # ..._rs_api_impl.cc file #includes headers like
-      # "third_party/crubit/rs_bindings_from_cc/support/offsetof.h"
-      # without telling GN about it...).
-      # See also `deps_for_generated_cc_file` in `crubit/rs_bindings_from_cc/BUILD`.
-      #"//third_party/crubit:rs_api_impl_support/TODO",
     ]
 
     rs_sources = [ _rs_out_path ]
@@ -168,11 +162,7 @@
     rs_deps = _bindings_deps
     rs_deps += [
       ":${_gen_bindings_target_name}",
-      # TODO(crbug.com/1297592): Add required dependencies (the generated bindings will
-      # depend on these extra crates if the generated bindings cover a C++ struct).
-      # See also `deps_for_generated_rs_file` in `crubit/rs_bindings_from_cc/BUILD`.
-      #"//third_party/rust/memoffset/v0_6:TODO",
-      #"//third_party/rust/static_assertions/v1:TODO",
+      "//third_party/crubit:deps_of_rs_api",
     ]
   }
 
diff --git a/build/rust/tests/test_rs_bindings_from_cc/main.rs b/build/rust/tests/test_rs_bindings_from_cc/main.rs
index a41fff3..b8f6546 100644
--- a/build/rust/tests/test_rs_bindings_from_cc/main.rs
+++ b/build/rust/tests/test_rs_bindings_from_cc/main.rs
@@ -11,8 +11,14 @@
 #[cfg(test)]
 mod tests {
     #[test]
-    fn test_self_contained_target() {
+    fn test_self_contained_target_function_call_basics() {
         assert_eq!(100 + 42, ::self_contained_target_rs_api::AddViaCc(100, 42));
         assert_eq!(100 * 42, ::self_contained_target_rs_api::MultiplyViaCc(100, 42));
     }
+
+    #[test]
+    fn test_self_contained_target_pod_struct_basics() {
+        let x = ::self_contained_target_rs_api::CcPodStruct { value: 123 };
+        assert_eq!(x.value, 123);
+    }
 }
diff --git a/build/rust/tests/test_rs_bindings_from_cc/self_contained_target_header2.h b/build/rust/tests/test_rs_bindings_from_cc/self_contained_target_header2.h
index 96c7b9e..6c5c5d7d 100644
--- a/build/rust/tests/test_rs_bindings_from_cc/self_contained_target_header2.h
+++ b/build/rust/tests/test_rs_bindings_from_cc/self_contained_target_header2.h
@@ -7,4 +7,8 @@
 
 int AddViaCc(int x, int y);
 
+struct CcPodStruct {
+  int value;
+};
+
 #endif  // BUILD_RUST_TESTS_TEST_RS_BINDINGS_FROM_CC_SELF_CONTAINED_TARGET_HEADER2_H_
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 30fc1651..ac39ecc5 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -94,6 +94,14 @@
 constexpr int kEventLatencyHistogramBucketCount = 100;
 constexpr base::TimeDelta kHighLatencyMin = base::Milliseconds(75);
 
+// Number of stages of the current PipelineReporter
+constexpr int kNumOfStages = static_cast<int>(StageType::kStageTypeCount);
+// Stores the weight of the most recent data point used in percentage when
+// predicting substages' latency. (It is stored and calculated in percentage
+// since TimeDelta calculate based on microseconds instead of nanoseconds,
+// therefore, decimals of stage durations in microseconds may be lost.)
+constexpr double kWeightOfCurStageInPercent = 25;
+
 std::string GetCompositorLatencyHistogramName(
     FrameReportType report_type,
     FrameSequenceTrackerType frame_sequence_tracker_type,
@@ -1217,6 +1225,66 @@
   DiscardOldPartialUpdateReporters();
 }
 
+void CompositorFrameReporter::CalculateStageLatencyPrediction(
+    std::vector<base::TimeDelta>& previous_predictions) {
+  // `stage_history_` should not be empty since we are calling this function
+  // from DidPresentCompositorFrame(), which means there has to be some sort of
+  // stage data.
+  DCHECK(!stage_history_.empty());
+
+  int index_of_total_latency = static_cast<int>(StageType::kTotalLatency);
+
+  // The bad case of having `previous_predictions` being 0s should never happen
+  // since this function always only record the current PipelineReporter's
+  // duration if its duration is not 0s. Investigate if such rare case happens.
+  DCHECK(!previous_predictions[index_of_total_latency].is_zero())
+      << "previous_predictions should theoretically never have duration of 0s ";
+
+  base::TimeDelta total_pipeline_latency =
+      stage_history_.back().end_time - stage_history_[0].start_time;
+
+  // Do not record current breakdown stages' duration if the total latency of
+  // the current PipelineReporter is 0s. And no further predictions could be
+  // made in this case.
+  if (total_pipeline_latency.is_zero())
+    return;
+
+  // Note that `current_stage_durations` would always have the same length as
+  // `previous_predictions`, since each index represent the breakdown stages of
+  // the PipelineReporter listed at enum class, StageType.
+  std::vector<base::TimeDelta> current_stage_durations(kNumOfStages,
+                                                       base::Microseconds(0));
+  for (auto stage : stage_history_) {
+    base::TimeDelta substageLatency = stage.end_time - stage.start_time;
+    current_stage_durations[static_cast<int>(stage.stage_type)] =
+        substageLatency;
+  }
+  current_stage_durations[index_of_total_latency] = total_pipeline_latency;
+
+  // Do not record current pipeline details or update predictions if no frame
+  // is submitted.
+  if (current_stage_durations
+          [static_cast<int>(
+               StageType::kSubmitCompositorFrameToPresentationCompositorFrame)]
+              .is_zero())
+    return;
+
+  // The previous prediction is initialized to be -1, so check if the current
+  // PipelineReporter is the first reporter ever to be calculated.
+  if (previous_predictions[index_of_total_latency] == base::Microseconds(-1)) {
+    previous_predictions = current_stage_durations;
+  } else {
+    // TODO(crbug.com/1334823): Perform latency attribution.
+
+    for (int i = 0; i < kNumOfStages; i++) {
+      previous_predictions[i] =
+          (kWeightOfCurStageInPercent * current_stage_durations[i] +
+           (100 - kWeightOfCurStageInPercent) * previous_predictions[i]) /
+          100;
+    }
+  }
+}
+
 void CompositorFrameReporter::SetPartialUpdateDecider(
     CompositorFrameReporter* decider) {
   DCHECK(decider);
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index 33f2264..ec805643 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -133,6 +133,9 @@
     kBreakdownCount
   };
 
+  // To distinguish between impl and main reporter
+  enum class ReporterType { kImpl = 0, kMain = 1 };
+
   struct CC_EXPORT StageData {
     StageType stage_type;
     base::TimeTicks start_time;
@@ -353,6 +356,18 @@
   using FrameReportTypes =
       std::bitset<static_cast<size_t>(FrameReportType::kMaxValue) + 1>;
 
+  // This function is called to calculate breakdown stage duration's prediction
+  // based on the `previous_predictions` and update the `previous_predictions`
+  // to the new prediction calculated.
+  void CalculateStageLatencyPrediction(
+      std::vector<base::TimeDelta>& previous_predictions);
+
+  ReporterType get_reporter_type() { return reporter_type_; }
+
+  void set_reporter_type_to_impl() { reporter_type_ = ReporterType::kImpl; }
+
+  void set_reporter_type_to_main() { reporter_type_ = ReporterType::kMain; }
+
  protected:
   void set_has_partial_update(bool has_partial_update) {
     has_partial_update_ = has_partial_update;
@@ -494,6 +509,8 @@
 
   const GlobalMetricsTrackers global_trackers_;
 
+  ReporterType reporter_type_;
+
   base::WeakPtrFactory<CompositorFrameReporter> weak_factory_{this};
 };
 
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc
index 014a1a4a..ed56a293 100644
--- a/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -32,12 +32,12 @@
 class CompositorFrameReporterTest : public testing::Test {
  public:
   CompositorFrameReporterTest() : pipeline_reporter_(CreatePipelineReporter()) {
-    AdvanceNowByMs(1);
+    AdvanceNowByUs(1);
     dropped_frame_counter_.set_total_counter(&total_frame_counter_);
   }
 
  protected:
-  base::TimeTicks AdvanceNowByMs(int advance_ms) {
+  base::TimeTicks AdvanceNowByUs(int advance_ms) {
     test_tick_clock_.Advance(base::Microseconds(advance_ms));
     return test_tick_clock_.NowTicks();
   }
@@ -57,28 +57,28 @@
     breakdown->update_layers = base::Microseconds(1);
 
     // Advance now by the sum of the breakdowns.
-    AdvanceNowByMs(10 + 9 + 8 + 7 + 6 + 5 + 3 + 2 + 1);
+    AdvanceNowByUs(10 + 9 + 8 + 7 + 6 + 5 + 3 + 2 + 1);
 
     return breakdown;
   }
 
   viz::FrameTimingDetails BuildVizBreakdown() {
     viz::FrameTimingDetails viz_breakdown;
-    viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByMs(1);
-    viz_breakdown.draw_start_timestamp = AdvanceNowByMs(2);
-    viz_breakdown.swap_timings.swap_start = AdvanceNowByMs(3);
-    viz_breakdown.swap_timings.swap_end = AdvanceNowByMs(4);
-    viz_breakdown.presentation_feedback.timestamp = AdvanceNowByMs(5);
+    viz_breakdown.received_compositor_frame_timestamp = AdvanceNowByUs(1);
+    viz_breakdown.draw_start_timestamp = AdvanceNowByUs(2);
+    viz_breakdown.swap_timings.swap_start = AdvanceNowByUs(3);
+    viz_breakdown.swap_timings.swap_end = AdvanceNowByUs(4);
+    viz_breakdown.presentation_feedback.timestamp = AdvanceNowByUs(5);
     return viz_breakdown;
   }
 
   std::unique_ptr<EventMetrics> SetupEventMetrics(
       std::unique_ptr<EventMetrics> metrics) {
     if (metrics) {
-      AdvanceNowByMs(3);
+      AdvanceNowByUs(3);
       metrics->SetDispatchStageTimestamp(
           EventMetrics::DispatchStage::kRendererCompositorStarted);
-      AdvanceNowByMs(3);
+      AdvanceNowByUs(3);
       metrics->SetDispatchStageTimestamp(
           EventMetrics::DispatchStage::kRendererCompositorFinished);
     }
@@ -86,15 +86,15 @@
   }
 
   std::unique_ptr<EventMetrics> CreateEventMetrics(ui::EventType type) {
-    const base::TimeTicks event_time = AdvanceNowByMs(3);
-    AdvanceNowByMs(3);
+    const base::TimeTicks event_time = AdvanceNowByUs(3);
+    AdvanceNowByUs(3);
     return SetupEventMetrics(
         EventMetrics::CreateForTesting(type, event_time, &test_tick_clock_));
   }
 
   std::unique_ptr<EventMetrics> CreateScrollBeginMetrics() {
-    const base::TimeTicks event_time = AdvanceNowByMs(3);
-    AdvanceNowByMs(3);
+    const base::TimeTicks event_time = AdvanceNowByUs(3);
+    AdvanceNowByUs(3);
     return SetupEventMetrics(ScrollEventMetrics::CreateForTesting(
         ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kWheel,
         /*is_inertial=*/false, event_time, &test_tick_clock_));
@@ -103,8 +103,8 @@
   std::unique_ptr<EventMetrics> CreateScrollUpdateEventMetrics(
       bool is_inertial,
       ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type) {
-    const base::TimeTicks event_time = AdvanceNowByMs(3);
-    AdvanceNowByMs(3);
+    const base::TimeTicks event_time = AdvanceNowByUs(3);
+    AdvanceNowByUs(3);
     return SetupEventMetrics(ScrollUpdateEventMetrics::CreateForTesting(
         ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kWheel, is_inertial,
         scroll_update_type, /*delta=*/10.0f, event_time, &test_tick_clock_));
@@ -113,8 +113,8 @@
   std::unique_ptr<EventMetrics> CreatePinchEventMetrics(
       ui::EventType type,
       ui::ScrollInputType input_type) {
-    const base::TimeTicks event_time = AdvanceNowByMs(3);
-    AdvanceNowByMs(3);
+    const base::TimeTicks event_time = AdvanceNowByUs(3);
+    AdvanceNowByUs(3);
     return SetupEventMetrics(PinchEventMetrics::CreateForTesting(
         type, input_type, event_time, &test_tick_clock_));
   }
@@ -161,25 +161,25 @@
       Now());
   EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now());
   EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
       Now());
   EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::
           kSubmitCompositorFrameToPresentationCompositorFrame,
       Now());
   EXPECT_EQ(3u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->TerminateFrame(
       CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now());
   EXPECT_EQ(4u, pipeline_reporter_->stage_history_size_for_testing());
@@ -206,12 +206,12 @@
                                  Now());
   EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kEndCommitToActivation, Now());
   EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(2);
+  AdvanceNowByUs(2);
   pipeline_reporter_->TerminateFrame(
       CompositorFrameReporter::FrameTerminationStatus::kReplacedByNewReporter,
       Now());
@@ -230,13 +230,13 @@
       CompositorFrameReporter::StageType::kActivation, Now());
   EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
       Now());
   EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(2);
+  AdvanceNowByUs(2);
   pipeline_reporter_->TerminateFrame(
       CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now());
   EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing());
@@ -266,12 +266,12 @@
       CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now());
   EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit,
                                  Now());
   EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing());
 
-  AdvanceNowByMs(2);
+  AdvanceNowByUs(2);
   pipeline_reporter_->TerminateFrame(
       CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame,
       Now());
@@ -313,24 +313,24 @@
       std::make_move_iterator(std::end(event_metrics_ptrs)));
   std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics);
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
       Now());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
       Now());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::
           kSubmitCompositorFrameToPresentationCompositorFrame,
       Now());
   pipeline_reporter_->AddEventsMetrics(std::move(events_metrics));
 
-  const base::TimeTicks presentation_time = AdvanceNowByMs(3);
+  const base::TimeTicks presentation_time = AdvanceNowByUs(3);
   pipeline_reporter_->TerminateFrame(
       CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame,
       presentation_time);
@@ -405,24 +405,24 @@
       std::make_move_iterator(std::end(event_metrics_ptrs)));
   std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics);
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
       Now());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
       Now());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::
           kSubmitCompositorFrameToPresentationCompositorFrame,
       Now());
   pipeline_reporter_->AddEventsMetrics(std::move(events_metrics));
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown();
   pipeline_reporter_->SetVizBreakdown(viz_breakdown);
   pipeline_reporter_->TerminateFrame(
@@ -493,24 +493,24 @@
       std::make_move_iterator(std::end(event_metrics_ptrs)));
   std::vector<base::TimeTicks> event_times = GetEventTimestamps(events_metrics);
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
       Now());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
       Now());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::
           kSubmitCompositorFrameToPresentationCompositorFrame,
       Now());
   pipeline_reporter_->AddEventsMetrics(std::move(events_metrics));
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   viz::FrameTimingDetails viz_breakdown = BuildVizBreakdown();
   pipeline_reporter_->SetVizBreakdown(viz_breakdown);
   pipeline_reporter_->TerminateFrame(
@@ -575,24 +575,24 @@
       std::make_move_iterator(std::begin(event_metrics_ptrs)),
       std::make_move_iterator(std::end(event_metrics_ptrs)));
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
       Now());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
       Now());
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->StartStage(
       CompositorFrameReporter::StageType::
           kSubmitCompositorFrameToPresentationCompositorFrame,
       Now());
   pipeline_reporter_->AddEventsMetrics(std::move(events_metrics));
 
-  AdvanceNowByMs(3);
+  AdvanceNowByUs(3);
   pipeline_reporter_->TerminateFrame(
       CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame,
       Now());
@@ -720,5 +720,211 @@
       pipeline_reporter_->owned_partial_update_dependents_size_for_testing());
 }
 
+TEST_F(CompositorFrameReporterTest, StageLatencyGeneralPrediction) {
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
+      Now());
+
+  AdvanceNowByUs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now());
+
+  AdvanceNowByUs(3);
+  pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit,
+                                 Now());
+
+  AdvanceNowByUs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kEndCommitToActivation, Now());
+
+  AdvanceNowByUs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kActivation, Now());
+
+  AdvanceNowByUs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
+      Now());
+
+  AdvanceNowByUs(3);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::
+          kSubmitCompositorFrameToPresentationCompositorFrame,
+      Now());
+
+  AdvanceNowByUs(3);
+  pipeline_reporter_->TerminateFrame(
+      CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now());
+
+  int kNumOfStages =
+      static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount);
+
+  // predictions when this is the very first prediction
+  std::vector<base::TimeDelta> expected_latency_predictions1(
+      kNumOfStages - 1, base::Microseconds(3));
+  expected_latency_predictions1.push_back(base::Microseconds(21));
+
+  // predictions when there exists a previous prediction
+  std::vector<base::TimeDelta> expected_latency_predictions2 = {
+      base::Microseconds(1), base::Microseconds(0), base::Microseconds(3),
+      base::Microseconds(0), base::Microseconds(2), base::Microseconds(3),
+      base::Microseconds(0), base::Microseconds(12)};
+
+  std::vector<base::TimeDelta> actual_latency_predictions1(
+      kNumOfStages, base::Microseconds(-1));
+  pipeline_reporter_->CalculateStageLatencyPrediction(
+      actual_latency_predictions1);
+
+  std::vector<base::TimeDelta> actual_latency_predictions2 = {
+      base::Microseconds(1), base::Microseconds(0), base::Microseconds(4),
+      base::Microseconds(0), base::Microseconds(2), base::Microseconds(3),
+      base::Microseconds(0), base::Microseconds(10)};
+  pipeline_reporter_->CalculateStageLatencyPrediction(
+      actual_latency_predictions2);
+
+  EXPECT_EQ(expected_latency_predictions1, actual_latency_predictions1);
+  EXPECT_EQ(expected_latency_predictions2, actual_latency_predictions2);
+
+  pipeline_reporter_ = nullptr;
+}
+
+TEST_F(CompositorFrameReporterTest, StageLatencyAllZeroPrediction) {
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
+      Now());
+
+  AdvanceNowByUs(0);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now());
+
+  AdvanceNowByUs(0);
+  pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit,
+                                 Now());
+
+  AdvanceNowByUs(0);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kEndCommitToActivation, Now());
+
+  AdvanceNowByUs(0);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kActivation, Now());
+
+  AdvanceNowByUs(0);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
+      Now());
+
+  AdvanceNowByUs(0);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::
+          kSubmitCompositorFrameToPresentationCompositorFrame,
+      Now());
+
+  AdvanceNowByUs(0);
+  pipeline_reporter_->TerminateFrame(
+      CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now());
+
+  int kNumOfStages =
+      static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount);
+
+  // predictions when this is the very first prediction
+  std::vector<base::TimeDelta> expected_latency_predictions1(
+      kNumOfStages, base::Microseconds(-1));
+
+  // predictions when there exists a previous prediction
+  std::vector<base::TimeDelta> expected_latency_predictions2 = {
+      base::Microseconds(1), base::Microseconds(0), base::Microseconds(4),
+      base::Microseconds(0), base::Microseconds(2), base::Microseconds(3),
+      base::Microseconds(0), base::Microseconds(10)};
+
+  std::vector<base::TimeDelta> actual_latency_predictions1(
+      kNumOfStages, base::Microseconds(-1));
+  pipeline_reporter_->CalculateStageLatencyPrediction(
+      actual_latency_predictions1);
+
+  std::vector<base::TimeDelta> actual_latency_predictions2 = {
+      base::Microseconds(1), base::Microseconds(0), base::Microseconds(4),
+      base::Microseconds(0), base::Microseconds(2), base::Microseconds(3),
+      base::Microseconds(0), base::Microseconds(10)};
+  pipeline_reporter_->CalculateStageLatencyPrediction(
+      actual_latency_predictions2);
+
+  EXPECT_EQ(expected_latency_predictions1, actual_latency_predictions1);
+  EXPECT_EQ(expected_latency_predictions2, actual_latency_predictions2);
+
+  pipeline_reporter_ = nullptr;
+}
+
+TEST_F(CompositorFrameReporterTest, StageLatencyLargeDurationPrediction) {
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
+      Now());
+
+  AdvanceNowByUs(10000000);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now());
+
+  AdvanceNowByUs(5000000);
+  pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit,
+                                 Now());
+
+  AdvanceNowByUs(6000000);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kEndCommitToActivation, Now());
+
+  AdvanceNowByUs(1000000);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kActivation, Now());
+
+  AdvanceNowByUs(0);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
+      Now());
+
+  AdvanceNowByUs(2000000);
+  pipeline_reporter_->StartStage(
+      CompositorFrameReporter::StageType::
+          kSubmitCompositorFrameToPresentationCompositorFrame,
+      Now());
+
+  AdvanceNowByUs(10000000);
+  pipeline_reporter_->TerminateFrame(
+      CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now());
+
+  int kNumOfStages =
+      static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount);
+
+  // predictions when this is the very first prediction
+  std::vector<base::TimeDelta> expected_latency_predictions1 = {
+      base::Microseconds(10000000), base::Microseconds(5000000),
+      base::Microseconds(6000000),  base::Microseconds(1000000),
+      base::Microseconds(0),        base::Microseconds(2000000),
+      base::Microseconds(10000000), base::Microseconds(34000000)};
+
+  // predictions when there exists a previous prediction
+  std::vector<base::TimeDelta> expected_latency_predictions2 = {
+      base::Microseconds(2500000), base::Microseconds(1250000),
+      base::Microseconds(1500003), base::Microseconds(250000),
+      base::Microseconds(1),       base::Microseconds(500002),
+      base::Microseconds(2500000), base::Microseconds(8500007)};
+
+  std::vector<base::TimeDelta> actual_latency_predictions1(
+      kNumOfStages, base::Microseconds(-1));
+  pipeline_reporter_->CalculateStageLatencyPrediction(
+      actual_latency_predictions1);
+
+  std::vector<base::TimeDelta> actual_latency_predictions2 = {
+      base::Microseconds(1), base::Microseconds(0), base::Microseconds(4),
+      base::Microseconds(0), base::Microseconds(2), base::Microseconds(3),
+      base::Microseconds(0), base::Microseconds(10)};
+  pipeline_reporter_->CalculateStageLatencyPrediction(
+      actual_latency_predictions2);
+
+  EXPECT_EQ(expected_latency_predictions1, actual_latency_predictions1);
+  EXPECT_EQ(expected_latency_predictions2, actual_latency_predictions2);
+
+  pipeline_reporter_ = nullptr;
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc
index b7d8e9c2..aa12ca9 100644
--- a/cc/metrics/compositor_frame_reporting_controller.cc
+++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -23,6 +23,7 @@
 using FrameTerminationStatus = CompositorFrameReporter::FrameTerminationStatus;
 
 constexpr char kTraceCategory[] = "cc,benchmark";
+constexpr int kNumOfStages = static_cast<int>(StageType::kStageTypeCount);
 }  // namespace
 
 CompositorFrameReportingController::CompositorFrameReportingController(
@@ -31,7 +32,9 @@
     int layer_tree_host_id)
     : should_report_histograms_(should_report_histograms),
       layer_tree_host_id_(layer_tree_host_id),
-      latency_ukm_reporter_(std::make_unique<LatencyUkmReporter>()) {
+      latency_ukm_reporter_(std::make_unique<LatencyUkmReporter>()),
+      previous_latency_predictions_main_(kNumOfStages, base::Microseconds(-1)),
+      previous_latency_predictions_impl_(kNumOfStages, base::Microseconds(-1)) {
   if (should_report_ukm) {
     // UKM metrics should be reported if and only if `latency_ukm_reporter` is
     // set on `global_trackers_`.
@@ -316,6 +319,7 @@
     main_reporter->AddEventsMetrics(
         std::move(events_metrics.main_event_metrics));
     main_reporter->set_has_missing_content(has_missing_content);
+    main_reporter->set_reporter_type_to_main();
     submitted_compositor_frames_.emplace_back(frame_token,
                                               std::move(main_reporter));
   }
@@ -330,6 +334,7 @@
     impl_reporter->set_has_missing_content(has_missing_content);
     impl_reporter->set_is_accompanied_by_main_thread_update(
         is_activated_frame_new);
+    impl_reporter->set_reporter_type_to_impl();
     submitted_compositor_frames_.emplace_back(frame_token,
                                               std::move(impl_reporter));
   }
@@ -471,6 +476,17 @@
     reporter->TerminateFrame(termination_status,
                              details.presentation_feedback.timestamp);
 
+    switch (reporter->get_reporter_type()) {
+      case CompositorFrameReporter::ReporterType::kImpl:
+        reporter->CalculateStageLatencyPrediction(
+            previous_latency_predictions_impl_);
+        break;
+      case CompositorFrameReporter::ReporterType::kMain:
+        reporter->CalculateStageLatencyPrediction(
+            previous_latency_predictions_main_);
+        break;
+    }
+
     if (termination_status == FrameTerminationStatus::kPresentedFrame) {
       // If there are outstanding metrics from dropped frames older than this
       // frame, this frame would be the first frame presented after those
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 3d1640c..07cb748 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -236,6 +236,11 @@
 
   // interval of last begin frame args.
   base::TimeDelta last_interval_;
+
+  // These variables store the breakdown stage latency predictions made based
+  // on impl and main reporter's previous frames.
+  std::vector<base::TimeDelta> previous_latency_predictions_main_;
+  std::vector<base::TimeDelta> previous_latency_predictions_impl_;
 };
 
 }  // namespace cc
diff --git a/chrome/VERSION b/chrome/VERSION
index b23a5a9c..8709312 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=105
 MINOR=0
-BUILD=5181
+BUILD=5182
 PATCH=0
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
index dc5fef5..5c89dd3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -14,6 +14,7 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.SystemClock;
@@ -332,9 +333,9 @@
             }
 
             @Override
-            public void triggerBitmapCapture() {
+            public Bitmap getBitmap() {
                 long startTime = SystemClock.elapsedRealtime();
-                super.triggerBitmapCapture();
+                Bitmap bitmap = super.getBitmap();
                 long elapsed = SystemClock.elapsedRealtime() - startTime;
                 if (elapsed == 0) elapsed = 1;
 
@@ -348,6 +349,7 @@
                 mSuppressedUntil = SystemClock.elapsedRealtime() + suppressedFor;
                 Log.d(TAG, "DynamicView: spent %dms on getBitmap, suppress updating for %dms.",
                         elapsed, suppressedFor);
+                return bitmap;
             }
         };
         mDynamicView.setDownsamplingScale(getDownsamplingScale());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index f378393..5d48e17 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -1129,7 +1129,8 @@
             mRedirectHandler.updateNewUrlLoading(navigationHandle.pageTransition(),
                     navigationHandle.isRedirect(), navigationHandle.hasUserGesture(),
                     mLastUserInteractionTimeSupplier.get(),
-                    RedirectHandler.NO_COMMITTED_ENTRY_INDEX, true /* isInitialNavigation */);
+                    RedirectHandler.NO_COMMITTED_ENTRY_INDEX, true /* isInitialNavigation */,
+                    navigationHandle.isRendererInitiated());
             ExternalNavigationParams params =
                     new ExternalNavigationParams
                             .Builder(escapedUrl, false, navigationHandle.getReferrerUrl(),
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 8b06fc6..1bc404c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -1060,7 +1060,7 @@
         }
 
         @Override
-        public void onActionRecorded(String action) {
+        public void onResult(String action) {
             for (String entry : mUserActionPrefixes) {
                 if (action.startsWith(entry)) {
                     mUserActionCounts.put(entry, mUserActionCounts.get(entry) + 1);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
index dcc3b5ef12..e9addd9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
@@ -17,6 +17,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
+import android.os.PatternMatcher;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.lifecycle.Stage;
@@ -119,6 +120,7 @@
     public CustomTabActivityTestRule mCustomTabActivityRule = new CustomTabActivityTestRule();
 
     private static final String BASE_PATH = "/chrome/test/data/android/url_overriding/";
+    private static final String HELLO_PAGE = BASE_PATH + "hello.html";
     private static final String NAVIGATION_FROM_TIMEOUT_PAGE =
             BASE_PATH + "navigation_from_timer.html";
     private static final String NAVIGATION_FROM_TIMEOUT_WITH_FALLBACK_PAGE =
@@ -588,15 +590,25 @@
         final Tab tab = mActivityTestRule.getActivity().getActivityTab();
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> RedirectHandlerTabHelper.swapHandlerFor(tab, mSpyRedirectHandler));
-
         // This is a little fragile to code changes, but better than waiting 15 real seconds.
-        Mockito.doReturn(SystemClock.elapsedRealtime()) // Initial Navigation create
-                .doReturn(SystemClock.elapsedRealtime()) // Initial Navigation shouldOverride
-                .doReturn(SystemClock.elapsedRealtime()) // XHR Navigation create
-                .doReturn(SystemClock.elapsedRealtime()
-                        + RedirectHandler.NAVIGATION_CHAIN_TIMEOUT_MILLIS + 1) // xhr callback
-                .when(mSpyRedirectHandler)
-                .currentRealtime();
+        if (RedirectHandler.isRefactoringEnabled()) {
+            Mockito.doReturn(SystemClock.elapsedRealtime()) // Initial Navigation create
+                    .doReturn(SystemClock.elapsedRealtime()) // Initial Navigation shouldOverride
+                    .doReturn(SystemClock.elapsedRealtime()) // XHR Navigation create
+                    .doReturn(SystemClock.elapsedRealtime()) // XHR callback navigation create
+                    .doReturn(SystemClock.elapsedRealtime()
+                            + RedirectHandler.NAVIGATION_CHAIN_TIMEOUT_MILLIS + 1) // xhr callback
+                    .when(mSpyRedirectHandler)
+                    .currentRealtime();
+        } else {
+            Mockito.doReturn(SystemClock.elapsedRealtime()) // Initial Navigation create
+                    .doReturn(SystemClock.elapsedRealtime()) // Initial Navigation shouldOverride
+                    .doReturn(SystemClock.elapsedRealtime()) // XHR Navigation create
+                    .doReturn(SystemClock.elapsedRealtime()
+                            + RedirectHandler.NAVIGATION_CHAIN_TIMEOUT_MILLIS + 1) // xhr callback
+                    .when(mSpyRedirectHandler)
+                    .currentRealtime();
+        }
 
         @OverrideUrlLoadingResultType
         int result = loadUrlAndWaitForIntentUrl(
@@ -809,8 +821,8 @@
 
     private void runRedirectToOtherBrowserTest(Instrumentation.ActivityResult chooserResult) {
         Context context = ContextUtils.getApplicationContext();
-        Intent intent = new Intent(
-                Intent.ACTION_VIEW, Uri.parse(mTestServer.getURL(REDIRECT_TO_OTHER_BROWSER)));
+        String targetUrl = getRedirectToOtherBrowserUrl();
+        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(targetUrl));
         intent.setClassName(context, ChromeLauncherActivity.class.getName());
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
@@ -829,6 +841,25 @@
         InstrumentationRegistry.getInstrumentation().removeMonitor(monitor);
     }
 
+    private String getRedirectToOtherBrowserUrl() {
+        // Strip off the "https:" for intent scheme formatting.
+        String redirectUrl = mTestServer.getURL(HELLO_PAGE).substring(6);
+        byte[] param = ApiCompatibilityUtils.getBytesUtf8("PARAM_URL");
+        byte[] value = ApiCompatibilityUtils.getBytesUtf8(redirectUrl);
+        return mTestServer.getURL(REDIRECT_TO_OTHER_BROWSER)
+                + "?replace_text=" + Base64.encodeToString(param, Base64.URL_SAFE) + ":"
+                + Base64.encodeToString(value, Base64.URL_SAFE);
+    }
+
+    private IntentFilter createHelloIntentFilter() {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_VIEW);
+        filter.addDataScheme(UrlConstants.HTTPS_SCHEME);
+        filter.addCategory(Intent.CATEGORY_BROWSABLE);
+        filter.addDataAuthority("*", null);
+        filter.addDataPath(HELLO_PAGE, PatternMatcher.PATTERN_LITERAL);
+        return filter;
+    }
+
     @Test
     @LargeTest
     public void testRedirectToOtherBrowser_ChooseSelf() throws TimeoutException {
@@ -840,9 +871,8 @@
 
         // Wait for the target (data) URL to load in the tab.
         CriteriaHelper.pollUiThread(() -> {
-            Criteria.checkThat(
-                    mActivityTestRule.getActivity().getActivityTab().getUrl().getScheme(),
-                    Matchers.is(UrlConstants.DATA_SCHEME));
+            Criteria.checkThat(mActivityTestRule.getActivity().getActivityTab().getUrl().getSpec(),
+                    Matchers.is(mTestServer.getURL(HELLO_PAGE)));
         });
     }
 
@@ -850,9 +880,7 @@
     @LargeTest
     public void testRedirectToOtherBrowser_ChooseOther() throws TimeoutException {
         mTestContext.setResolveBrowserIntentToNonBrowserPackage(false);
-        IntentFilter filter = new IntentFilter(Intent.ACTION_VIEW);
-        filter.addDataScheme(UrlConstants.DATA_SCHEME);
-        filter.addCategory(Intent.CATEGORY_BROWSABLE);
+        IntentFilter filter = createHelloIntentFilter();
         Instrumentation.ActivityMonitor monitor =
                 InstrumentationRegistry.getInstrumentation().addMonitor(filter, null, true);
 
@@ -872,15 +900,13 @@
     @LargeTest
     public void testRedirectToOtherBrowser_DefaultNonBrowserPackage() throws TimeoutException {
         mTestContext.setResolveBrowserIntentToNonBrowserPackage(true);
-        IntentFilter filter = new IntentFilter(Intent.ACTION_VIEW);
-        filter.addDataScheme(UrlConstants.DATA_SCHEME);
-        filter.addCategory(Intent.CATEGORY_BROWSABLE);
+        IntentFilter filter = createHelloIntentFilter();
         Instrumentation.ActivityMonitor viewMonitor =
                 InstrumentationRegistry.getInstrumentation().addMonitor(filter, null, true);
 
         Context context = ContextUtils.getApplicationContext();
-        Intent intent = new Intent(
-                Intent.ACTION_VIEW, Uri.parse(mTestServer.getURL(REDIRECT_TO_OTHER_BROWSER)));
+        String targetUrl = getRedirectToOtherBrowserUrl();
+        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(targetUrl));
         intent.setClassName(context, ChromeLauncherActivity.class.getName());
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
index d57649ccf..cf26964 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
@@ -10,13 +10,13 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.Build;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 
+import androidx.test.espresso.Espresso;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 
@@ -28,6 +28,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.task.PostTask;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
@@ -53,6 +54,8 @@
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.FullscreenTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.content_public.browser.GestureListenerManager;
 import org.chromium.content_public.browser.GestureStateListener;
 import org.chromium.content_public.browser.SelectionPopupController;
@@ -65,6 +68,7 @@
 import org.chromium.content_public.browser.test.util.UiUtils;
 import org.chromium.content_public.browser.test.util.WebContentsUtils;
 
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -76,6 +80,7 @@
 @CommandLineFlags.Add({
         ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
 })
+@Batch(Batch.PER_CLASS)
 public class FullscreenManagerTest {
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@@ -190,6 +195,31 @@
     @Test
     @MediumTest
     @Feature({"Fullscreen"})
+    @DisableFeatures({ChromeFeatureList.BACK_GESTURE_REFACTOR})
+    public void testBackPressExitPersistentFullscreen() {
+        testBackPressExitPersistentFullscreenInternal();
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Fullscreen"})
+    @EnableFeatures({ChromeFeatureList.BACK_GESTURE_REFACTOR})
+    public void testBackPressExitPersistentFullscreen_backGestureRefactor() {
+        testBackPressExitPersistentFullscreenInternal();
+    }
+
+    private void testBackPressExitPersistentFullscreenInternal() {
+        launchOnFullscreenMode(LONG_HTML_TEST_PAGE);
+        Assert.assertTrue(getPersistentFullscreenMode());
+
+        Espresso.pressBack();
+
+        Assert.assertFalse(getPersistentFullscreenMode());
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Fullscreen"})
     public void testDelayedPersistentFullscreen() {
         mActivityTestRule.startMainActivityWithURL(LONG_HTML_TEST_PAGE);
 
@@ -213,18 +243,34 @@
     }
 
     private boolean getPersistentFullscreenMode() {
-        return TestThreadUtils.runOnUiThreadBlockingNoException(
+        boolean b1 = TestThreadUtils.runOnUiThreadBlockingNoException(
                 mActivityTestRule.getActivity()
                         .getFullscreenManager()::getPersistentFullscreenMode);
+        Boolean b2 = TestThreadUtils.runOnUiThreadBlockingNoException(
+                mActivityTestRule.getActivity()
+                        .getFullscreenManager()
+                        .getPersistentFullscreenModeSupplier()::get);
+        Assert.assertTrue("Fullscreen mode supplier is holding a different value.",
+                (b2 == null && !b1) || Objects.equals(b1, b2));
+        return b1;
+    }
+
+    private void launchOnFullscreenMode(String url) {
+        mActivityTestRule.startMainActivityWithURL(url);
+
+        Tab tab = mActivityTestRule.getActivity().getActivityTab();
+        final TabWebContentsDelegateAndroid delegate = TabTestUtils.getTabWebContentsDelegate(tab);
+
+        FullscreenTestUtils.waitForFullscreenFlag(tab, false, mActivityTestRule.getActivity());
+        FullscreenTestUtils.waitForPersistentFullscreen(delegate, false);
+        FullscreenTestUtils.togglePersistentFullscreenAndAssert(
+                tab, true, mActivityTestRule.getActivity());
     }
 
     @Test
     @LargeTest
     @Feature({"Fullscreen"})
     public void testPersistentFullscreenChangingUiFlags() throws InterruptedException {
-        // Exiting fullscreen via UI Flags is not supported in versions prior to MR2.
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return;
-
         mActivityTestRule.startMainActivityWithURL(LONG_HTML_TEST_PAGE);
 
         final Tab tab = mActivityTestRule.getActivity().getActivityTab();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/OWNERS
new file mode 100644
index 0000000..207f44d
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/fullscreen/android/OWNERS
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/video/FullscreenVideoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/video/FullscreenVideoTest.java
index 5ca4be6..40767c3b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/video/FullscreenVideoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/video/FullscreenVideoTest.java
@@ -6,8 +6,8 @@
 
 import android.graphics.Rect;
 import android.support.test.InstrumentationRegistry;
-import android.view.KeyEvent;
 
+import androidx.test.espresso.Espresso;
 import androidx.test.filters.MediumTest;
 
 import org.hamcrest.Matchers;
@@ -17,20 +17,22 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.FlakyTest;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.browser.app.ChromeActivity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.fullscreen.FullscreenManager;
-import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.FullscreenTestUtils;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.DOMUtils;
-import org.chromium.content_public.browser.test.util.KeyUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.media.MediaSwitches;
@@ -44,27 +46,15 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
         MediaSwitches.AUTOPLAY_NO_GESTURE_REQUIRED_POLICY})
+@Batch(Batch.PER_CLASS)
 public class FullscreenVideoTest {
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
     @Rule
     public EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule();
 
-    private static final int TEST_TIMEOUT = 3000;
-    private boolean mIsTabFullscreen;
     private ChromeActivity mActivity;
 
-    private class FullscreenToggleListener implements FullscreenManager.Observer {
-        @Override
-        public void onEnterFullscreen(Tab tab, FullscreenOptions options) {
-            mIsTabFullscreen = true;
-        }
-        @Override
-        public void onExitFullscreen(Tab tab) {
-            mIsTabFullscreen = false;
-        }
-    }
-
     @Before
     public void setUp() throws InterruptedException {
         mActivityTestRule.startMainActivityOnBlankPage();
@@ -77,26 +67,30 @@
      */
     @Test
     @MediumTest
-    @FlakyTest(message = "crbug.com/458368")
+    @DisabledTest(message = "Flaky https://crbug.com/458368 https://crbug.com/1331504")
+    @DisableFeatures({ChromeFeatureList.BACK_GESTURE_REFACTOR})
     public void testExitFullscreenNotifiesTabObservers() {
-        String url = mTestServerRule.getServer().getURL(
-                "/chrome/test/data/android/media/video-fullscreen.html");
-        mActivityTestRule.loadUrl(url);
-        Tab tab = mActivity.getActivityTab();
-        FullscreenManager.Observer listener = new FullscreenToggleListener();
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mActivity.getFullscreenManager().addObserver(listener));
+        testExitFullscreenNotifiesTabObserversInternal();
+    }
 
-        TestTouchUtils.singleClickView(
-                InstrumentationRegistry.getInstrumentation(), tab.getView(), 500, 500);
-        waitForVideoToEnterFullscreen();
-        // Key events have to be dispached on UI thread.
-        KeyUtils.singleKeyEventActivity(
-                InstrumentationRegistry.getInstrumentation(), mActivity, KeyEvent.KEYCODE_BACK);
+    @Test
+    @MediumTest
+    @DisabledTest(message = "Flaky https://crbug.com/458368 https://crbug.com/1331504")
+    @EnableFeatures({ChromeFeatureList.BACK_GESTURE_REFACTOR})
+    public void testExitFullscreenNotifiesTabObservers_backGestureRefactor() {
+        testExitFullscreenNotifiesTabObserversInternal();
+    }
+
+    private void testExitFullscreenNotifiesTabObserversInternal() {
+        String url = launchOnFullscreenMode();
+
+        Espresso.pressBack();
 
         waitForTabToExitFullscreen();
-        Assert.assertEquals("URL mismatch after exiting fullscreen video", url,
-                mActivity.getActivityTab().getUrl().getSpec());
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals("URL mismatch after exiting fullscreen video", url,
+                    mActivity.getActivityTab().getUrl().getSpec());
+        });
     }
 
     /**
@@ -113,9 +107,6 @@
         mActivityTestRule.loadUrl(url);
 
         final Tab tab = mActivity.getActivityTab();
-        FullscreenManager.Observer listener = new FullscreenToggleListener();
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mActivity.getFullscreenManager().addObserver(listener));
 
         // Start playback to guarantee it's properly loaded.
         WebContents webContents = mActivity.getCurrentWebContents();
@@ -137,13 +128,23 @@
         Assert.assertEquals(expectedSize, tab.getWebContents().getFullscreenVideoSize());
     }
 
+    private String launchOnFullscreenMode() {
+        String url = mTestServerRule.getServer().getURL(
+                "/chrome/test/data/android/media/video-fullscreen.html");
+        mActivityTestRule.loadUrl(url);
+        final Tab tab = mActivity.getActivityTab();
+
+        TestTouchUtils.singleClickView(
+                InstrumentationRegistry.getInstrumentation(), tab.getView(), 500, 500);
+        waitForVideoToEnterFullscreen();
+        return url;
+    }
+
     void waitForVideoToEnterFullscreen() {
-        CriteriaHelper.pollInstrumentationThread(
-                () -> mIsTabFullscreen, TEST_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+        FullscreenTestUtils.waitForFullscreenFlag(mActivity.getActivityTab(), true, mActivity);
     }
 
     void waitForTabToExitFullscreen() {
-        CriteriaHelper.pollInstrumentationThread(
-                () -> !mIsTabFullscreen, TEST_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+        FullscreenTestUtils.waitForFullscreenFlag(mActivity.getActivityTab(), false, mActivity);
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/QualityEnforcerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/QualityEnforcerUnitTest.java
index f785a16..b19cecb 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/QualityEnforcerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/QualityEnforcerUnitTest.java
@@ -266,8 +266,8 @@
                 false /* isReload */);
         navigation.didFinish(url, false /* isErrorPage */, true /* hasCommitted */,
                 false /* isFragmentNavigation */, false /* isDownload */,
-                false /* isValidSearchFormUrl */, 0 /* pageTransition */, errorCode,
-                httpStatusCode);
+                false /* isValidSearchFormUrl */, 0 /* pageTransition */, errorCode, httpStatusCode,
+                false /* isExternalProtocol */);
         for (CustomTabTabObserver tabObserver : mTabObserverCaptor.getAllValues()) {
             tabObserver.onDidFinishNavigation(mTab, navigation);
         }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/CurrentPageVerifierTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/CurrentPageVerifierTest.java
index 10cebb9..1a136ba 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/CurrentPageVerifierTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/CurrentPageVerifierTest.java
@@ -199,7 +199,7 @@
         navigation.didFinish(gurl, false /* isErrorPage */, true /* hasCommitted */,
                 false /* isFragmentNavigation */, false /* isDownload */,
                 false /* isValidSearchFormUrl */, 0 /* pageTransition */, 0 /* errorCode*/,
-                200 /* httpStatusCode*/);
+                200 /* httpStatusCode*/, false /* isExternalProtocol */);
         for (CustomTabTabObserver tabObserver : mTabObserverCaptor.getAllValues()) {
             tabObserver.onDidFinishNavigation(mTab, navigation);
         }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsUnitTest.java
index 57a193e..0ecd19b2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsUnitTest.java
@@ -28,9 +28,9 @@
 import org.robolectric.annotation.Implements;
 import org.robolectric.shadows.ShadowLooper;
 
-import org.chromium.base.metrics.test.ShadowRecordUserAction;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.homepage.HomepageManager;
 import org.chromium.chrome.browser.homepage.HomepagePolicyManager;
@@ -53,8 +53,7 @@
  * Test for {@link HomepageSettings} to check the UI components and the interactions.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowGURL.class, ShadowUrlFormatter.class, ShadowLooper.class,
-                ShadowRecordUserAction.class})
+@Config(shadows = {ShadowGURL.class, ShadowUrlFormatter.class, ShadowLooper.class})
 public class HomepageSettingsUnitTest {
     private static final String ASSERT_MESSAGE_SWITCH_ENABLE = "Switch should be enabled.";
     private static final String ASSERT_MESSAGE_SWITCH_DISABLE = "Switch should be disabled.";
@@ -107,6 +106,8 @@
     private ActivityScenario<TestActivity> mActivityScenario;
     private TestActivity mActivity;
 
+    private UserActionTester mActionTester;
+
     private ChromeSwitchPreference mSwitch;
     private RadioButtonGroupHomepagePreference mRadioGroupPreference;
 
@@ -124,14 +125,15 @@
             // Needed for HomepageSettings to inflate correctly.
             mActivity.setTheme(R.style.ColorOverlay_ChromiumAndroid);
         });
+        mActionTester = new UserActionTester();
     }
 
     @After
     public void tearDown() {
-        ShadowRecordUserAction.reset();
         mActivityScenario.close();
         PartnerBrowserCustomizations.setInstanceForTesting(null);
         HomepagePolicyManager.setInstanceForTests(null);
+        mActionTester.tearDown();
     }
 
     private void launchHomepageSettings() {
@@ -554,7 +556,6 @@
     private void assertUserActionRecorded(boolean recorded) {
         Assert.assertEquals(
                 "User action <Settings.Homepage.LocationChanged_V2> record differently.", recorded,
-                ShadowRecordUserAction.getSamples().contains(
-                        "Settings.Homepage.LocationChanged_V2"));
+                mActionTester.getActions().contains("Settings.Homepage.LocationChanged_V2"));
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTestTabHolder.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTestTabHolder.java
index 3add0ca4..21213d8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTestTabHolder.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationTestTabHolder.java
@@ -137,7 +137,7 @@
         navigation.didFinish(gurl, false /* isErrorPage */, true /* hasCommitted */,
                 false /* isFragmentNavigation */, false /* isDownload */,
                 false /* isValidSearchFormUrl */, 0 /* pageTransition */, 0 /* errorCode */,
-                200 /* httpStatusCode */);
+                200 /* httpStatusCode */, false /* isExternalProtocol */);
         mMediaSessionTabHelper.mMediaSessionHelper.mWebContentsObserver.didFinishNavigation(
                 navigation);
     }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index a97d049..fde2e47 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3034,7 +3034,7 @@
           Device Credentials
         </message>
         <!--
-          NOTE: As per Bluetooth v4.2 spec. (section 3.2.3.2):
+          NOTE: As per Bluetooth spec. v4.2 [Vol 3, Part C] (section 3.2.3.2):
           > When the Bluetooth PIN is referred to on the UI level, the term
           > 'Bluetooth Passkey' should be used."
         -->
@@ -3047,6 +3047,12 @@
         <message name="IDS_BLUETOOTH_DEVICE_PAIR_CONFIRM_LABEL" desc="Label of the text prompt for Bluetooth device pair confirmation.">
           Bluetooth device <ph name="DEVICE">$1</ph> would like permission to pair.
         </message>
+        <message name="IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_TITLE" desc="Title of the Bluetooth device pair passkey confirmation prompt dialog.">
+          Confirm passkey
+        </message>
+        <message name="IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_LABEL" desc="Label of the text prompt for Bluetooth device pair passkey confirmation.">
+          Please confirm that <ph name="PASSKEY">$1<ex>123456</ex></ph> is the passkey displayed on Bluetooth device <ph name="DEVICE">$2</ph>.
+        </message>
       </if>
 
       <!-- Content blocking strings -->
diff --git a/chrome/app/generated_resources_grd/IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_LABEL.png.sha1
new file mode 100644
index 0000000..28ee6bc
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_LABEL.png.sha1
@@ -0,0 +1 @@
+6c493a98dfb69b598a1a5af841ea29eb6510f475
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_TITLE.png.sha1
new file mode 100644
index 0000000..28ee6bc
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_TITLE.png.sha1
@@ -0,0 +1 @@
+6c493a98dfb69b598a1a5af841ea29eb6510f475
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d0855d8..28607bf 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -8883,6 +8883,11 @@
      FEATURE_VALUE_TYPE(ash::features::kSeamlessRefreshRateSwitching)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+    {"clipboard-unsanitized-content",
+     flag_descriptions::kClipboardUnsanitizedContentName,
+     flag_descriptions::kClipboardUnsanitizedContentDescription, kOsAll,
+     FEATURE_VALUE_TYPE(blink::features::kClipboardUnsanitizedContent)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/omnibox/omnibox_prerender.cc b/chrome/browser/android/omnibox/omnibox_prerender.cc
index b150aac..5f2e4917 100644
--- a/chrome/browser/android/omnibox/omnibox_prerender.cc
+++ b/chrome/browser/android/omnibox/omnibox_prerender.cc
@@ -109,7 +109,8 @@
   action_predictor->
       RegisterTransitionalMatches(url_string, *autocomplete_result);
   AutocompleteActionPredictor::Action recommended_action =
-      action_predictor->RecommendAction(url_string, *default_match);
+      action_predictor->RecommendAction(url_string, *default_match,
+                                        web_contents);
 
   GURL current_url = GURL(current_url_string);
   // Ask for prerendering if the destination URL is different than the
diff --git a/chrome/browser/apps/app_service/app_launch_params.cc b/chrome/browser/apps/app_service/app_launch_params.cc
index f346ae9..684417e 100644
--- a/chrome/browser/apps/app_service/app_launch_params.cc
+++ b/chrome/browser/apps/app_service/app_launch_params.cc
@@ -11,7 +11,7 @@
 AppLaunchParams::AppLaunchParams(const std::string& app_id,
                                  LaunchContainer container,
                                  WindowOpenDisposition disposition,
-                                 apps::mojom::LaunchSource launch_source,
+                                 apps::LaunchSource launch_source,
                                  int64_t display_id)
     : app_id(app_id),
       container(container),
@@ -23,7 +23,7 @@
 AppLaunchParams::AppLaunchParams(const std::string& app_id,
                                  LaunchContainer container,
                                  WindowOpenDisposition disposition,
-                                 apps::mojom::LaunchSource launch_source,
+                                 apps::LaunchSource launch_source,
                                  int64_t display_id,
                                  const std::vector<base::FilePath>& files,
                                  const IntentPtr& intentPtr)
diff --git a/chrome/browser/apps/app_service/app_launch_params.h b/chrome/browser/apps/app_service/app_launch_params.h
index 0b211de..2c0989c 100644
--- a/chrome/browser/apps/app_service/app_launch_params.h
+++ b/chrome/browser/apps/app_service/app_launch_params.h
@@ -25,13 +25,13 @@
   AppLaunchParams(const std::string& app_id,
                   LaunchContainer container,
                   WindowOpenDisposition disposition,
-                  apps::mojom::LaunchSource launch_source,
+                  apps::LaunchSource launch_source,
                   int64_t display_id = display::kInvalidDisplayId);
 
   AppLaunchParams(const std::string& app_id,
                   LaunchContainer container,
                   WindowOpenDisposition disposition,
-                  apps::mojom::LaunchSource launch_source,
+                  apps::LaunchSource launch_source,
                   int64_t display_id,
                   const std::vector<base::FilePath>& files,
                   const IntentPtr& intentPtr);
@@ -79,7 +79,7 @@
 
   // Record where the app is launched from for tracking purpose.
   // Different app may have their own enumeration of sources.
-  apps::mojom::LaunchSource launch_source;
+  apps::LaunchSource launch_source;
 
   // The id of the display from which the app is launched.
   // display::kInvalidDisplayId means that the default display for new windows
diff --git a/chrome/browser/apps/app_service/app_service_proxy_ash.cc b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
index 8f1e859..4a0ff92 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.cc
@@ -568,7 +568,7 @@
 void AppServiceProxyAsh::RecordAppPlatformMetrics(
     Profile* profile,
     const apps::AppUpdate& update,
-    apps::mojom::LaunchSource launch_source,
+    apps::LaunchSource launch_source,
     apps::LaunchContainer container) {
   RecordAppLaunchMetrics(profile, update.AppType(), update.AppId(),
                          launch_source, container);
@@ -593,9 +593,8 @@
 }
 
 void AppServiceProxyAsh::PerformPostLaunchTasks(
-    apps::mojom::LaunchSource launch_source) {
-  if (apps_util::IsHumanLaunch(
-          ConvertMojomLaunchSourceToLaunchSource(launch_source))) {
+    apps::LaunchSource launch_source) {
+  if (apps_util::IsHumanLaunch(launch_source)) {
     ash::full_restore::FullRestoreService::MaybeCloseNotification(profile_);
   }
 }
diff --git a/chrome/browser/apps/app_service/app_service_proxy_ash.h b/chrome/browser/apps/app_service/app_service_proxy_ash.h
index 85e4bfa..00e0284f 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_ash.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_ash.h
@@ -197,11 +197,11 @@
   void OnAppRegistryCacheWillBeDestroyed(
       apps::AppRegistryCache* cache) override;
 
-  void PerformPostLaunchTasks(apps::mojom::LaunchSource launch_source) override;
+  void PerformPostLaunchTasks(apps::LaunchSource launch_source) override;
 
   void RecordAppPlatformMetrics(Profile* profile,
                                 const apps::AppUpdate& update,
-                                apps::mojom::LaunchSource launch_source,
+                                apps::LaunchSource launch_source,
                                 apps::LaunchContainer container) override;
 
   void InitAppPlatformMetrics();
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.cc b/chrome/browser/apps/app_service/app_service_proxy_base.cc
index 2078a69..aed8fcb 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_base.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_base.cc
@@ -278,22 +278,24 @@
 
 void AppServiceProxyBase::Launch(const std::string& app_id,
                                  int32_t event_flags,
-                                 apps::mojom::LaunchSource launch_source,
+                                 apps::mojom::LaunchSource mojom_launch_source,
                                  apps::mojom::WindowInfoPtr window_info) {
   if (app_service_.is_connected()) {
     app_registry_cache_.ForOneApp(
-        app_id, [this, event_flags, launch_source,
+        app_id, [this, event_flags, mojom_launch_source,
                  &window_info](const apps::AppUpdate& update) {
           if (MaybeShowLaunchPreventionDialog(update)) {
             return;
           }
 
+          apps::LaunchSource launch_source =
+              ConvertMojomLaunchSourceToLaunchSource(mojom_launch_source);
           RecordAppLaunch(update.AppId(), launch_source);
           RecordAppPlatformMetrics(profile_, update, launch_source,
                                    apps::LaunchContainer::kLaunchContainerNone);
 
           app_service_->Launch(ConvertAppTypeToMojomAppType(update.AppType()),
-                               update.AppId(), event_flags, launch_source,
+                               update.AppId(), event_flags, mojom_launch_source,
                                std::move(window_info));
 
           PerformPostLaunchTasks(launch_source);
@@ -304,16 +306,18 @@
 void AppServiceProxyBase::LaunchAppWithFiles(
     const std::string& app_id,
     int32_t event_flags,
-    apps::mojom::LaunchSource launch_source,
+    apps::mojom::LaunchSource mojom_launch_source,
     apps::mojom::FilePathsPtr file_paths) {
   if (app_service_.is_connected()) {
     app_registry_cache_.ForOneApp(
-        app_id, [this, event_flags, launch_source,
+        app_id, [this, event_flags, mojom_launch_source,
                  &file_paths](const apps::AppUpdate& update) {
           if (MaybeShowLaunchPreventionDialog(update)) {
             return;
           }
 
+          apps::LaunchSource launch_source =
+              ConvertMojomLaunchSourceToLaunchSource(mojom_launch_source);
           RecordAppPlatformMetrics(profile_, update, launch_source,
                                    apps::LaunchContainer::kLaunchContainerNone);
 
@@ -321,13 +325,13 @@
           // launched. So we only record launches from other places. We should
           // eventually move those metrics here, after AppService supports all
           // app types launched by file manager.
-          if (launch_source != apps::mojom::LaunchSource::kFromFileManager) {
+          if (launch_source != apps::LaunchSource::kFromFileManager) {
             RecordAppLaunch(update.AppId(), launch_source);
           }
 
           app_service_->LaunchAppWithFiles(
               ConvertAppTypeToMojomAppType(update.AppType()), update.AppId(),
-              event_flags, launch_source, std::move(file_paths));
+              event_flags, mojom_launch_source, std::move(file_paths));
 
           PerformPostLaunchTasks(launch_source);
         });
@@ -338,13 +342,13 @@
     const std::string& app_id,
     int32_t event_flags,
     apps::mojom::IntentPtr intent,
-    apps::mojom::LaunchSource launch_source,
+    apps::mojom::LaunchSource mojom_launch_source,
     apps::mojom::WindowInfoPtr window_info,
     apps::mojom::Publisher::LaunchAppWithIntentCallback callback) {
   CHECK(intent);
   if (app_service_.is_connected()) {
     app_registry_cache_.ForOneApp(
-        app_id, [this, event_flags, &intent, launch_source, &window_info,
+        app_id, [this, event_flags, &intent, mojom_launch_source, &window_info,
                  callback = std::move(callback)](
                     const apps::AppUpdate& update) mutable {
           if (MaybeShowLaunchPreventionDialog(update)) {
@@ -353,11 +357,13 @@
             return;
           }
 
+          apps::LaunchSource launch_source =
+              ConvertMojomLaunchSourceToLaunchSource(mojom_launch_source);
           // TODO(crbug/1117655): File manager records metrics for apps it
           // launched. So we only record launches from other places. We should
           // eventually move those metrics here, after AppService supports all
           // app types launched by file manager.
-          if (launch_source != apps::mojom::LaunchSource::kFromFileManager) {
+          if (launch_source != apps::LaunchSource::kFromFileManager) {
             RecordAppLaunch(update.AppId(), launch_source);
           }
           RecordAppPlatformMetrics(profile_, update, launch_source,
@@ -365,7 +371,7 @@
 
           app_service_->LaunchAppWithIntent(
               ConvertAppTypeToMojomAppType(update.AppType()), update.AppId(),
-              event_flags, std::move(intent), launch_source,
+              event_flags, std::move(intent), mojom_launch_source,
               std::move(window_info), std::move(callback));
 
           PerformPostLaunchTasks(launch_source);
@@ -406,7 +412,7 @@
         // launched. So we only record launches from other places. We should
         // eventually move those metrics here, after AppService supports all
         // app types launched by file manager.
-        if (launch_source != apps::mojom::LaunchSource::kFromFileManager) {
+        if (launch_source != apps::LaunchSource::kFromFileManager) {
           RecordAppLaunch(update.AppId(), launch_source);
         }
 
@@ -865,12 +871,12 @@
 }
 
 void AppServiceProxyBase::PerformPostLaunchTasks(
-    apps::mojom::LaunchSource launch_source) {}
+    apps::LaunchSource launch_source) {}
 
 void AppServiceProxyBase::RecordAppPlatformMetrics(
     Profile* profile,
     const apps::AppUpdate& update,
-    apps::mojom::LaunchSource launch_source,
+    apps::LaunchSource launch_source,
     apps::LaunchContainer container) {}
 
 void AppServiceProxyBase::PerformPostUninstallTasks(
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.h b/chrome/browser/apps/app_service/app_service_proxy_base.h
index 2fdf709c..002be6ff7 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_base.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_base.h
@@ -389,11 +389,11 @@
   apps::mojom::IntentFilterPtr FindBestMatchingMojomFilter(
       const apps::mojom::IntentPtr& intent);
 
-  virtual void PerformPostLaunchTasks(apps::mojom::LaunchSource launch_source);
+  virtual void PerformPostLaunchTasks(apps::LaunchSource launch_source);
 
   virtual void RecordAppPlatformMetrics(Profile* profile,
                                         const apps::AppUpdate& update,
-                                        apps::mojom::LaunchSource launch_source,
+                                        apps::LaunchSource launch_source,
                                         apps::LaunchContainer container);
 
   virtual void PerformPostUninstallTasks(
diff --git a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
index fb40052..8765d9b 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "chromeos/lacros/lacros_service.h"
 #include "components/services/app_service/app_service_mojom_impl.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
 #include "components/services/app_service/public/cpp/intent_filter_util.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
@@ -132,9 +133,10 @@
   }
 
   remote_crosapi_app_service_proxy_->Launch(
-      CreateCrosapiLaunchParamsWithEventFlags(this, app_id, event_flags,
-                                              launch_source,
-                                              display::kInvalidDisplayId));
+      CreateCrosapiLaunchParamsWithEventFlags(
+          this, app_id, event_flags,
+          ConvertMojomLaunchSourceToLaunchSource(launch_source),
+          display::kInvalidDisplayId));
 }
 
 void AppServiceProxyLacros::LaunchAppWithFiles(
@@ -155,7 +157,9 @@
     return;
   }
   auto params = CreateCrosapiLaunchParamsWithEventFlags(
-      this, app_id, event_flags, launch_source, display::kInvalidDisplayId);
+      this, app_id, event_flags,
+      ConvertMojomLaunchSourceToLaunchSource(launch_source),
+      display::kInvalidDisplayId);
   params->intent = apps_util::CreateCrosapiIntentForViewFiles(file_paths);
   remote_crosapi_app_service_proxy_->Launch(std::move(params));
 }
@@ -182,7 +186,8 @@
   }
 
   auto params = CreateCrosapiLaunchParamsWithEventFlags(
-      this, app_id, event_flags, launch_source,
+      this, app_id, event_flags,
+      ConvertMojomLaunchSourceToLaunchSource(launch_source),
       window_info ? window_info->display_id : display::kInvalidDisplayId);
   params->intent =
       apps_util::ConvertAppServiceToCrosapiIntent(intent, profile_);
diff --git a/chrome/browser/apps/app_service/browser_app_launcher.cc b/chrome/browser/apps/app_service/browser_app_launcher.cc
index 26a8489e..f4395fa 100644
--- a/chrome/browser/apps/app_service/browser_app_launcher.cc
+++ b/chrome/browser/apps/app_service/browser_app_launcher.cc
@@ -47,7 +47,7 @@
         params.launch_source, params.display_id, params.launch_files,
         params.intent);
     std::string app_id = params.app_id;
-    apps::mojom::LaunchSource launch_source = params.launch_source;
+    apps::LaunchSource launch_source = params.launch_source;
     apps::LaunchContainer container = params.container;
     int restore_id = params.restore_id;
 
@@ -65,8 +65,7 @@
     }
 
     RecordAppLaunchMetrics(profile, apps::AppType::kWeb, app_id,
-                           apps::mojom::LaunchSource::kFromFullRestore,
-                           container);
+                           apps::LaunchSource::kFromFullRestore, container);
 
     int session_id = apps::GetSessionIdForRestoreFromWebContents(web_contents);
     if (!SessionID::IsValidValue(session_id)) {
@@ -93,7 +92,7 @@
   // restore file.
   if (SessionID::IsValidValue(params.restore_id)) {
     RecordAppLaunchMetrics(profile, apps::AppType::kChromeApp, params.app_id,
-                           apps::mojom::LaunchSource::kFromFullRestore,
+                           apps::LaunchSource::kFromFullRestore,
                            params.container);
 
     apps::AppLaunchParams params_for_restore(
@@ -148,7 +147,7 @@
   LaunchAppWithParamsImpl(
       CreateAppLaunchParamsUserContainer(
           profile_, extension, WindowOpenDisposition::NEW_WINDOW,
-          apps::mojom::LaunchSource::kFromChromeInternal),
+          apps::LaunchSource::kFromChromeInternal),
       profile_, &web_app_launch_manager_);
 }
 #endif
diff --git a/chrome/browser/apps/app_service/launch_utils.cc b/chrome/browser/apps/app_service/launch_utils.cc
index 752635bb..8cbba24 100644
--- a/chrome/browser/apps/app_service/launch_utils.cc
+++ b/chrome/browser/apps/app_service/launch_utils.cc
@@ -188,7 +188,7 @@
 AppLaunchParams CreateAppIdLaunchParamsWithEventFlags(
     const std::string& app_id,
     int event_flags,
-    apps::mojom::LaunchSource launch_source,
+    apps::LaunchSource launch_source,
     int64_t display_id,
     apps::LaunchContainer fallback_container) {
   WindowOpenDisposition raw_disposition =
@@ -216,7 +216,7 @@
 apps::AppLaunchParams CreateAppLaunchParamsForIntent(
     const std::string& app_id,
     int32_t event_flags,
-    apps::mojom::LaunchSource launch_source,
+    apps::LaunchSource launch_source,
     int64_t display_id,
     apps::LaunchContainer fallback_container,
     apps::mojom::IntentPtr&& intent,
@@ -256,56 +256,56 @@
 }
 
 extensions::AppLaunchSource GetAppLaunchSource(
-    apps::mojom::LaunchSource launch_source) {
+    apps::LaunchSource launch_source) {
   switch (launch_source) {
-    case apps::mojom::LaunchSource::kUnknown:
-    case apps::mojom::LaunchSource::kFromAppListGrid:
-    case apps::mojom::LaunchSource::kFromAppListGridContextMenu:
-    case apps::mojom::LaunchSource::kFromAppListQuery:
-    case apps::mojom::LaunchSource::kFromAppListQueryContextMenu:
-    case apps::mojom::LaunchSource::kFromAppListRecommendation:
-    case apps::mojom::LaunchSource::kFromParentalControls:
-    case apps::mojom::LaunchSource::kFromShelf:
-    case apps::mojom::LaunchSource::kFromLink:
-    case apps::mojom::LaunchSource::kFromOmnibox:
-    case apps::mojom::LaunchSource::kFromOtherApp:
-    case apps::mojom::LaunchSource::kFromSharesheet:
+    case apps::LaunchSource::kUnknown:
+    case apps::LaunchSource::kFromAppListGrid:
+    case apps::LaunchSource::kFromAppListGridContextMenu:
+    case apps::LaunchSource::kFromAppListQuery:
+    case apps::LaunchSource::kFromAppListQueryContextMenu:
+    case apps::LaunchSource::kFromAppListRecommendation:
+    case apps::LaunchSource::kFromParentalControls:
+    case apps::LaunchSource::kFromShelf:
+    case apps::LaunchSource::kFromLink:
+    case apps::LaunchSource::kFromOmnibox:
+    case apps::LaunchSource::kFromOtherApp:
+    case apps::LaunchSource::kFromSharesheet:
       return extensions::AppLaunchSource::kSourceAppLauncher;
-    case apps::mojom::LaunchSource::kFromMenu:
+    case apps::LaunchSource::kFromMenu:
       return extensions::AppLaunchSource::kSourceContextMenu;
-    case apps::mojom::LaunchSource::kFromKeyboard:
+    case apps::LaunchSource::kFromKeyboard:
       return extensions::AppLaunchSource::kSourceKeyboard;
-    case apps::mojom::LaunchSource::kFromFileManager:
+    case apps::LaunchSource::kFromFileManager:
       return extensions::AppLaunchSource::kSourceFileHandler;
-    case apps::mojom::LaunchSource::kFromChromeInternal:
-    case apps::mojom::LaunchSource::kFromReleaseNotesNotification:
-    case apps::mojom::LaunchSource::kFromFullRestore:
-    case apps::mojom::LaunchSource::kFromSmartTextContextMenu:
-    case apps::mojom::LaunchSource::kFromDiscoverTabNotification:
+    case apps::LaunchSource::kFromChromeInternal:
+    case apps::LaunchSource::kFromReleaseNotesNotification:
+    case apps::LaunchSource::kFromFullRestore:
+    case apps::LaunchSource::kFromSmartTextContextMenu:
+    case apps::LaunchSource::kFromDiscoverTabNotification:
       return extensions::AppLaunchSource::kSourceChromeInternal;
-    case apps::mojom::LaunchSource::kFromInstalledNotification:
+    case apps::LaunchSource::kFromInstalledNotification:
       return extensions::AppLaunchSource::kSourceInstalledNotification;
-    case apps::mojom::LaunchSource::kFromTest:
+    case apps::LaunchSource::kFromTest:
       return extensions::AppLaunchSource::kSourceTest;
-    case apps::mojom::LaunchSource::kFromArc:
+    case apps::LaunchSource::kFromArc:
       return extensions::AppLaunchSource::kSourceArc;
-    case apps::mojom::LaunchSource::kFromManagementApi:
+    case apps::LaunchSource::kFromManagementApi:
       return extensions::AppLaunchSource::kSourceManagementApi;
-    case apps::mojom::LaunchSource::kFromKiosk:
+    case apps::LaunchSource::kFromKiosk:
       return extensions::AppLaunchSource::kSourceKiosk;
-    case apps::mojom::LaunchSource::kFromCommandLine:
+    case apps::LaunchSource::kFromCommandLine:
       return extensions::AppLaunchSource::kSourceCommandLine;
-    case apps::mojom::LaunchSource::kFromBackgroundMode:
+    case apps::LaunchSource::kFromBackgroundMode:
       return extensions::AppLaunchSource::kSourceBackground;
-    case apps::mojom::LaunchSource::kFromNewTabPage:
+    case apps::LaunchSource::kFromNewTabPage:
       return extensions::AppLaunchSource::kSourceNewTabPage;
-    case apps::mojom::LaunchSource::kFromIntentUrl:
+    case apps::LaunchSource::kFromIntentUrl:
       return extensions::AppLaunchSource::kSourceIntentUrl;
-    case apps::mojom::LaunchSource::kFromOsLogin:
+    case apps::LaunchSource::kFromOsLogin:
       return extensions::AppLaunchSource::kSourceRunOnOsLogin;
-    case apps::mojom::LaunchSource::kFromProtocolHandler:
+    case apps::LaunchSource::kFromProtocolHandler:
       return extensions::AppLaunchSource::kSourceProtocolHandler;
-    case apps::mojom::LaunchSource::kFromUrlHandler:
+    case apps::LaunchSource::kFromUrlHandler:
       return extensions::AppLaunchSource::kSourceUrlHandler;
   }
 }
@@ -405,8 +405,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
   crosapi_params->app_id = id;
-  crosapi_params->launch_source =
-      ConvertMojomLaunchSourceToLaunchSource(params.launch_source);
+  crosapi_params->launch_source = params.launch_source;
 
   // Both launch_files and override_url will be represent by intent in crosapi
   // launch params. These info will normally represent in the intent field in
@@ -439,8 +438,7 @@
       crosapi_params->app_id,
       ConvertCrosapiToAppServiceLaunchContainer(crosapi_params->container),
       ConvertWindowOpenDispositionFromCrosapi(crosapi_params->disposition),
-      ConvertLaunchSourceToMojomLaunchSource(crosapi_params->launch_source),
-      crosapi_params->display_id);
+      crosapi_params->launch_source, crosapi_params->display_id);
   if (!crosapi_params->intent) {
     return params;
   }
@@ -464,7 +462,7 @@
     apps::AppServiceProxy* proxy,
     const std::string& app_id,
     int event_flags,
-    apps::mojom::LaunchSource launch_source,
+    apps::LaunchSource launch_source,
     int64_t display_id) {
   WindowMode window_mode = WindowMode::kUnknown;
   proxy->AppRegistryCache().ForOneApp(
diff --git a/chrome/browser/apps/app_service/launch_utils.h b/chrome/browser/apps/app_service/launch_utils.h
index 9e59573..1ab3c3b 100644
--- a/chrome/browser/apps/app_service/launch_utils.h
+++ b/chrome/browser/apps/app_service/launch_utils.h
@@ -55,21 +55,21 @@
 AppLaunchParams CreateAppIdLaunchParamsWithEventFlags(
     const std::string& app_id,
     int event_flags,
-    apps::mojom::LaunchSource source,
+    apps::LaunchSource source,
     int64_t display_id,
     apps::LaunchContainer fallback_container);
 
 apps::AppLaunchParams CreateAppLaunchParamsForIntent(
     const std::string& app_id,
     int32_t event_flags,
-    apps::mojom::LaunchSource source,
+    apps::LaunchSource source,
     int64_t display_id,
     apps::LaunchContainer fallback_container,
     apps::mojom::IntentPtr&& intent,
     Profile* profile);
 
 extensions::AppLaunchSource GetAppLaunchSource(
-    apps::mojom::LaunchSource launch_source);
+    apps::LaunchSource launch_source);
 
 // Returns event flag for |disposition|. If |prefer_container|
 // is true, |disposition| will be ignored. Otherwise, an event flag based on
@@ -115,7 +115,7 @@
     apps::AppServiceProxy* proxy,
     const std::string& app_id,
     int event_flags,
-    apps::mojom::LaunchSource launch_source,
+    apps::LaunchSource launch_source,
     int64_t display_id);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
diff --git a/chrome/browser/apps/app_service/launch_utils_unittest.cc b/chrome/browser/apps/app_service/launch_utils_unittest.cc
index 1ee713872..3fc368fd 100644
--- a/chrome/browser/apps/app_service/launch_utils_unittest.cc
+++ b/chrome/browser/apps/app_service/launch_utils_unittest.cc
@@ -30,7 +30,7 @@
           apps::LaunchContainer::kLaunchContainerNone) {
     return apps::CreateAppIdLaunchParamsWithEventFlags(
         app_id, apps::GetEventFlags(disposition, preferred_container),
-        apps::mojom::LaunchSource::kFromChromeInternal, display_id,
+        apps::LaunchSource::kFromChromeInternal, display_id,
         fallback_container);
   }
 
@@ -98,9 +98,9 @@
 
   auto params = apps::CreateAppLaunchParamsForIntent(
       app_id, apps::GetEventFlags(disposition, true),
-      apps::mojom::LaunchSource::kFromChromeInternal,
-      display::kInvalidDisplayId, apps::LaunchContainer::kLaunchContainerWindow,
-      std::move(intent), &profile_);
+      apps::LaunchSource::kFromChromeInternal, display::kInvalidDisplayId,
+      apps::LaunchContainer::kLaunchContainerWindow, std::move(intent),
+      &profile_);
 
   EXPECT_EQ(url, params.override_url);
 }
@@ -119,9 +119,9 @@
 
   auto params = apps::CreateAppLaunchParamsForIntent(
       app_id, apps::GetEventFlags(disposition, true),
-      apps::mojom::LaunchSource::kFromChromeInternal,
-      display::kInvalidDisplayId, apps::LaunchContainer::kLaunchContainerWindow,
-      std::move(intent), &profile_);
+      apps::LaunchSource::kFromChromeInternal, display::kInvalidDisplayId,
+      apps::LaunchContainer::kLaunchContainerWindow, std::move(intent),
+      &profile_);
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   ASSERT_EQ(params.launch_files.size(), 1U);
@@ -289,8 +289,7 @@
   EXPECT_EQ(apps::LaunchContainer::kLaunchContainerNone,
             converted_params.container);
   EXPECT_EQ(WindowOpenDisposition::UNKNOWN, converted_params.disposition);
-  EXPECT_EQ(apps::mojom::LaunchSource::kFromIntentUrl,
-            converted_params.launch_source);
+  EXPECT_EQ(apps::LaunchSource::kFromIntentUrl, converted_params.launch_source);
 }
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -331,7 +330,7 @@
   EXPECT_EQ(converted_params.disposition,
             WindowOpenDisposition::NEW_FOREGROUND_TAB);
   EXPECT_EQ(converted_params.launch_source,
-            apps::mojom::LaunchSource::kFromSharesheet);
+            apps::LaunchSource::kFromSharesheet);
   EXPECT_EQ(converted_params.display_id, kDisplayId);
 
   EXPECT_EQ(converted_params.launch_files.size(), 1U);
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
index 49c1f3bb..190fce1b 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
@@ -220,7 +220,7 @@
 
 // Records the number of times Chrome OS apps are launched grouped by the launch
 // source.
-void RecordAppLaunchSource(apps::mojom::LaunchSource launch_source) {
+void RecordAppLaunchSource(apps::LaunchSource launch_source) {
   base::UmaHistogramEnumeration("Apps.AppLaunchSource", launch_source);
 }
 
@@ -325,7 +325,7 @@
 void RecordAppLaunchMetrics(Profile* profile,
                             AppType app_type,
                             const std::string& app_id,
-                            apps::mojom::LaunchSource launch_source,
+                            apps::LaunchSource launch_source,
                             apps::LaunchContainer container) {
   if (app_type == AppType::kUnknown) {
     return;
@@ -632,11 +632,10 @@
   RecordAppsUsageTimeUkm();
 }
 
-void AppPlatformMetrics::RecordAppLaunchUkm(
-    AppType app_type,
-    const std::string& app_id,
-    apps::mojom::LaunchSource launch_source,
-    apps::LaunchContainer container) {
+void AppPlatformMetrics::RecordAppLaunchUkm(AppType app_type,
+                                            const std::string& app_id,
+                                            apps::LaunchSource launch_source,
+                                            apps::LaunchContainer container) {
   if (app_type == AppType::kUnknown || !ShouldRecordUkm(profile_)) {
     return;
   }
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.h b/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
index 8723cbc..c4e1944 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
@@ -60,7 +60,7 @@
 void RecordAppLaunchMetrics(Profile* profile,
                             AppType app_type,
                             const std::string& app_id,
-                            apps::mojom::LaunchSource launch_source,
+                            apps::LaunchSource launch_source,
                             apps::LaunchContainer container);
 
 class AppPlatformMetrics : public apps::AppRegistryCache::Observer,
@@ -127,7 +127,7 @@
   // Records UKM when launching an app.
   void RecordAppLaunchUkm(AppType app_type,
                           const std::string& app_id,
-                          apps::mojom::LaunchSource launch_source,
+                          apps::LaunchSource launch_source,
                           apps::LaunchContainer container);
 
   // Records UKM when uninstalling an app.
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
index e24adbbe..85716ab 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
@@ -2152,7 +2152,7 @@
   proxy->BrowserAppLauncher()->LaunchAppWithParamsForTesting(
       apps::AppLaunchParams("w2", apps::LaunchContainer::kLaunchContainerTab,
                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                            apps::mojom::LaunchSource::kFromTest));
+                            apps::LaunchSource::kFromTest));
   if (IsLacrosPrimary()) {
     VerifyAppsLaunchUkm("https://foo2.com", AppTypeName::kStandaloneBrowser,
                         apps::mojom::LaunchSource::kFromTest);
@@ -2171,7 +2171,7 @@
   proxy->BrowserAppLauncher()->LaunchAppWithParamsForTesting(
       apps::AppLaunchParams("s", apps::LaunchContainer::kLaunchContainerTab,
                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                            apps::mojom::LaunchSource::kFromTest));
+                            apps::LaunchSource::kFromTest));
   VerifyAppsLaunchUkm("app://s", AppTypeName::kSystemWeb,
                       apps::mojom::LaunchSource::kFromTest);
   VerifyAppLaunchPerAppTypeHistogram(1, AppTypeName::kSystemWeb);
diff --git a/chrome/browser/apps/app_service/metrics/app_service_metrics.cc b/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
index c7a60d6..f7fe5fd 100644
--- a/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
@@ -12,7 +12,6 @@
 #include "chrome/common/extensions/extension_constants.h"
 #include "components/app_constants/constants.h"
 #include "components/services/app_service/public/cpp/app_update.h"
-#include "components/services/app_service/public/mojom/app_service.mojom.h"
 #include "extensions/common/constants.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -26,125 +25,125 @@
 namespace {
 
 void RecordDefaultAppLaunch(apps::DefaultAppName default_app_name,
-                            apps::mojom::LaunchSource launch_source) {
+                            apps::LaunchSource launch_source) {
   switch (launch_source) {
-    case apps::mojom::LaunchSource::kUnknown:
-    case apps::mojom::LaunchSource::kFromParentalControls:
-    case apps::mojom::LaunchSource::kFromTest:
+    case apps::LaunchSource::kUnknown:
+    case apps::LaunchSource::kFromParentalControls:
+    case apps::LaunchSource::kFromTest:
       return;
-    case apps::mojom::LaunchSource::kFromAppListGrid:
+    case apps::LaunchSource::kFromAppListGrid:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromAppListGrid",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromAppListGridContextMenu:
+    case apps::LaunchSource::kFromAppListGridContextMenu:
       base::UmaHistogramEnumeration(
           "Apps.DefaultAppLaunch.FromAppListGridContextMenu", default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromAppListQuery:
+    case apps::LaunchSource::kFromAppListQuery:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromAppListQuery",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromAppListQueryContextMenu:
+    case apps::LaunchSource::kFromAppListQueryContextMenu:
       base::UmaHistogramEnumeration(
           "Apps.DefaultAppLaunch.FromAppListQueryContextMenu",
           default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromAppListRecommendation:
+    case apps::LaunchSource::kFromAppListRecommendation:
       base::UmaHistogramEnumeration(
           "Apps.DefaultAppLaunch.FromAppListRecommendation", default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromShelf:
+    case apps::LaunchSource::kFromShelf:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromShelf",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromFileManager:
+    case apps::LaunchSource::kFromFileManager:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromFileManager",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromLink:
+    case apps::LaunchSource::kFromLink:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromLink",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromOmnibox:
+    case apps::LaunchSource::kFromOmnibox:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromOmnibox",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromChromeInternal:
+    case apps::LaunchSource::kFromChromeInternal:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromChromeInternal",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromKeyboard:
+    case apps::LaunchSource::kFromKeyboard:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromKeyboard",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromOtherApp:
+    case apps::LaunchSource::kFromOtherApp:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromOtherApp",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromMenu:
+    case apps::LaunchSource::kFromMenu:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromMenu",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromInstalledNotification:
+    case apps::LaunchSource::kFromInstalledNotification:
       base::UmaHistogramEnumeration(
           "Apps.DefaultAppLaunch.FromInstalledNotification", default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromArc:
+    case apps::LaunchSource::kFromArc:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromArc",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromSharesheet:
+    case apps::LaunchSource::kFromSharesheet:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromSharesheet",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromReleaseNotesNotification:
+    case apps::LaunchSource::kFromReleaseNotesNotification:
       base::UmaHistogramEnumeration(
           "Apps.DefaultAppLaunch.FromReleaseNotesNotification",
           default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromFullRestore:
+    case apps::LaunchSource::kFromFullRestore:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromFullRestore",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromSmartTextContextMenu:
+    case apps::LaunchSource::kFromSmartTextContextMenu:
       base::UmaHistogramEnumeration(
           "Apps.DefaultAppLaunch.FromSmartTextContextMenu", default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromDiscoverTabNotification:
+    case apps::LaunchSource::kFromDiscoverTabNotification:
       base::UmaHistogramEnumeration(
           "Apps.DefaultAppLaunch.FromDiscoverTabNotification",
           default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromManagementApi:
+    case apps::LaunchSource::kFromManagementApi:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromManagementApi",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromKiosk:
+    case apps::LaunchSource::kFromKiosk:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromKiosk",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromNewTabPage:
+    case apps::LaunchSource::kFromNewTabPage:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromNewTabPage",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromIntentUrl:
+    case apps::LaunchSource::kFromIntentUrl:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromIntentUrl",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromOsLogin:
+    case apps::LaunchSource::kFromOsLogin:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromOsLogin",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromProtocolHandler:
+    case apps::LaunchSource::kFromProtocolHandler:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromProtocolHandler",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromUrlHandler:
+    case apps::LaunchSource::kFromUrlHandler:
       base::UmaHistogramEnumeration("Apps.DefaultAppLaunch.FromUrlHandler",
                                     default_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromCommandLine:
-    case apps::mojom::LaunchSource::kFromBackgroundMode:
+    case apps::LaunchSource::kFromCommandLine:
+    case apps::LaunchSource::kFromBackgroundMode:
       NOTREACHED();
       break;
   }
@@ -152,47 +151,47 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 void RecordBuiltInAppLaunch(apps::BuiltInAppName built_in_app_name,
-                            apps::mojom::LaunchSource launch_source) {
+                            apps::LaunchSource launch_source) {
   switch (launch_source) {
-    case apps::mojom::LaunchSource::kUnknown:
-    case apps::mojom::LaunchSource::kFromParentalControls:
+    case apps::LaunchSource::kUnknown:
+    case apps::LaunchSource::kFromParentalControls:
       break;
-    case apps::mojom::LaunchSource::kFromAppListGrid:
-    case apps::mojom::LaunchSource::kFromAppListGridContextMenu:
+    case apps::LaunchSource::kFromAppListGrid:
+    case apps::LaunchSource::kFromAppListGridContextMenu:
       base::UmaHistogramEnumeration("Apps.AppListInternalApp.Activate",
                                     built_in_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromAppListQuery:
-    case apps::mojom::LaunchSource::kFromAppListQueryContextMenu:
-    case apps::mojom::LaunchSource::kFromAppListRecommendation:
+    case apps::LaunchSource::kFromAppListQuery:
+    case apps::LaunchSource::kFromAppListQueryContextMenu:
+    case apps::LaunchSource::kFromAppListRecommendation:
       base::UmaHistogramEnumeration("Apps.AppListSearchResultInternalApp.Open",
                                     built_in_app_name);
       break;
-    case apps::mojom::LaunchSource::kFromShelf:
-    case apps::mojom::LaunchSource::kFromFileManager:
-    case apps::mojom::LaunchSource::kFromLink:
-    case apps::mojom::LaunchSource::kFromOmnibox:
-    case apps::mojom::LaunchSource::kFromChromeInternal:
-    case apps::mojom::LaunchSource::kFromKeyboard:
-    case apps::mojom::LaunchSource::kFromOtherApp:
-    case apps::mojom::LaunchSource::kFromMenu:
-    case apps::mojom::LaunchSource::kFromInstalledNotification:
-    case apps::mojom::LaunchSource::kFromTest:
-    case apps::mojom::LaunchSource::kFromArc:
-    case apps::mojom::LaunchSource::kFromSharesheet:
-    case apps::mojom::LaunchSource::kFromReleaseNotesNotification:
-    case apps::mojom::LaunchSource::kFromFullRestore:
-    case apps::mojom::LaunchSource::kFromSmartTextContextMenu:
-    case apps::mojom::LaunchSource::kFromDiscoverTabNotification:
-    case apps::mojom::LaunchSource::kFromManagementApi:
-    case apps::mojom::LaunchSource::kFromKiosk:
-    case apps::mojom::LaunchSource::kFromCommandLine:
-    case apps::mojom::LaunchSource::kFromBackgroundMode:
-    case apps::mojom::LaunchSource::kFromNewTabPage:
-    case apps::mojom::LaunchSource::kFromIntentUrl:
-    case apps::mojom::LaunchSource::kFromOsLogin:
-    case apps::mojom::LaunchSource::kFromProtocolHandler:
-    case apps::mojom::LaunchSource::kFromUrlHandler:
+    case apps::LaunchSource::kFromShelf:
+    case apps::LaunchSource::kFromFileManager:
+    case apps::LaunchSource::kFromLink:
+    case apps::LaunchSource::kFromOmnibox:
+    case apps::LaunchSource::kFromChromeInternal:
+    case apps::LaunchSource::kFromKeyboard:
+    case apps::LaunchSource::kFromOtherApp:
+    case apps::LaunchSource::kFromMenu:
+    case apps::LaunchSource::kFromInstalledNotification:
+    case apps::LaunchSource::kFromTest:
+    case apps::LaunchSource::kFromArc:
+    case apps::LaunchSource::kFromSharesheet:
+    case apps::LaunchSource::kFromReleaseNotesNotification:
+    case apps::LaunchSource::kFromFullRestore:
+    case apps::LaunchSource::kFromSmartTextContextMenu:
+    case apps::LaunchSource::kFromDiscoverTabNotification:
+    case apps::LaunchSource::kFromManagementApi:
+    case apps::LaunchSource::kFromKiosk:
+    case apps::LaunchSource::kFromCommandLine:
+    case apps::LaunchSource::kFromBackgroundMode:
+    case apps::LaunchSource::kFromNewTabPage:
+    case apps::LaunchSource::kFromIntentUrl:
+    case apps::LaunchSource::kFromOsLogin:
+    case apps::LaunchSource::kFromProtocolHandler:
+    case apps::LaunchSource::kFromUrlHandler:
       break;
   }
 }
@@ -203,7 +202,7 @@
 namespace apps {
 
 void RecordAppLaunch(const std::string& app_id,
-                     apps::mojom::LaunchSource launch_source) {
+                     apps::LaunchSource launch_source) {
   if (app_id == web_app::kCursiveAppId) {
     RecordDefaultAppLaunch(DefaultAppName::kCursive, launch_source);
   } else if (app_id == extension_misc::kCalculatorAppId) {
diff --git a/chrome/browser/apps/app_service/metrics/app_service_metrics.h b/chrome/browser/apps/app_service/metrics/app_service_metrics.h
index f385e7bc..8f87c42 100644
--- a/chrome/browser/apps/app_service/metrics/app_service_metrics.h
+++ b/chrome/browser/apps/app_service/metrics/app_service_metrics.h
@@ -9,7 +9,7 @@
 #include <string>
 
 #include "build/chromeos_buildflags.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 
 namespace apps {
 
@@ -87,7 +87,7 @@
 };
 
 void RecordAppLaunch(const std::string& app_id,
-                     apps::mojom::LaunchSource launch_source);
+                     apps::LaunchSource launch_source);
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 void RecordBuiltInAppSearchResult(const std::string& app_id);
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.cc b/chrome/browser/apps/app_service/publishers/arc_apps.cc
index 14628f8..32af418 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.cc
@@ -774,9 +774,11 @@
   if (params.intent) {
     LaunchAppWithIntent(
         params.app_id, event_flags, ConvertIntentToMojomIntent(params.intent),
-        params.launch_source, std::move(window_info), base::DoNothing());
+        ConvertLaunchSourceToMojomLaunchSource(params.launch_source),
+        std::move(window_info), base::DoNothing());
   } else {
-    Launch(params.app_id, event_flags, params.launch_source,
+    Launch(params.app_id, event_flags,
+           ConvertLaunchSourceToMojomLaunchSource(params.launch_source),
            std::move(window_info));
   }
   // TODO(crbug.com/1244506): Add launch return value.
diff --git a/chrome/browser/apps/app_service/publishers/crostini_apps.cc b/chrome/browser/apps/app_service/publishers/crostini_apps.cc
index a9a65363..fe470dd2 100644
--- a/chrome/browser/apps/app_service/publishers/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/crostini_apps.cc
@@ -126,9 +126,11 @@
   if (params.intent) {
     LaunchAppWithIntent(
         params.app_id, event_flags, ConvertIntentToMojomIntent(params.intent),
-        params.launch_source, std::move(window_info), base::DoNothing());
+        ConvertLaunchSourceToMojomLaunchSource(params.launch_source),
+        std::move(window_info), base::DoNothing());
   } else {
-    Launch(params.app_id, event_flags, params.launch_source,
+    Launch(params.app_id, event_flags,
+           ConvertLaunchSourceToMojomLaunchSource(params.launch_source),
            std::move(window_info));
   }
   // TODO(crbug.com/1244506): Add launch return value.
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_base.cc b/chrome/browser/apps/app_service/publishers/extension_apps_base.cc
index 21b17375..9eb09ca 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_base.cc
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_base.cc
@@ -81,45 +81,44 @@
   }
 }
 
-ash::ShelfLaunchSource ConvertLaunchSource(
-    apps::mojom::LaunchSource launch_source) {
+ash::ShelfLaunchSource ConvertLaunchSource(apps::LaunchSource launch_source) {
   switch (launch_source) {
-    case apps::mojom::LaunchSource::kUnknown:
-    case apps::mojom::LaunchSource::kFromParentalControls:
+    case apps::LaunchSource::kUnknown:
+    case apps::LaunchSource::kFromParentalControls:
       return ash::LAUNCH_FROM_UNKNOWN;
-    case apps::mojom::LaunchSource::kFromAppListGrid:
-    case apps::mojom::LaunchSource::kFromAppListGridContextMenu:
+    case apps::LaunchSource::kFromAppListGrid:
+    case apps::LaunchSource::kFromAppListGridContextMenu:
       return ash::LAUNCH_FROM_APP_LIST;
-    case apps::mojom::LaunchSource::kFromAppListQuery:
-    case apps::mojom::LaunchSource::kFromAppListQueryContextMenu:
-    case apps::mojom::LaunchSource::kFromAppListRecommendation:
+    case apps::LaunchSource::kFromAppListQuery:
+    case apps::LaunchSource::kFromAppListQueryContextMenu:
+    case apps::LaunchSource::kFromAppListRecommendation:
       return ash::LAUNCH_FROM_APP_LIST_SEARCH;
-    case apps::mojom::LaunchSource::kFromShelf:
+    case apps::LaunchSource::kFromShelf:
       return ash::LAUNCH_FROM_SHELF;
-    case apps::mojom::LaunchSource::kFromFileManager:
-    case apps::mojom::LaunchSource::kFromLink:
-    case apps::mojom::LaunchSource::kFromOmnibox:
-    case apps::mojom::LaunchSource::kFromChromeInternal:
-    case apps::mojom::LaunchSource::kFromKeyboard:
-    case apps::mojom::LaunchSource::kFromOtherApp:
-    case apps::mojom::LaunchSource::kFromMenu:
-    case apps::mojom::LaunchSource::kFromInstalledNotification:
-    case apps::mojom::LaunchSource::kFromTest:
-    case apps::mojom::LaunchSource::kFromArc:
-    case apps::mojom::LaunchSource::kFromSharesheet:
-    case apps::mojom::LaunchSource::kFromReleaseNotesNotification:
-    case apps::mojom::LaunchSource::kFromFullRestore:
-    case apps::mojom::LaunchSource::kFromSmartTextContextMenu:
-    case apps::mojom::LaunchSource::kFromDiscoverTabNotification:
-    case apps::mojom::LaunchSource::kFromManagementApi:
-    case apps::mojom::LaunchSource::kFromKiosk:
-    case apps::mojom::LaunchSource::kFromCommandLine:
-    case apps::mojom::LaunchSource::kFromBackgroundMode:
-    case apps::mojom::LaunchSource::kFromNewTabPage:
-    case apps::mojom::LaunchSource::kFromIntentUrl:
-    case apps::mojom::LaunchSource::kFromOsLogin:
-    case apps::mojom::LaunchSource::kFromProtocolHandler:
-    case apps::mojom::LaunchSource::kFromUrlHandler:
+    case apps::LaunchSource::kFromFileManager:
+    case apps::LaunchSource::kFromLink:
+    case apps::LaunchSource::kFromOmnibox:
+    case apps::LaunchSource::kFromChromeInternal:
+    case apps::LaunchSource::kFromKeyboard:
+    case apps::LaunchSource::kFromOtherApp:
+    case apps::LaunchSource::kFromMenu:
+    case apps::LaunchSource::kFromInstalledNotification:
+    case apps::LaunchSource::kFromTest:
+    case apps::LaunchSource::kFromArc:
+    case apps::LaunchSource::kFromSharesheet:
+    case apps::LaunchSource::kFromReleaseNotesNotification:
+    case apps::LaunchSource::kFromFullRestore:
+    case apps::LaunchSource::kFromSmartTextContextMenu:
+    case apps::LaunchSource::kFromDiscoverTabNotification:
+    case apps::LaunchSource::kFromManagementApi:
+    case apps::LaunchSource::kFromKiosk:
+    case apps::LaunchSource::kFromCommandLine:
+    case apps::LaunchSource::kFromBackgroundMode:
+    case apps::LaunchSource::kFromNewTabPage:
+    case apps::LaunchSource::kFromIntentUrl:
+    case apps::LaunchSource::kFromOsLogin:
+    case apps::LaunchSource::kFromProtocolHandler:
+    case apps::LaunchSource::kFromUrlHandler:
       return ash::LAUNCH_FROM_UNKNOWN;
   }
 }
@@ -327,7 +326,8 @@
   }
 
   auto params = apps::CreateAppLaunchParamsForIntent(
-      app_id, event_flags, launch_source,
+      app_id, event_flags,
+      ConvertMojomLaunchSourceToLaunchSource(launch_source),
       window_info ? window_info->display_id : display::kInvalidDisplayId,
       extensions::GetLaunchContainer(extensions::ExtensionPrefs::Get(profile_),
                                      extension),
@@ -427,7 +427,78 @@
                                int32_t event_flags,
                                LaunchSource launch_source,
                                WindowInfoPtr window_info) {
-  // TODO(crbug.com/1253250): Add the implementation.
+  const auto* extension = MaybeGetExtension(app_id);
+  if (!extension || !extensions::util::IsAppLaunchable(app_id, profile_)) {
+    return;
+  }
+
+  if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id, profile_)) {
+    RunExtensionEnableFlow(
+        app_id, base::BindOnce(&ExtensionAppsBase::LaunchWhenEnabled,
+                               weak_factory_.GetWeakPtr(), app_id, event_flags,
+                               launch_source, std::move(window_info)));
+    return;
+  }
+
+  switch (launch_source) {
+    case apps::LaunchSource::kUnknown:
+    case apps::LaunchSource::kFromParentalControls:
+      break;
+    case apps::LaunchSource::kFromAppListGrid:
+    case apps::LaunchSource::kFromAppListGridContextMenu:
+      extensions::RecordAppListMainLaunch(extension);
+      break;
+    case apps::LaunchSource::kFromAppListQuery:
+    case apps::LaunchSource::kFromAppListQueryContextMenu:
+      extensions::RecordAppListSearchLaunch(extension);
+      break;
+    case apps::LaunchSource::kFromAppListRecommendation:
+    case apps::LaunchSource::kFromShelf:
+    case apps::LaunchSource::kFromFileManager:
+    case apps::LaunchSource::kFromLink:
+    case apps::LaunchSource::kFromOmnibox:
+    case apps::LaunchSource::kFromChromeInternal:
+    case apps::LaunchSource::kFromKeyboard:
+    case apps::LaunchSource::kFromOtherApp:
+    case apps::LaunchSource::kFromMenu:
+    case apps::LaunchSource::kFromInstalledNotification:
+    case apps::LaunchSource::kFromTest:
+    case apps::LaunchSource::kFromArc:
+    case apps::LaunchSource::kFromSharesheet:
+    case apps::LaunchSource::kFromReleaseNotesNotification:
+    case apps::LaunchSource::kFromFullRestore:
+    case apps::LaunchSource::kFromSmartTextContextMenu:
+    case apps::LaunchSource::kFromDiscoverTabNotification:
+    case apps::LaunchSource::kFromManagementApi:
+    case apps::LaunchSource::kFromKiosk:
+    case apps::LaunchSource::kFromCommandLine:
+    case apps::LaunchSource::kFromBackgroundMode:
+    case apps::LaunchSource::kFromNewTabPage:
+    case apps::LaunchSource::kFromIntentUrl:
+    case apps::LaunchSource::kFromOsLogin:
+    case apps::LaunchSource::kFromProtocolHandler:
+    case apps::LaunchSource::kFromUrlHandler:
+      break;
+  }
+
+  // The app will be created for the currently active profile.
+  AppLaunchParams params = CreateAppLaunchParamsWithEventFlags(
+      profile_, extension, event_flags, launch_source,
+      window_info ? window_info->display_id : display::kInvalidDisplayId);
+  ash::ShelfLaunchSource source = ConvertLaunchSource(launch_source);
+  if ((source == ash::LAUNCH_FROM_APP_LIST ||
+       source == ash::LAUNCH_FROM_APP_LIST_SEARCH) &&
+      app_id == extensions::kWebStoreAppId) {
+    // Get the corresponding source string.
+    std::string source_value = GetSourceFromAppListSource(source);
+
+    // Set an override URL to include the source.
+    GURL extension_url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
+    params.override_url = net::AppendQueryParameter(
+        extension_url, extension_urls::kWebstoreSourceField, source_value);
+  }
+
+  LaunchImpl(std::move(params));
 }
 
 void ExtensionAppsBase::LaunchAppWithParams(AppLaunchParams&& params,
@@ -479,7 +550,7 @@
 
 void ExtensionAppsBase::Launch(const std::string& app_id,
                                int32_t event_flags,
-                               apps::mojom::LaunchSource launch_source,
+                               apps::mojom::LaunchSource mojom_launch_source,
                                apps::mojom::WindowInfoPtr window_info) {
   const auto* extension = MaybeGetExtension(app_id);
   if (!extension || !extensions::util::IsAppLaunchable(app_id, profile_)) {
@@ -490,11 +561,11 @@
     RunExtensionEnableFlow(
         app_id, base::BindOnce(&ExtensionAppsBase::LaunchMojom,
                                weak_factory_.GetWeakPtr(), app_id, event_flags,
-                               launch_source, std::move(window_info)));
+                               mojom_launch_source, std::move(window_info)));
     return;
   }
 
-  switch (launch_source) {
+  switch (mojom_launch_source) {
     case apps::mojom::LaunchSource::kUnknown:
     case apps::mojom::LaunchSource::kFromParentalControls:
       break;
@@ -535,6 +606,9 @@
       break;
   }
 
+  auto launch_source =
+      ConvertMojomLaunchSourceToLaunchSource(mojom_launch_source);
+
   // The app will be created for the currently active profile.
   AppLaunchParams params = CreateAppLaunchParamsWithEventFlags(
       profile_, extension, event_flags, launch_source,
@@ -565,7 +639,8 @@
       app_id,
       extensions::GetLaunchContainer(extensions::ExtensionPrefs::Get(profile_),
                                      extension),
-      ui::DispositionFromEventFlags(event_flags), launch_source,
+      ui::DispositionFromEventFlags(event_flags),
+      ConvertMojomLaunchSourceToLaunchSource(launch_source),
       display::kDefaultDisplayId);
   for (const auto& file_path : file_paths->file_paths) {
     params.launch_files.push_back(file_path);
@@ -829,6 +904,13 @@
   }
 }
 
+void ExtensionAppsBase::LaunchWhenEnabled(const std::string& app_id,
+                                          int32_t event_flags,
+                                          LaunchSource launch_source,
+                                          WindowInfoPtr window_info) {
+  Launch(app_id, event_flags, launch_source, std::move(window_info));
+}
+
 void ExtensionAppsBase::LaunchMojom(const std::string& app_id,
                                     int32_t event_flags,
                                     apps::mojom::LaunchSource launch_source,
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_base.h b/chrome/browser/apps/app_service/publishers/extension_apps_base.h
index 0aaf8c6..156140e 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_base.h
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_base.h
@@ -240,6 +240,16 @@
                      apps::mojom::Readiness readiness,
                      std::vector<apps::mojom::AppPtr>* apps_out);
 
+  // TODO(crbug.com/1253250): This function is used as `callback` for
+  // RunExtensionEnableFlow. The Launch interface can't be used as `callback`
+  // with `base::BindOnce`, because we have both mojom and non mojom Launch
+  // function. Remove this function after migrating to the non mojom Launch
+  // interface when we have one non mojom Launch interface only.
+  void LaunchWhenEnabled(const std::string& app_id,
+                         int32_t event_flags,
+                         LaunchSource launch_source,
+                         WindowInfoPtr window_info);
+
   // TODO(crbug.com/1253250): Remove after migrating to the non mojom Launch
   // interface.
   void LaunchMojom(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
index f6148d5..6a81433a 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
@@ -193,7 +193,7 @@
     auto launch_source = params.launch_source;
     content::WebContents* web_contents = LaunchImpl(std::move(params));
 
-    if (launch_source == apps::mojom::LaunchSource::kFromArc && web_contents) {
+    if (launch_source == apps::LaunchSource::kFromArc && web_contents) {
       // Add a flag to remember this web_contents originated in the ARC context.
       web_contents->SetUserData(
           &arc::ArcWebContentsData::kArcTransitionFlag,
@@ -205,10 +205,10 @@
     auto event_flags = apps::GetEventFlags(params.disposition,
                                            /*prefer_container=*/false);
     auto window_info = apps::MakeWindowInfo(params.display_id);
-    LaunchExtension(
-        params.app_id, event_flags, std::move(params.intent),
-        ConvertMojomLaunchSourceToLaunchSource(params.launch_source),
-        ConvertMojomWindowInfoToWindowInfo(window_info), base::DoNothing());
+    LaunchExtension(params.app_id, event_flags, std::move(params.intent),
+                    params.launch_source,
+                    ConvertMojomWindowInfoToWindowInfo(window_info),
+                    base::DoNothing());
   }
 }
 
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc
index 223a41e..4d49ddc 100644
--- a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc
@@ -129,7 +129,30 @@
                                             int32_t event_flags,
                                             LaunchSource launch_source,
                                             WindowInfoPtr window_info) {
-  // TODO(crbug.com/1253250): Add the implementation.
+  // It is possible that Lacros is briefly unavailable, for example if it shuts
+  // down for an update.
+  if (!controller_.is_bound())
+    return;
+
+  // The following code assumes |app_type_| must be
+  // AppType::kStandaloneBrowserChromeApp. Therefore, the app must be either
+  // platform app or hosted app.
+  // In the future, this class is possible to be instantiated with other
+  // AppType, please make sure to modify the logic if necessary.
+  controller_->Launch(
+      CreateCrosapiLaunchParamsWithEventFlags(
+          proxy(), app_id, event_flags, launch_source,
+          window_info ? window_info->display_id : display::kInvalidDisplayId),
+      /*callback=*/base::DoNothing());
+
+  if (ShouldSaveToFullRestore(proxy(), app_id)) {
+    auto launch_info = std::make_unique<app_restore::AppLaunchInfo>(
+        app_id, apps::LaunchContainer::kLaunchContainerNone,
+        WindowOpenDisposition::UNKNOWN, display::kInvalidDisplayId,
+        std::vector<base::FilePath>{}, nullptr);
+    full_restore::SaveAppLaunchInfo(proxy()->profile()->GetPath(),
+                                    std::move(launch_info));
+  }
 }
 
 void StandaloneBrowserExtensionApps::LaunchAppWithParams(
@@ -189,7 +212,8 @@
   // AppType, please make sure to modify the logic if necessary.
   controller_->Launch(
       CreateCrosapiLaunchParamsWithEventFlags(
-          proxy(), app_id, event_flags, launch_source,
+          proxy(), app_id, event_flags,
+          ConvertMojomLaunchSourceToLaunchSource(launch_source),
           window_info ? window_info->display_id : display::kInvalidDisplayId),
       /*callback=*/base::DoNothing());
 
diff --git a/chrome/browser/apps/app_service/publishers/web_apps_crosapi.cc b/chrome/browser/apps/app_service/publishers/web_apps_crosapi.cc
index 5182beb7..e2098c9 100644
--- a/chrome/browser/apps/app_service/publishers/web_apps_crosapi.cc
+++ b/chrome/browser/apps/app_service/publishers/web_apps_crosapi.cc
@@ -94,7 +94,15 @@
                             int32_t event_flags,
                             LaunchSource launch_source,
                             WindowInfoPtr window_info) {
-  // TODO(crbug.com/1253250): Add the implementation.
+  if (!LogIfNotConnected(FROM_HERE)) {
+    return;
+  }
+
+  controller_->Launch(
+      CreateCrosapiLaunchParamsWithEventFlags(
+          proxy_, app_id, event_flags, launch_source,
+          window_info ? window_info->display_id : display::kInvalidDisplayId),
+      base::DoNothing());
 }
 
 void WebAppsCrosapi::LaunchAppWithParams(AppLaunchParams&& params,
@@ -137,7 +145,8 @@
 
   controller_->Launch(
       CreateCrosapiLaunchParamsWithEventFlags(
-          proxy_, app_id, event_flags, launch_source,
+          proxy_, app_id, event_flags,
+          ConvertMojomLaunchSourceToLaunchSource(launch_source),
           window_info ? window_info->display_id : display::kInvalidDisplayId),
       base::DoNothing());
 }
@@ -155,7 +164,8 @@
   }
 
   auto params = CreateCrosapiLaunchParamsWithEventFlags(
-      proxy_, app_id, event_flags, launch_source,
+      proxy_, app_id, event_flags,
+      ConvertMojomLaunchSourceToLaunchSource(launch_source),
       window_info ? window_info->display_id : display::kInvalidDisplayId);
 
   params->intent =
@@ -174,7 +184,9 @@
   }
 
   auto params = CreateCrosapiLaunchParamsWithEventFlags(
-      proxy_, app_id, event_flags, launch_source, display::kInvalidDisplayId);
+      proxy_, app_id, event_flags,
+      ConvertMojomLaunchSourceToLaunchSource(launch_source),
+      display::kInvalidDisplayId);
   params->intent = apps_util::CreateCrosapiIntentForViewFiles(file_paths);
   controller_->Launch(std::move(params), base::DoNothing());
 }
diff --git a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
index d5d3e0b..fcac278 100644
--- a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
+++ b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
@@ -222,11 +222,10 @@
 
   apps::LaunchContainer launch_container =
       web_app::ConvertDisplayModeToAppLaunchContainer(effective_display_mode);
-  apps::mojom::LaunchSource launch_source =
-      apps::mojom::LaunchSource::kFromCommandLine;
+  apps::LaunchSource launch_source = apps::LaunchSource::kFromCommandLine;
   if (login_item_restore_state !=
       chrome::mojom::AppShimLoginItemRestoreState::kNone) {
-    launch_source = apps::mojom::LaunchSource::kFromOsLogin;
+    launch_source = apps::LaunchSource::kFromOsLogin;
   }
 
   apps::AppLaunchParams params(app_id, launch_container,
@@ -270,7 +269,7 @@
     }
 
     params.protocol_handler_launch_url = url;
-    params.launch_source = apps::mojom::LaunchSource::kFromProtocolHandler;
+    params.launch_source = apps::LaunchSource::kFromProtocolHandler;
   }
 
   WebAppProvider* const provider = WebAppProvider::GetForWebApps(profile);
diff --git a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac_unittest.cc b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac_unittest.cc
index 5ebb82380..0370ca9 100644
--- a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac_unittest.cc
+++ b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac_unittest.cc
@@ -144,7 +144,7 @@
     apps::AppLaunchParams params(app_id_,
                                  apps::LaunchContainer::kLaunchContainerWindow,
                                  WindowOpenDisposition::NEW_WINDOW,
-                                 apps::mojom::LaunchSource::kFromCommandLine);
+                                 apps::LaunchSource::kFromCommandLine);
 
     params.launch_files = launch_files;
     params.url_handler_launch_url = url_handler_launch_url;
@@ -213,8 +213,7 @@
   apps::AppLaunchParams expected_results =
       CreateLaunchParams(std::vector<base::FilePath>(), absl::nullopt,
                          protocol_handler_launch_url, GURL());
-  expected_results.launch_source =
-      apps::mojom::LaunchSource::kFromProtocolHandler;
+  expected_results.launch_source = apps::LaunchSource::kFromProtocolHandler;
 
   std::unique_ptr<MockDelegate> delegate = std::make_unique<MockDelegate>();
   WebAppShimManagerDelegate shim_manager(std::move(delegate));
@@ -236,8 +235,7 @@
   apps::AppLaunchParams expected_results =
       CreateLaunchParams(std::vector<base::FilePath>(), absl::nullopt,
                          protocol_handler_launch_url, GURL());
-  expected_results.launch_source =
-      apps::mojom::LaunchSource::kFromProtocolHandler;
+  expected_results.launch_source = apps::LaunchSource::kFromProtocolHandler;
 
   std::unique_ptr<MockDelegate> delegate = std::make_unique<MockDelegate>();
   WebAppShimManagerDelegate shim_manager(std::move(delegate));
@@ -382,8 +380,7 @@
 
   apps::AppLaunchParams expected_results = CreateLaunchParams(
       {}, absl::nullopt, protocol_handler_launch_url, GURL());
-  expected_results.launch_source =
-      apps::mojom::LaunchSource::kFromProtocolHandler;
+  expected_results.launch_source = apps::LaunchSource::kFromProtocolHandler;
 
   std::unique_ptr<MockDelegate> delegate = std::make_unique<MockDelegate>();
   WebAppShimManagerDelegate shim_manager(std::move(delegate));
@@ -409,8 +406,7 @@
 
   apps::AppLaunchParams expected_results = CreateLaunchParams(
       {}, absl::nullopt, protocol_handler_launch_url, GURL());
-  expected_results.launch_source =
-      apps::mojom::LaunchSource::kFromProtocolHandler;
+  expected_results.launch_source = apps::LaunchSource::kFromProtocolHandler;
 
   std::unique_ptr<MockDelegate> delegate = std::make_unique<MockDelegate>();
   WebAppShimManagerDelegate shim_manager(std::move(delegate));
diff --git a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
index 7493ad9e..556d1e0 100644
--- a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
+++ b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
@@ -280,7 +280,7 @@
   extensions::ResultCatcher catcher;
   apps::AppLaunchParams params(
       extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-      WindowOpenDisposition::NEW_WINDOW, apps::mojom::LaunchSource::kFromTest);
+      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
   params.command_line = *base::CommandLine::ForCurrentProcess();
   apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
       ->BrowserAppLauncher()
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index 45d7a53..6ad44e4 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -268,10 +268,9 @@
       return false;
     }
 
-    apps::AppLaunchParams params(extension->id(),
-                                 apps::LaunchContainer::kLaunchContainerNone,
-                                 WindowOpenDisposition::NEW_WINDOW,
-                                 apps::mojom::LaunchSource::kFromTest);
+    apps::AppLaunchParams params(
+        extension->id(), apps::LaunchContainer::kLaunchContainerNone,
+        WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
     params.command_line = command_line;
     params.current_directory = test_data_dir_;
     apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
@@ -951,8 +950,7 @@
         ->BrowserAppLauncher()
         ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
             extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-            WindowOpenDisposition::NEW_WINDOW,
-            apps::mojom::LaunchSource::kFromTest));
+            WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
     app_loaded_observer.Wait();
     window = GetFirstAppWindow();
     ASSERT_TRUE(window);
@@ -1102,8 +1100,7 @@
       ->BrowserAppLauncher()
       ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
           extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-          WindowOpenDisposition::NEW_WINDOW,
-          apps::mojom::LaunchSource::kFromTest));
+          WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
 
   ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
 }
@@ -1127,8 +1124,7 @@
       ->BrowserAppLauncher()
       ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
           extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-          WindowOpenDisposition::NEW_WINDOW,
-          apps::mojom::LaunchSource::kFromTest));
+          WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
 
   ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
   ASSERT_FALSE(should_not_install.seen());
@@ -1168,8 +1164,7 @@
       ->BrowserAppLauncher()
       ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
           extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-          WindowOpenDisposition::NEW_WINDOW,
-          apps::mojom::LaunchSource::kFromTest));
+          WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
 
   ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
 }
@@ -1196,8 +1191,7 @@
         ->BrowserAppLauncher()
         ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
             extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-            WindowOpenDisposition::NEW_WINDOW,
-            apps::mojom::LaunchSource::kFromTest));
+            WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
     ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
   }
 
diff --git a/chrome/browser/apps/platform_apps/app_browsertest_util.cc b/chrome/browser/apps/platform_apps/app_browsertest_util.cc
index 9b482f6..c7f57d8 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest_util.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest_util.cc
@@ -163,8 +163,7 @@
       ->BrowserAppLauncher()
       ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
           extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-          WindowOpenDisposition::NEW_WINDOW,
-          apps::mojom::LaunchSource::kFromTest));
+          WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
 }
 
 void PlatformAppBrowserTest::LaunchHostedApp(const Extension* extension) {
@@ -173,7 +172,7 @@
       ->LaunchAppWithParamsForTesting(CreateAppLaunchParamsUserContainer(
           browser()->profile(), extension,
           WindowOpenDisposition::NEW_FOREGROUND_TAB,
-          apps::mojom::LaunchSource::kFromCommandLine));
+          apps::LaunchSource::kFromCommandLine));
 }
 
 WebContents* PlatformAppBrowserTest::GetFirstAppWindowWebContents() {
diff --git a/chrome/browser/apps/platform_apps/app_window_browsertest.cc b/chrome/browser/apps/platform_apps/app_window_browsertest.cc
index f01ee52..a56c1a08 100644
--- a/chrome/browser/apps/platform_apps/app_window_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_window_browsertest.cc
@@ -234,8 +234,7 @@
       ->BrowserAppLauncher()
       ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
           extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-          WindowOpenDisposition::NEW_WINDOW,
-          apps::mojom::LaunchSource::kFromTest));
+          WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
 
   ExtensionTestMessageListener geometry_listener("ListenGeometryChange",
                                                  ReplyBehavior::kWillReply);
diff --git a/chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.cc b/chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.cc
index 649cf7f3..ea071ab 100644
--- a/chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.cc
+++ b/chrome/browser/apps/platform_apps/extension_app_shim_manager_delegate_mac.cc
@@ -195,7 +195,7 @@
   if (extension->is_hosted_app()) {
     auto params = CreateAppLaunchParamsUserContainer(
         profile, extension, WindowOpenDisposition::NEW_FOREGROUND_TAB,
-        apps::mojom::LaunchSource::kFromCommandLine);
+        apps::LaunchSource::kFromCommandLine);
     params.launch_files = files;
     apps::AppServiceProxyFactory::GetForProfile(profile)
         ->BrowserAppLauncher()
diff --git a/chrome/browser/apps/platform_apps/platform_app_launch.cc b/chrome/browser/apps/platform_apps/platform_app_launch.cc
index b78bfd3..2579f7e 100644
--- a/chrome/browser/apps/platform_apps/platform_app_launch.cc
+++ b/chrome/browser/apps/platform_apps/platform_app_launch.cc
@@ -92,7 +92,7 @@
 
   apps::AppLaunchParams params(app_id, launch_container,
                                WindowOpenDisposition::NEW_WINDOW,
-                               apps::mojom::LaunchSource::kFromCommandLine);
+                               apps::LaunchSource::kFromCommandLine);
   params.command_line = command_line;
   params.current_directory = current_directory;
 
@@ -119,7 +119,7 @@
       profile,
       apps::AppLaunchParams(app_id, apps::LaunchContainer::kLaunchContainerTab,
                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                            apps::mojom::LaunchSource::kFromCommandLine));
+                            apps::LaunchSource::kFromCommandLine));
   return app_tab != nullptr;
 }
 
@@ -162,10 +162,9 @@
     return false;
 
   RecordCmdLineAppHistogram(extensions::Manifest::TYPE_PLATFORM_APP);
-  apps::AppLaunchParams params(app_id,
-                               apps::LaunchContainer::kLaunchContainerNone,
-                               WindowOpenDisposition::NEW_WINDOW,
-                               apps::mojom::LaunchSource::kFromCommandLine);
+  apps::AppLaunchParams params(
+      app_id, apps::LaunchContainer::kLaunchContainerNone,
+      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromCommandLine);
   params.command_line = command_line;
   params.current_directory = current_directory;
   ::OpenApplicationWithReenablePrompt(profile, std::move(params));
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 20f06c27..c9c8d93 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -1576,6 +1576,7 @@
     "//chromeos/ash/components/dbus/concierge",
     "//chromeos/ash/components/dbus/concierge:concierge_proto",
     "//chromeos/ash/components/dbus/fusebox:proto",
+    "//chromeos/ash/components/dbus/gnubby",
     "//chromeos/ash/components/dbus/image_burner",
     "//chromeos/ash/components/dbus/kerberos:kerberos_proto",
     "//chromeos/ash/components/dbus/os_install",
@@ -1608,7 +1609,6 @@
     "//chromeos/dbus/cryptohome:cryptohome_proto",
     "//chromeos/dbus/debug_daemon",
     "//chromeos/dbus/dlcservice",
-    "//chromeos/dbus/gnubby",
     "//chromeos/dbus/missive",
     "//chromeos/dbus/power",
     "//chromeos/dbus/power:power_manager_proto",
@@ -1818,6 +1818,7 @@
     "//chrome/common:non_code_constants",
     "//chrome/common/net",
     "//chromeos/ash/components/assistant:buildflags",
+    "//chromeos/ash/components/dbus/arc",
     "//chromeos/ash/components/dbus/audio",
     "//chromeos/ash/components/dbus/biod",
     "//chromeos/ash/components/dbus/biod:biod_proto",
@@ -1861,7 +1862,6 @@
     "//chromeos/dbus:vm_disk_management_proto",
     "//chromeos/dbus:vm_permission_service_proto",
     "//chromeos/dbus:vm_sk_forwarding_proto",
-    "//chromeos/dbus/arc",
     "//chromeos/dbus/cryptohome:attestation_proto",
     "//chromeos/dbus/dlcservice:dlcservice_proto",
     "//chromeos/dbus/dlp",
diff --git a/chrome/browser/ash/app_restore/app_launch_handler.cc b/chrome/browser/ash/app_restore/app_launch_handler.cc
index 6342934..779d745 100644
--- a/chrome/browser/ash/app_restore/app_launch_handler.cc
+++ b/chrome/browser/ash/app_restore/app_launch_handler.cc
@@ -251,8 +251,7 @@
         app_id,
         static_cast<apps::LaunchContainer>(it.second->container.value()),
         static_cast<WindowOpenDisposition>(it.second->disposition.value()),
-        apps::mojom::LaunchSource::kFromFullRestore,
-        it.second->display_id.value(),
+        apps::LaunchSource::kFromFullRestore, it.second->display_id.value(),
         it.second->file_paths.has_value() ? it.second->file_paths.value()
                                           : std::vector<base::FilePath>{},
         it.second->intent ? it.second->intent->Clone() : nullptr);
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
index 929f3ab6..0429e3a 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -226,7 +226,7 @@
   SetDisplayMode(DisplayMode::kMenu);
 
   input_menu_view_ = parent_view->AddChildView(
-      InputMenuView::BuildMenuView(this, menu_entry_));
+      InputMenuView::BuildMenuView(this, menu_entry_, parent_view->size()));
   // Hide the menu entry when the menu is displayed.
   menu_entry_->SetVisible(false);
 }
diff --git a/chrome/browser/ash/arc/input_overlay/ui/educational_view.cc b/chrome/browser/ash/arc/input_overlay/ui/educational_view.cc
index a5c6b60..5bed2e0 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/educational_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/educational_view.cc
@@ -31,10 +31,9 @@
 
 namespace {
 // Full view size.
-constexpr int kDialogWidthLandscape = 416;
-constexpr int kDialogHeightLandscape = 380;
-constexpr int kDialogWidthPortrait = 320;
-constexpr int kDialogHeightPortrait = 305;
+constexpr int kDialogWidthMax = 416;
+constexpr int kDialogWidthMin = 100;
+constexpr int kDialogMarginMin = 24;
 
 // About title style.
 constexpr int kDialogShadowElevation = 3;
@@ -97,12 +96,11 @@
   return portrait_mode ? kBorderSidesPortrait : kBorderSidesLandscape;
 }
 
-int GetDialogWidth(bool portrait_mode) {
-  return portrait_mode ? kDialogWidthPortrait : kDialogWidthLandscape;
-}
+int GetDialogWidth(int parent_width) {
+  if (parent_width < kDialogWidthMax + 2 * kDialogMarginMin)
+    return std::max(kDialogWidthMin, parent_width - 2 * kDialogMarginMin);
 
-int GetDialogHeight(bool portrait_mode) {
-  return portrait_mode ? kDialogHeightPortrait : kDialogHeightLandscape;
+  return kDialogWidthMax;
 }
 
 int GetTitleFontSize(bool portrait_mode) {
@@ -117,7 +115,7 @@
     views::View* parent) {
   auto educational_view = std::make_unique<EducationalView>(
       display_overlay_controller, parent->width());
-  educational_view->Init(parent);
+  educational_view->Init(parent->size());
   auto* view_ptr = parent->AddChildView(std::move(educational_view));
   view_ptr->AddShadow();
 
@@ -128,13 +126,12 @@
     DisplayOverlayController* display_overlay_controller,
     int parent_width)
     : display_overlay_controller_(display_overlay_controller) {
-  portrait_mode_ = parent_width < kDialogWidthLandscape;
+  portrait_mode_ = parent_width < kDialogWidthMax;
 }
 
 EducationalView::~EducationalView() {}
 
-void EducationalView::Init(views::View* parent) {
-  DCHECK(parent);
+void EducationalView::Init(const gfx::Size& parent_size) {
   DCHECK(display_overlay_controller_);
 
   SetLayoutManager(std::make_unique<views::FlexLayout>())
@@ -144,6 +141,7 @@
   SetBackground(views::CreateRoundedRectBackground(
       GetDialogBackgroundBaseColor(), kDialogCornerRadius));
 
+  const int parent_width = parent_size.width();
   {
     // UI's banner.
     const gfx::ImageSkia* skia_banner =
@@ -159,7 +157,8 @@
       // TODO(djacobo): Confirm scale factor, for now 70% looks fine.
       resized_banner = gfx::ImageSkiaOperations::CreateResizedImage(
           *skia_banner, skia::ImageOperations::RESIZE_BEST,
-          gfx::Size(kDialogWidthPortrait * 0.7, kBannerHeightPortrait * 0.7));
+          gfx::Size(GetDialogWidth(parent_width) * 0.7,
+                    kBannerHeightPortrait * 0.7));
     }
     auto banner =
         std::make_unique<views::ImageView>(ui::ImageModel::FromImageSkia(
@@ -237,7 +236,7 @@
                                               GetBorderRow3(portrait_mode_),
                                               GetBorderSides(portrait_mode_)));
     description_label->SetMultiLine(true);
-    description_label->SetMaximumWidth(GetDialogWidth(portrait_mode_) -
+    description_label->SetMaximumWidth(GetDialogWidth(parent_width) -
                                        2 * GetBorderSides(portrait_mode_));
     description_label->SetSize(gfx::Size());
   }
@@ -266,7 +265,6 @@
       gfx::Insets::TLBR(0, 0, GetBorderRow4(portrait_mode_), 0)));
   const auto ui_size = GetPreferredSize();
   SetSize(ui_size);
-  const auto parent_size = parent->size();
   SetPosition(gfx::Point((parent_size.width() - ui_size.width()) / 2,
                          (parent_size.height() - ui_size.height()) / 2));
 }
diff --git a/chrome/browser/ash/arc/input_overlay/ui/educational_view.h b/chrome/browser/ash/arc/input_overlay/ui/educational_view.h
index 1aca1e1..a18fb51 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/educational_view.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/educational_view.h
@@ -35,7 +35,7 @@
   ~EducationalView() override;
 
  private:
-  void Init(views::View* parent);
+  void Init(const gfx::Size& parent_size);
   void OnAcceptedPressed();
   // Shadow has to be added after the view is added to its parent to display the
   // shadow correctly.
diff --git a/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.cc b/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.cc
index c1161a0..966534c 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.cc
@@ -44,9 +44,15 @@
 namespace input_overlay {
 
 namespace {
+// If the parent's width smaller than |kParentWidthThreshold|, it uses smaller
+// specs.
+constexpr int kParentWidthThreshold = 376;
 // Whole Menu measurements.
 constexpr int kMenuWidth = 328;
+constexpr int kMenuWidthSmall = 280;
 constexpr int kMenuHeight = 244;
+constexpr int kMenuMarginRight = 32;
+constexpr int kMenuMarginRightSmall = 24;
 
 // Individual entries and header.
 constexpr int kHeaderMinHeight = 64;
@@ -64,6 +70,7 @@
 // String styles/sizes.
 constexpr char kGoogleSansFont[] = "Google Sans";
 constexpr int kTitleFontSize = 20;
+constexpr int kTitleFontSizeSmall = 16;
 constexpr int kBodyFontSize = 13;
 
 // About Alpha style.
@@ -72,6 +79,7 @@
 constexpr int kAlphaHeight = 16;
 constexpr int kAlphaSidePadding = 4;
 constexpr int kAlphaLeftMargin = 8;
+constexpr int kAlphaLeftMarginSmall = 4;
 
 constexpr char kFeedbackUrl[] =
     "https://docs.google.com/forms/d/e/"
@@ -93,6 +101,25 @@
   return url;
 }
 
+int GetMenuWidth(int parent_width) {
+  return parent_width < kParentWidthThreshold ? kMenuWidthSmall : kMenuWidth;
+}
+
+int GetMenuMarginRight(int parent_width) {
+  return parent_width < kParentWidthThreshold ? kMenuMarginRightSmall
+                                              : kMenuMarginRight;
+}
+
+int GetTitleFontSize(int parent_width) {
+  return parent_width < kParentWidthThreshold ? kTitleFontSizeSmall
+                                              : kTitleFontSize;
+}
+
+int GetAlphaLeftMargin(int parent_width) {
+  return parent_width < kParentWidthThreshold ? kAlphaLeftMarginSmall
+                                              : kAlphaLeftMargin;
+}
+
 }  // namespace
 
 class InputMenuView::FeedbackButton : public views::LabelButton {
@@ -135,14 +162,15 @@
 // static
 std::unique_ptr<InputMenuView> InputMenuView::BuildMenuView(
     DisplayOverlayController* display_overlay_controller,
-    views::View* entry_view) {
+    views::View* entry_view,
+    const gfx::Size& parent_size) {
   // Ensure there is only one menu at any time.
   if (display_overlay_controller->HasMenuView())
     display_overlay_controller->RemoveInputMenuView();
 
   auto menu_view_ptr =
       std::make_unique<InputMenuView>(display_overlay_controller, entry_view);
-  menu_view_ptr->Init();
+  menu_view_ptr->Init(parent_size);
 
   return menu_view_ptr;
 }
@@ -160,7 +188,7 @@
     display_overlay_controller_->SetDisplayMode(DisplayMode::kView);
 }
 
-void InputMenuView::Init() {
+void InputMenuView::Init(const gfx::Size& parent_size) {
   DCHECK(display_overlay_controller_);
   DCHECK(ash::AshColorProvider::Get());
   SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -171,7 +199,7 @@
   SetBackground(views::CreateRoundedRectBackground(bg_color, kCornerRadius));
   SkColor color = color_provider->GetContentLayerColor(
       ash::AshColorProvider::ContentLayerType::kTextColorPrimary);
-
+  int menu_width = GetMenuWidth(parent_size.width());
   {
     // Title, main control for the feature and close button.
     auto header_view = std::make_unique<views::View>();
@@ -185,7 +213,8 @@
             /*view_defining_max_width=*/nullptr, color,
             /*font_list=*/
             gfx::FontList({kGoogleSansFont}, gfx::Font::FontStyle::NORMAL,
-                          kTitleFontSize, gfx::Font::Weight::MEDIUM),
+                          GetTitleFontSize(parent_size.width()),
+                          gfx::Font::Weight::MEDIUM),
             /*line_height=*/kHeaderMinHeight));
 
     auto* alpha_label =
@@ -237,8 +266,9 @@
     SetCustomToggleColor(game_control_toggle_);
     alpha_label->SetProperty(
         views::kMarginsKey,
-        CalculateInsets(header_view.get(), kAlphaLeftMargin, /*right=*/0,
-                        /*other_spacing=*/kCloseInset));
+        CalculateInsets(header_view.get(),
+                        GetAlphaLeftMargin(parent_size.width()), /*right=*/0,
+                        /*other_spacing=*/kCloseInset, menu_width));
 
     AddChildView(std::move(header_view));
     AddChildView(BuildSeparator());
@@ -267,9 +297,9 @@
             ash::PillButton::Type::kIconless,
             /*icon=*/nullptr));
     edit_button_->SetEnabled(game_control_toggle_->GetIsOn());
-    key_mapping_label->SetBorder(views::CreateEmptyBorder(
-        CalculateInsets(customize_view.get(), /*left=*/kSideInset,
-                        /*right=*/kSideInset, /*other_spacing=*/0)));
+    key_mapping_label->SetBorder(views::CreateEmptyBorder(CalculateInsets(
+        customize_view.get(), /*left=*/kSideInset,
+        /*right=*/kSideInset, /*other_spacing=*/0, menu_width)));
     AddChildView(std::move(customize_view));
     AddChildView(BuildSeparator());
   }
@@ -298,9 +328,9 @@
         game_control_toggle_->GetIsOn() &&
         display_overlay_controller_->GetInputMappingViewVisible());
     SetCustomToggleColor(show_mapping_toggle_);
-    mapping_label->SetBorder(views::CreateEmptyBorder(
-        CalculateInsets(hint_view.get(), /*left=*/kSideInset,
-                        /*right=*/kSideInset, /*other_spacing=*/0)));
+    mapping_label->SetBorder(views::CreateEmptyBorder(CalculateInsets(
+        hint_view.get(), /*left=*/kSideInset,
+        /*right=*/kSideInset, /*other_spacing=*/0, menu_width)));
     AddChildView(std::move(hint_view));
     AddChildView(BuildSeparator());
   }
@@ -319,9 +349,15 @@
     AddChildView(std::move(feedback_button));
   }
 
-  SetSize(gfx::Size(kMenuWidth, kMenuHeight));
-  SetPosition(gfx::Point(entry_view_->x() - width() + entry_view_->width(),
-                         entry_view_->y()));
+  SetSize(gfx::Size(menu_width, kMenuHeight));
+  int x = std::max(0, parent_size.width() - width() -
+                          GetMenuMarginRight(parent_size.width()));
+  if (x < GetMenuMarginRight(parent_size.width())) {
+    // Set the menu in the middle if there is not enough margin on the left
+    // side.
+    x = std::max(0, (parent_size.width() - width()) / 2);
+  }
+  SetPosition(gfx::Point(x, entry_view_->y()));
 }
 
 std::unique_ptr<views::View> InputMenuView::BuildSeparator() {
@@ -377,13 +413,14 @@
 gfx::Insets InputMenuView::CalculateInsets(views::View* view,
                                            int left,
                                            int right,
-                                           int other_spacing) const {
+                                           int other_spacing,
+                                           int menu_width) const {
   int total_width = 0;
   for (auto* child : view->children())
     total_width += child->GetPreferredSize().width();
 
   int right_inset =
-      std::max(0, kMenuWidth - (total_width + left + right + other_spacing));
+      std::max(0, menu_width - (total_width + left + right + other_spacing));
   return gfx::Insets::TLBR(0, left, 0, right_inset);
 }
 
diff --git a/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.h b/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.h
index 65de2b3..f061962 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/input_menu_view.h
@@ -41,7 +41,8 @@
  public:
   static std::unique_ptr<InputMenuView> BuildMenuView(
       DisplayOverlayController* display_overlay_controller,
-      views::View* entry_view);
+      views::View* entry_view,
+      const gfx::Size& parent_size);
 
   InputMenuView(DisplayOverlayController* display_overlay_controller,
                 views::View* entry_view);
@@ -54,7 +55,7 @@
   class FeedbackButton;
 
   void CloseMenu();
-  void Init();
+  void Init(const gfx::Size& parent_size);
   std::unique_ptr<views::View> BuildSeparator();
 
   void OnToggleGameControlPressed();
@@ -66,7 +67,8 @@
   gfx::Insets CalculateInsets(views::View* view,
                               int left,
                               int right,
-                              int other_spacing) const;
+                              int other_spacing,
+                              int menu_width) const;
   // Set |toggle| colors to spec.
   void SetCustomToggleColor(views::ToggleButton* toggle);
 
diff --git a/chrome/browser/ash/arc/keymaster/arc_keymaster_bridge.cc b/chrome/browser/ash/arc/keymaster/arc_keymaster_bridge.cc
index df7dd0c..7f120dd 100644
--- a/chrome/browser/ash/arc/keymaster/arc_keymaster_bridge.cc
+++ b/chrome/browser/ash/arc/keymaster/arc_keymaster_bridge.cc
@@ -13,7 +13,7 @@
 #include "base/memory/singleton.h"
 #include "base/process/process_handle.h"
 #include "chrome/services/keymaster/public/mojom/cert_store.mojom.h"
-#include "chromeos/dbus/arc/arc_keymaster_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_keymaster_client.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/system/invitation.h"
@@ -145,7 +145,7 @@
   keymaster_server_proxy_.set_disconnect_handler(
       base::BindOnce(&mojo::Remote<mojom::KeymasterServer>::reset,
                      base::Unretained(&keymaster_server_proxy_)));
-  chromeos::ArcKeymasterClient::Get()->BootstrapMojoConnection(
+  ash::ArcKeymasterClient::Get()->BootstrapMojoConnection(
       channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD(),
       base::BindOnce(&ArcKeymasterBridge::OnBootstrapMojoConnection,
                      weak_factory_.GetWeakPtr(), std::move(callback)));
diff --git a/chrome/browser/ash/dbus/ash_dbus_helper.cc b/chrome/browser/ash/dbus/ash_dbus_helper.cc
index 2dbd37b..58e77c0 100644
--- a/chrome/browser/ash/dbus/ash_dbus_helper.cc
+++ b/chrome/browser/ash/dbus/ash_dbus_helper.cc
@@ -17,6 +17,13 @@
 #include "chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/common/chrome_paths.h"
 #include "chromeos/ash/components/dbus/anomaly_detector/anomaly_detector_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_camera_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_keymaster_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_midis_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_obb_mounter_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_sensor_service_client.h"
 #include "chromeos/ash/components/dbus/attestation/attestation_client.h"
 #include "chromeos/ash/components/dbus/audio/cras_audio_client.h"
 #include "chromeos/ash/components/dbus/authpolicy/authpolicy_client.h"
@@ -29,6 +36,7 @@
 #include "chromeos/ash/components/dbus/cups_proxy/cups_proxy_client.h"
 #include "chromeos/ash/components/dbus/federated/federated_client.h"
 #include "chromeos/ash/components/dbus/fusebox/fusebox_reverse_client.h"
+#include "chromeos/ash/components/dbus/gnubby/gnubby_client.h"
 #include "chromeos/ash/components/dbus/hermes/hermes_clients.h"
 #include "chromeos/ash/components/dbus/human_presence/human_presence_dbus_client.h"
 #include "chromeos/ash/components/dbus/image_burner/image_burner_client.h"
@@ -62,19 +70,12 @@
 #include "chromeos/ash/components/dbus/virtual_file_provider/virtual_file_provider_client.h"
 #include "chromeos/ash/components/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h"
 #include "chromeos/ash/components/hibernate/buildflags.h"  // ENABLE_HIBERNATE
-#include "chromeos/dbus/arc/arc_appfuse_provider_client.h"
-#include "chromeos/dbus/arc/arc_camera_client.h"
-#include "chromeos/dbus/arc/arc_data_snapshotd_client.h"
-#include "chromeos/dbus/arc/arc_keymaster_client.h"
-#include "chromeos/dbus/arc/arc_midis_client.h"
-#include "chromeos/dbus/arc/arc_obb_mounter_client.h"
-#include "chromeos/dbus/arc/arc_sensor_service_client.h"
 #include "chromeos/dbus/cec_service/cec_service_client.h"
 #include "chromeos/dbus/constants/dbus_paths.h"
+#include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
 #include "chromeos/dbus/dlp/dlp_client.h"
-#include "chromeos/dbus/gnubby/gnubby_client.h"
 #include "chromeos/dbus/init/initialize_dbus_client.h"
 #include "chromeos/dbus/machine_learning/machine_learning_client.h"
 #include "chromeos/dbus/missive/missive_client.h"
@@ -128,14 +129,14 @@
   // NOTE: base::Feature is not initialized yet, so any non MultiProcessMash
   // dbus client initialization for Ash should be done in Shell::Init.
   InitializeDBusClient<AnomalyDetectorClient>(bus);
-  InitializeDBusClient<chromeos::ArcAppfuseProviderClient>(bus);
-  InitializeDBusClient<chromeos::ArcCameraClient>(bus);
-  InitializeDBusClient<chromeos::ArcDataSnapshotdClient>(bus);
-  InitializeDBusClient<chromeos::ArcKeymasterClient>(bus);
-  InitializeDBusClient<chromeos::ArcMidisClient>(bus);
-  InitializeDBusClient<chromeos::ArcObbMounterClient>(bus);
+  InitializeDBusClient<ArcAppfuseProviderClient>(bus);
+  InitializeDBusClient<ArcCameraClient>(bus);
+  InitializeDBusClient<ArcDataSnapshotdClient>(bus);
+  InitializeDBusClient<ArcKeymasterClient>(bus);
+  InitializeDBusClient<ArcMidisClient>(bus);
+  InitializeDBusClient<ArcObbMounterClient>(bus);
   InitializeDBusClient<ArcQuotaClient>(bus);
-  InitializeDBusClient<chromeos::ArcSensorServiceClient>(bus);
+  InitializeDBusClient<ArcSensorServiceClient>(bus);
   InitializeDBusClient<AttestationClient>(bus);
   InitializeDBusClient<AuthPolicyClient>(bus);
   InitializeDBusClient<BiodClient>(bus);  // For device::Fingerprint.
@@ -146,6 +147,7 @@
   // ConciergeClient depends on CiceroneClient.
   InitializeDBusClient<ConciergeClient>(bus);
   InitializeDBusClient<CrasAudioClient>(bus);
+  InitializeDBusClient<CrosDisksClient>(bus);
   InitializeDBusClient<cros_healthd::CrosHealthdClient>(bus);
   InitializeDBusClient<CryptohomeMiscClient>(bus);
   InitializeDBusClient<CryptohomePkcs11Client>(bus);
@@ -154,7 +156,7 @@
   InitializeDBusClient<chromeos::DlpClient>(bus);
   InitializeDBusClient<FederatedClient>(bus);
   InitializeDBusClient<FuseBoxReverseClient>(bus);
-  InitializeDBusClient<chromeos::GnubbyClient>(bus);
+  InitializeDBusClient<GnubbyClient>(bus);
   hermes_clients::Initialize(bus);
 #if BUILDFLAG(ENABLE_HIBERNATE)
   InitializeDBusClient<HibermanClient>(bus);
@@ -286,7 +288,7 @@
   HibermanClient::Shutdown();
 #endif
   hermes_clients::Shutdown();
-  chromeos::GnubbyClient::Shutdown();
+  GnubbyClient::Shutdown();
   FuseBoxReverseClient::Shutdown();
   FederatedClient::Shutdown();
   chromeos::DlcserviceClient::Shutdown();
@@ -295,6 +297,7 @@
   CryptohomePkcs11Client::Shutdown();
   CryptohomeMiscClient::Shutdown();
   cros_healthd::CrosHealthdClient::Shutdown();
+  CrosDisksClient::Shutdown();
   CrasAudioClient::Shutdown();
   ConciergeClient::Shutdown();
   CiceroneClient::Shutdown();
@@ -305,12 +308,12 @@
   AuthPolicyClient::Shutdown();
   AttestationClient::Shutdown();
   ArcQuotaClient::Shutdown();
-  chromeos::ArcObbMounterClient::Shutdown();
-  chromeos::ArcMidisClient::Shutdown();
-  chromeos::ArcKeymasterClient::Shutdown();
-  chromeos::ArcDataSnapshotdClient::Shutdown();
-  chromeos::ArcCameraClient::Shutdown();
-  chromeos::ArcAppfuseProviderClient::Shutdown();
+  ArcObbMounterClient::Shutdown();
+  ArcMidisClient::Shutdown();
+  ArcKeymasterClient::Shutdown();
+  ArcDataSnapshotdClient::Shutdown();
+  ArcCameraClient::Shutdown();
+  ArcAppfuseProviderClient::Shutdown();
   AnomalyDetectorClient::Shutdown();
 
   chromeos::DBusThreadManager::Shutdown();
diff --git a/chrome/browser/ash/file_manager/extract_io_task.cc b/chrome/browser/ash/file_manager/extract_io_task.cc
index e386c6d..b9ef1456 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.cc
+++ b/chrome/browser/ash/file_manager/extract_io_task.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/strcat.h"
 #include "base/task/thread_pool.h"
+#include "base/threading/platform_thread.h"
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "chrome/browser/ash/file_manager/filesystem_api_util.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
@@ -18,6 +19,7 @@
 #include "chrome/browser/platform_util.h"
 #include "components/services/unzip/content/unzip_service.h"
 #include "components/services/unzip/public/mojom/unzipper.mojom.h"
+#include "content/public/browser/browser_thread.h"
 #include "third_party/cros_system_api/constants/cryptohome.h"
 #include "third_party/zlib/google/redact.h"
 
@@ -56,7 +58,9 @@
   sizingCount_ = extractCount_ = progress_.sources.size();
 }
 
-ExtractIOTask::~ExtractIOTask() {}
+ExtractIOTask::~ExtractIOTask() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
 
 void ExtractIOTask::ZipListenerCallback(uint64_t bytes) {
   progress_.bytes_transferred += bytes;
@@ -76,6 +80,11 @@
     // Open a new window to show the extracted content.
     platform_util::ShowItemInFolder(profile_, directory);
   }
+  // Release the unpacker parameters stored for the extraction.
+  auto unpacker = unpackers_[directory];
+  if (unpacker) {
+    unpacker->CleanUp();
+  }
   DCHECK_GT(extractCount_, 0);
   if (--extractCount_ == 0) {
     progress_.state = success ? State::kSuccess : State::kError;
@@ -116,16 +125,20 @@
     base::FilePath destination_directory,
     base::FilePath source_file,
     bool created_ok) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (created_ok) {
     unzip::mojom::UnzipOptionsPtr options =
         unzip::mojom::UnzipOptions::New("auto", password_);
-    unzip::Unzip(
+    scoped_refptr<unzip::ZipFileUnpacker> unpacker =
+        base::MakeRefCounted<unzip::ZipFileUnpacker>();
+    unpacker->Unpack(
         unzip::LaunchUnzipper(), source_file, destination_directory,
         std::move(options),
         base::BindRepeating(&ExtractIOTask::ZipListenerCallback,
                             weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&ExtractIOTask::ZipExtractCallback,
                        weak_ptr_factory_.GetWeakPtr(), destination_directory));
+    unpackers_.insert({destination_directory, std::move(unpacker)});
   } else {
     LOG(ERROR) << "Cannot create directory "
                << zip::Redact(destination_directory);
@@ -266,7 +279,16 @@
 void ExtractIOTask::Cancel() {
   progress_.state = State::kCancelled;
   RecordUmaExtractStatus(ExtractStatus::kCancelled);
-  // Any inflight operation will be cancelled when the task is destroyed.
+  // Run through all existing extraction instances and cancel them all.
+  for (auto unpacker : unpackers_) {
+    if (unpacker.second) {
+      unpacker.second->Stop();
+      while (!unpacker.second->CancelDone()) {
+        // Yield until the cancellation tasks are done.
+        base::PlatformThread::Sleep(base::Microseconds(1));
+      }
+    }
+  }
 }
 
 // Calls the completion callback for the task. |progress_| should not be
diff --git a/chrome/browser/ash/file_manager/extract_io_task.h b/chrome/browser/ash/file_manager/extract_io_task.h
index 56cca7cc..275d6377 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.h
+++ b/chrome/browser/ash/file_manager/extract_io_task.h
@@ -110,6 +110,9 @@
   // Counter of the number of archives needing extraction.
   size_t extractCount_;
 
+  // Reference to the unpacker service instances.
+  std::map<base::FilePath, scoped_refptr<unzip::ZipFileUnpacker>> unpackers_;
+
   base::WeakPtrFactory<ExtractIOTask> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index 4f32cd3..234bb05 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -1805,6 +1805,20 @@
             .EnableFiltersInRecents()
             .EnableFiltersInRecentsV2()
             .FilesSwa(),
+        TestCase("recentsEmptyFolderMessage")
+            .EnableFiltersInRecents()
+            .EnableFiltersInRecentsV2(),
+        TestCase("recentsEmptyFolderMessage")
+            .EnableFiltersInRecents()
+            .EnableFiltersInRecentsV2()
+            .FilesSwa(),
+        TestCase("recentsEmptyFolderMessageAfterDeletion")
+            .EnableFiltersInRecents()
+            .EnableFiltersInRecentsV2(),
+        TestCase("recentsEmptyFolderMessageAfterDeletion")
+            .EnableFiltersInRecents()
+            .EnableFiltersInRecentsV2()
+            .FilesSwa(),
         TestCase("recentsDownloads"),
         TestCase("recentsDownloads").FilesSwa(),
         TestCase("recentsDownloads").EnableFiltersInRecents(),
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
index 2dbf2a69..46f4376 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
@@ -106,8 +106,8 @@
 #include "chrome/test/base/test_switches.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_service.pb.h"
 #include "chromeos/dbus/constants/dbus_switches.h"
+#include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/dbus/cros_disks/fake_cros_disks_client.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/drive/drive_pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
@@ -2055,15 +2055,13 @@
         crostini::ContainerInfo(crostini::kCrostiniDefaultContainerName,
                                 "testuser", "/home/testuser",
                                 "PLACEHOLDER_IP"));
-    chromeos::DBusThreadManager* dbus_thread_manager =
-        chromeos::DBusThreadManager::Get();
     static_cast<chromeos::FakeCrosDisksClient*>(
-        dbus_thread_manager->GetCrosDisksClient())
+        chromeos::CrosDisksClient::Get())
         ->AddCustomMountPointCallback(
             base::BindRepeating(&FileManagerBrowserTestBase::MaybeMountCrostini,
                                 base::Unretained(this)));
     static_cast<chromeos::FakeCrosDisksClient*>(
-        dbus_thread_manager->GetCrosDisksClient())
+        chromeos::CrosDisksClient::Get())
         ->AddCustomMountPointCallback(
             base::BindRepeating(&FileManagerBrowserTestBase::MaybeMountGuestOs,
                                 base::Unretained(this)));
@@ -3164,10 +3162,8 @@
   }
 
   if (name == "blockMounts") {
-    chromeos::DBusThreadManager* dbus_thread_manager =
-        chromeos::DBusThreadManager::Get();
     static_cast<chromeos::FakeCrosDisksClient*>(
-        dbus_thread_manager->GetCrosDisksClient())
+        chromeos::CrosDisksClient::Get())
         ->BlockMount();
     return;
   }
diff --git a/chrome/browser/ash/file_manager/file_tasks.cc b/chrome/browser/ash/file_manager/file_tasks.cc
index ed34958..7ffa07c 100644
--- a/chrome/browser/ash/file_manager/file_tasks.cc
+++ b/chrome/browser/ash/file_manager/file_tasks.cc
@@ -49,6 +49,7 @@
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
+#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h"
 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/common/chrome_features.h"
@@ -583,7 +584,7 @@
   if (task.task_type == TASK_TYPE_ARC_APP &&
       !ash::features::ShouldArcAndGuestOsFileTasksUseAppService()) {
     apps::RecordAppLaunchMetrics(profile, apps::AppType::kArc, task.app_id,
-                                 apps::mojom::LaunchSource::kFromFileManager,
+                                 apps::LaunchSource::kFromFileManager,
                                  apps::LaunchContainer::kLaunchContainerWindow);
     ExecuteArcTask(profile, task, file_urls, *mime_types, std::move(done));
   } else {
@@ -889,8 +890,7 @@
 
   // TODO(crbug.com/1005640): Move recording this metric to the App Service when
   // file handling is supported there.
-  apps::RecordAppLaunch(task.app_id,
-                        apps::mojom::LaunchSource::kFromFileManager);
+  apps::RecordAppLaunch(task.app_id, apps::LaunchSource::kFromFileManager);
 
   if (auto* notifier = FileTasksNotifier::GetForProfile(profile)) {
     notifier->NotifyFileTasks(file_urls);
@@ -901,6 +901,12 @@
   // this will always open on the current desktop, regardless of which profile
   // owns the files, so return TASK_RESULT_OPENED.
   const std::string parsed_action_id(ParseFilesAppActionId(task.action_id));
+
+  if (IsFilesAppId(task.app_id) &&
+      (parsed_action_id == "upload-office-to-drive")) {
+    return chromeos::cloud_upload::CloudUploadDialog::Show();
+  }
+
   if (ShouldBeOpenedWithBrowser(task.app_id, parsed_action_id)) {
     const bool result =
         OpenFilesWithBrowser(profile, file_urls, parsed_action_id);
diff --git a/chrome/browser/ash/file_manager/guest_os_file_tasks.cc b/chrome/browser/ash/file_manager/guest_os_file_tasks.cc
index ac6d8828..c9a0562 100644
--- a/chrome/browser/ash/file_manager/guest_os_file_tasks.cc
+++ b/chrome/browser/ash/file_manager/guest_os_file_tasks.cc
@@ -283,7 +283,7 @@
     case guest_os::VmType::TERMINA:
       apps::RecordAppLaunchMetrics(
           profile, apps::AppType::kCrostini, task.app_id,
-          apps::mojom::LaunchSource::kFromFileManager,
+          apps::LaunchSource::kFromFileManager,
           apps::LaunchContainer::kLaunchContainerWindow);
       crostini::LaunchCrostiniApp(
           profile, task.app_id, display::kInvalidDisplayId, args,
@@ -307,7 +307,7 @@
     case guest_os::VmType::PLUGIN_VM:
       apps::RecordAppLaunchMetrics(
           profile, apps::AppType::kPluginVm, task.app_id,
-          apps::mojom::LaunchSource::kFromFileManager,
+          apps::LaunchSource::kFromFileManager,
           apps::LaunchContainer::kLaunchContainerWindow);
       DCHECK(plugin_vm::PluginVmFeatures::Get()->IsEnabled(profile));
       plugin_vm::LaunchPluginVmApp(
diff --git a/chrome/browser/ash/file_manager/trash_unittest_base.cc b/chrome/browser/ash/file_manager/trash_unittest_base.cc
index 4044541..2142cb37 100644
--- a/chrome/browser/ash/file_manager/trash_unittest_base.cc
+++ b/chrome/browser/ash/file_manager/trash_unittest_base.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chromeos/ash/components/dbus/chunneld/chunneld_client.h"
 #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h"
+#include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "storage/browser/file_system/external_mount_points.h"
 #include "storage/browser/test/test_file_system_context.h"
@@ -65,6 +66,7 @@
   ash::ChunneldClient::InitializeFake();
   ash::CiceroneClient::InitializeFake();
   ash::ConciergeClient::InitializeFake();
+  chromeos::CrosDisksClient::InitializeFake();
   ash::SeneschalClient::InitializeFake();
 
   // Ensure Crostini is setup correctly.
@@ -98,6 +100,7 @@
   scoped_user_manager_.reset();
   profile_.reset();
   ash::SeneschalClient::Shutdown();
+  chromeos::CrosDisksClient::Shutdown();
   ash::ConciergeClient::Shutdown();
   ash::CiceroneClient::Shutdown();
   ash::ChunneldClient::Shutdown();
diff --git a/chrome/browser/ash/file_manager/volume_manager.cc b/chrome/browser/ash/file_manager/volume_manager.cc
index 2a6ffb49..f0b00604 100644
--- a/chrome/browser/ash/file_manager/volume_manager.cc
+++ b/chrome/browser/ash/file_manager/volume_manager.cc
@@ -345,8 +345,10 @@
     const ash::file_system_provider::ProvidedFileSystemInfo& file_system_info,
     MountContext mount_context) {
   std::unique_ptr<Volume> volume(new Volume());
+
   volume->file_system_id_ = file_system_info.file_system_id();
   volume->provider_id_ = file_system_info.provider_id();
+
   switch (file_system_info.source()) {
     case extensions::SOURCE_FILE:
       volume->source_ = SOURCE_FILE;
@@ -358,17 +360,61 @@
       volume->source_ = SOURCE_NETWORK;
       break;
   }
+
   volume->volume_label_ = file_system_info.display_name();
   volume->type_ = VOLUME_TYPE_PROVIDED;
   volume->mount_path_ = file_system_info.mount_path();
   volume->mount_condition_ = ash::disks::MOUNT_CONDITION_NONE;
   volume->mount_context_ = mount_context;
+
   volume->is_parent_ = true;
   volume->is_read_only_ = !file_system_info.writable();
   volume->configurable_ = file_system_info.configurable();
   volume->watchable_ = file_system_info.watchable();
-  volume->volume_id_ = GenerateVolumeId(*volume);
   volume->icon_set_ = file_system_info.icon_set();
+
+  volume->volume_id_ = GenerateVolumeId(*volume);
+  return volume;
+}
+
+// static: |mount_path| is the fusebox daemon AttachStorage API 'subdir'.
+std::unique_ptr<Volume> Volume::CreateForFuseBoxProvidedFileSystem(
+    const base::FilePath& mount_path,
+    const ash::file_system_provider::ProvidedFileSystemInfo& file_system_info,
+    MountContext mount_context) {
+  const base::FilePath mount_point(util::kFuseBoxMediaPath);
+
+  std::unique_ptr<Volume> volume(new Volume());
+
+  switch (file_system_info.source()) {
+    case extensions::SOURCE_FILE:
+      volume->source_ = SOURCE_FILE;
+      break;
+    case extensions::SOURCE_DEVICE:
+      volume->source_ = SOURCE_DEVICE;
+      break;
+    case extensions::SOURCE_NETWORK:
+      volume->source_ = SOURCE_NETWORK;
+      break;
+  }
+
+  volume->volume_label_ = file_system_info.display_name();
+  if (ash::features::IsFileManagerFuseBoxDebugEnabled())
+    volume->volume_label_.insert(0, "fusebox ");
+
+  volume->type_ = VOLUME_TYPE_PROVIDED;
+  volume->file_system_type_ = util::kFuseBox;
+  volume->mount_path_ = mount_point.Append(mount_path);
+  volume->mount_condition_ = ash::disks::MOUNT_CONDITION_NONE;
+  volume->mount_context_ = mount_context;
+
+  volume->is_parent_ = true;
+  volume->is_read_only_ = !file_system_info.writable();
+  volume->icon_set_ = file_system_info.icon_set();
+
+  // "fusebox" prefix the original FSP volume id.
+  volume->volume_id_ = util::kFuseBox;
+  volume->volume_id_.append(GenerateVolumeId(*volume));
   return volume;
 }
 
diff --git a/chrome/browser/ash/file_manager/volume_manager.h b/chrome/browser/ash/file_manager/volume_manager.h
index ec386db..181f92a5 100644
--- a/chrome/browser/ash/file_manager/volume_manager.h
+++ b/chrome/browser/ash/file_manager/volume_manager.h
@@ -111,6 +111,10 @@
   static std::unique_ptr<Volume> CreateForProvidedFileSystem(
       const ash::file_system_provider::ProvidedFileSystemInfo& file_system_info,
       MountContext mount_context);
+  static std::unique_ptr<Volume> CreateForFuseBoxProvidedFileSystem(
+      const base::FilePath& mount_path,
+      const ash::file_system_provider::ProvidedFileSystemInfo& file_system_info,
+      MountContext mount_context);
   static std::unique_ptr<Volume> CreateForMTP(const base::FilePath& mount_path,
                                               const std::string& label,
                                               bool read_only);
diff --git a/chrome/browser/ash/login/demo_mode/demo_session.cc b/chrome/browser/ash/login/demo_mode/demo_session.cc
index 19851d2..6941fc18 100644
--- a/chrome/browser/ash/login/demo_mode/demo_session.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_session.cc
@@ -650,7 +650,7 @@
       apps::AppLaunchParams(update.AppId(),
                             apps::LaunchContainer::kLaunchContainerWindow,
                             WindowOpenDisposition::NEW_WINDOW,
-                            apps::mojom::LaunchSource::kFromChromeInternal));
+                            apps::LaunchSource::kFromChromeInternal));
 }
 
 void DemoSession::OnAppRegistryCacheWillBeDestroyed(
diff --git a/chrome/browser/ash/notifications/gnubby_notification.cc b/chrome/browser/ash/notifications/gnubby_notification.cc
index 8ccceda0..1399220 100644
--- a/chrome/browser/ash/notifications/gnubby_notification.cc
+++ b/chrome/browser/ash/notifications/gnubby_notification.cc
@@ -11,13 +11,11 @@
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/notifications/system_notification_helper.h"
 #include "chrome/grit/generated_resources.h"
-#include "chromeos/dbus/gnubby/gnubby_client.h"
+#include "chromeos/ash/components/dbus/gnubby/gnubby_client.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_delegate.h"
 
-using chromeos::GnubbyClient;
-
 namespace {
 constexpr base::TimeDelta kNotificationTimeout = base::Seconds(2);
 }  // namespace
diff --git a/chrome/browser/ash/notifications/gnubby_notification.h b/chrome/browser/ash/notifications/gnubby_notification.h
index 08c46cb..81095fa0 100644
--- a/chrome/browser/ash/notifications/gnubby_notification.h
+++ b/chrome/browser/ash/notifications/gnubby_notification.h
@@ -9,7 +9,7 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/notifications/system_notification_helper.h"
-#include "chromeos/dbus/gnubby/gnubby_client.h"
+#include "chromeos/ash/components/dbus/gnubby/gnubby_client.h"
 
 namespace message_center {
 class Notification;
@@ -20,7 +20,7 @@
 // U2FD Authentication.  It is responsible for both creating, showing, and
 // closing the notification.
 
-class GnubbyNotification : public chromeos::GnubbyClient::Observer {
+class GnubbyNotification : public GnubbyClient::Observer {
  public:
   GnubbyNotification();
 
diff --git a/chrome/browser/ash/notifications/gnubby_notification_unittest.cc b/chrome/browser/ash/notifications/gnubby_notification_unittest.cc
index fac71d8..3d07a08 100644
--- a/chrome/browser/ash/notifications/gnubby_notification_unittest.cc
+++ b/chrome/browser/ash/notifications/gnubby_notification_unittest.cc
@@ -11,13 +11,11 @@
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
+#include "chromeos/ash/components/dbus/gnubby/gnubby_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/gnubby/gnubby_client.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 
-using chromeos::GnubbyClient;
-
 namespace ash {
 
 class GnubbyNotificationTest : public BrowserWithTestWindowTest {
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager.cc
index 13c5fee..fbdbf58 100644
--- a/chrome/browser/ash/system_web_apps/system_web_app_manager.cc
+++ b/chrome/browser/ash/system_web_apps/system_web_app_manager.cc
@@ -14,6 +14,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/containers/contains.h"
+#include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/one_shot_event.h"
 #include "base/run_loop.h"
@@ -40,7 +41,6 @@
 #include "chrome/browser/web_applications/web_app_system_web_app_delegate_map_utils.h"
 #include "chrome/browser/web_applications/web_app_ui_manager.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
@@ -149,7 +149,7 @@
   SystemWebAppDelegateMap delegate_map;
   for (auto& info : info_vec) {
     if (info->IsAppEnabled() ||
-        base::FeatureList::IsEnabled(::features::kEnableAllSystemWebApps)) {
+        base::FeatureList::IsEnabled(features::kEnableAllSystemWebApps)) {
       // Gets `type` before std::move().
       SystemWebAppType type = info->GetType();
       delegate_map.emplace(type, std::move(info));
@@ -224,7 +224,7 @@
     update_policy_ = UpdatePolicy::kAlwaysUpdate;
 
     // Populate with real system apps if the test asks for it.
-    if (base::FeatureList::IsEnabled(::features::kEnableAllSystemWebApps))
+    if (base::FeatureList::IsEnabled(features::kEnableAllSystemWebApps))
       system_app_delegates_ = CreateSystemWebApps(profile_);
 
     return;
@@ -305,7 +305,15 @@
 }
 
 bool SystemWebAppManager::IsAppEnabled(SystemWebAppType type) const {
-  return IsSystemWebAppEnabled(system_app_delegates_, type);
+  if (base::FeatureList::IsEnabled(features::kEnableAllSystemWebApps))
+    return true;
+
+  const SystemWebAppDelegate* delegate =
+      GetSystemWebApp(system_app_delegates_, type);
+  if (!delegate)
+    return false;
+
+  return delegate->IsAppEnabled();
 }
 
 void SystemWebAppManager::SetSubsystems(
@@ -682,7 +690,7 @@
 }
 
 bool SystemWebAppManager::ShouldForceInstallApps() const {
-  if (base::FeatureList::IsEnabled(::features::kAlwaysReinstallSystemWebApps))
+  if (base::FeatureList::IsEnabled(features::kAlwaysReinstallSystemWebApps))
     return true;
 
   if (update_policy_ == UpdatePolicy::kAlwaysUpdate)
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc
index 698919a..558cbc9 100644
--- a/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc
@@ -7,6 +7,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -46,6 +47,7 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/permissions/permission_util.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/app_update.h"
@@ -269,7 +271,7 @@
   content::WebContents* LaunchApp(std::vector<base::FilePath> launch_files,
                                   bool wait_for_load = true) {
     apps::AppLaunchParams params = LaunchParamsForApp(GetMockAppType());
-    params.launch_source = apps::mojom::LaunchSource::kFromChromeInternal;
+    params.launch_source = apps::LaunchSource::kFromChromeInternal;
     params.override_url = maybe_installation_->GetAppUrl();
     params.launch_files = std::move(launch_files);
 
@@ -279,7 +281,7 @@
   content::WebContents* LaunchAppWithoutWaiting(
       std::vector<base::FilePath> launch_files) {
     apps::AppLaunchParams params = LaunchParamsForApp(GetMockAppType());
-    params.launch_source = apps::mojom::LaunchSource::kFromChromeInternal;
+    params.launch_source = apps::LaunchSource::kFromChromeInternal;
     params.override_url = maybe_installation_->GetAppUrl();
     params.launch_files = std::move(launch_files);
 
@@ -846,7 +848,7 @@
 
     // Launch the App.
     apps::AppLaunchParams params = LaunchParamsForApp(GetMockAppType());
-    params.launch_source = apps::mojom::LaunchSource::kFromChromeInternal;
+    params.launch_source = apps::LaunchSource::kFromChromeInternal;
     params.launch_files = {temp_file_path};
     params.override_url = GetStartUrl();
 
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
index 1e1bae8..2730126f 100644
--- a/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
+++ b/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/feature_list.h"
@@ -352,16 +353,16 @@
     bool enable_migration = GetParam();
     if (enable_migration) {
       scoped_feature_list_.InitWithFeatures(
-          {features::kUseWebAppDBInsteadOfExternalPrefs}, {});
+          {::features::kUseWebAppDBInsteadOfExternalPrefs}, {});
     } else {
       scoped_feature_list_.InitWithFeatures(
-          {}, {features::kUseWebAppDBInsteadOfExternalPrefs});
+          {}, {::features::kUseWebAppDBInsteadOfExternalPrefs});
     }
   }
 
   bool IsExternalDataReadFromDBEnabled() {
     return base::FeatureList::IsEnabled(
-        features::kUseWebAppDBInsteadOfExternalPrefs);
+        ::features::kUseWebAppDBInsteadOfExternalPrefs);
   }
 
  private:
diff --git a/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.cc b/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.cc
index 818535cf..10f5815 100644
--- a/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.cc
+++ b/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.cc
@@ -66,10 +66,9 @@
       GetManager().GetAppIdForSystemApp(system_app_type);
 
   CHECK(app_id.has_value());
-  return apps::AppLaunchParams(*app_id,
-                               apps::LaunchContainer::kLaunchContainerWindow,
-                               WindowOpenDisposition::CURRENT_TAB,
-                               apps::mojom::LaunchSource::kFromAppListGrid);
+  return apps::AppLaunchParams(
+      *app_id, apps::LaunchContainer::kLaunchContainerWindow,
+      WindowOpenDisposition::CURRENT_TAB, apps::LaunchSource::kFromAppListGrid);
 }
 
 content::WebContents* SystemWebAppBrowserTestBase::LaunchApp(
diff --git a/chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.cc b/chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.cc
index 4c0f83c0..6c2a771 100644
--- a/chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.cc
+++ b/chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.cc
@@ -4,23 +4,8 @@
 
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.h"
 
-#include "base/feature_list.h"
-#include "chrome/common/chrome_features.h"
-
 namespace ash {
 
-bool IsSystemWebAppEnabled(const SystemWebAppDelegateMap& delegates,
-                           SystemWebAppType type) {
-  if (base::FeatureList::IsEnabled(features::kEnableAllSystemWebApps))
-    return true;
-
-  const SystemWebAppDelegate* delegate = GetSystemWebApp(delegates, type);
-  if (!delegate)
-    return false;
-
-  return delegate->IsAppEnabled();
-}
-
 const SystemWebAppDelegate* GetSystemWebApp(
     const SystemWebAppDelegateMap& delegates,
     SystemWebAppType type) {
diff --git a/chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.h b/chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.h
index 6ac7bbf7..8efb625 100644
--- a/chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.h
+++ b/chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.h
@@ -16,10 +16,6 @@
 using SystemWebAppDelegateMap =
     base::flat_map<SystemWebAppType, std::unique_ptr<SystemWebAppDelegate>>;
 
-// Returns whether the given app type is enabled.
-bool IsSystemWebAppEnabled(const SystemWebAppDelegateMap& delegates,
-                           SystemWebAppType type);
-
 // Returns the System App Delegate for the given App |type|.
 const SystemWebAppDelegate* GetSystemWebApp(
     const SystemWebAppDelegateMap& delegates,
diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc
index e06bfc4..89d912b 100644
--- a/chrome/browser/background/background_mode_manager.cc
+++ b/chrome/browser/background/background_mode_manager.cc
@@ -441,7 +441,7 @@
       ->BrowserAppLauncher()
       ->LaunchAppWithParams(CreateAppLaunchParamsUserContainer(
           profile, extension, WindowOpenDisposition::NEW_FOREGROUND_TAB,
-          apps::mojom::LaunchSource::kFromBackgroundMode));
+          apps::LaunchSource::kFromBackgroundMode));
 #else
   // background mode is not used in Chrome OS platform.
   // TODO(crbug.com/1291803): Remove the background mode manager from Chrome OS
diff --git a/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc b/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc
index 5d4ee0a2..459f988e 100644
--- a/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc
+++ b/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc
@@ -81,6 +81,7 @@
 #endif
 }
 
+// TODO(crbug.com/1309721): Plumb the |pin| from WebBluetoothPairingManagerImpl.
 void ChromeBluetoothDelegateImplClient::ShowBluetoothDevicePairDialog(
     content::RenderFrameHost* frame,
     const std::u16string& device_identifier,
@@ -97,7 +98,12 @@
     case (content::BluetoothDelegate::PairingKind::kConfirmOnly):
       chrome::ShowBluetoothDevicePairConfirmDialog(
           content::WebContents::FromRenderFrameHost(frame), device_identifier,
-          std::move(callback));
+          absl::nullopt, std::move(callback));
+      break;
+    case (content::BluetoothDelegate::PairingKind::kConfirmPinMatch):
+      chrome::ShowBluetoothDevicePairConfirmDialog(
+          content::WebContents::FromRenderFrameHost(frame), device_identifier,
+          u"123456", std::move(callback));
       break;
     default:
       NOTREACHED();
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 3ffbf61..15bda91 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -6330,20 +6330,6 @@
                                      &replacement_data);
 }
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-bool ChromeContentBrowserClient::ShouldAllowPluginCreation(
-    const url::Origin& embedder_origin,
-    const content::PepperPluginInfo& plugin_info) {
-#if BUILDFLAG(ENABLE_PDF)
-  if (plugin_info.name == ChromeContentClient::kPDFInternalPluginName) {
-    return IsPdfInternalPluginAllowedOrigin(embedder_origin);
-  }
-#endif  // BUILDFLAG(ENABLE_PDF)
-
-  return true;
-}
-#endif  // BUILDFLAG(ENABLE_PLUGINS)
-
 #if BUILDFLAG(ENABLE_VR)
 content::XrIntegrationClient*
 ChromeContentBrowserClient::GetXrIntegrationClient() {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 51ab032e..00a4d7e9 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -33,7 +33,6 @@
 #include "media/media_buildflags.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "ppapi/buildflags/buildflags.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 class ChromeContentBrowserClientParts;
@@ -738,12 +737,6 @@
                               size_t data_size_in_bytes,
                               std::u16string& replacement_data) override;
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-  bool ShouldAllowPluginCreation(
-      const url::Origin& embedder_origin,
-      const content::PepperPluginInfo& plugin_info) override;
-#endif
-
 #if BUILDFLAG(ENABLE_VR)
   content::XrIntegrationClient* GetXrIntegrationClient() override;
 #endif
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index af6877a..f1e9e9ee 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -265,6 +265,7 @@
     "//chromeos/ash/components/dbus/federated",
     "//chromeos/ash/components/dbus/fusebox",
     "//chromeos/ash/components/dbus/fusebox:proto",
+    "//chromeos/ash/components/dbus/gnubby",
     "//chromeos/ash/components/dbus/hermes",
     "//chromeos/ash/components/dbus/human_presence",
     "//chromeos/ash/components/dbus/image_loader",
@@ -344,7 +345,6 @@
     "//chromeos/dbus/dlp:dlp_proto",
     "//chromeos/dbus/easy_unlock",
     "//chromeos/dbus/fwupd",
-    "//chromeos/dbus/gnubby",
     "//chromeos/dbus/machine_learning",
     "//chromeos/dbus/missive",
     "//chromeos/dbus/permission_broker",
@@ -3639,6 +3639,7 @@
     "//chromeos/ash/components/dbus/attestation",
     "//chromeos/ash/components/dbus/attestation:attestation_proto",
     "//chromeos/ash/components/dbus/authpolicy",
+    "//chromeos/ash/components/dbus/gnubby",
     "//chromeos/ash/components/dbus/lorgnette_manager:lorgnette_proto",
     "//chromeos/ash/components/dbus/oobe_config",
     "//chromeos/ash/components/dbus/services:test_support",
@@ -3661,7 +3662,6 @@
     "//chromeos/dbus/cryptohome:attestation_proto",
     "//chromeos/dbus/dlcservice",
     "//chromeos/dbus/dlp",
-    "//chromeos/dbus/gnubby",
     "//chromeos/dbus/missive",
     "//chromeos/dbus/power",
     "//chromeos/ime:gencode",
diff --git a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc
index 725a3d39..6526eb4 100644
--- a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc
@@ -92,11 +92,11 @@
   SYSLOG(INFO) << "Attempt to launch app.";
 
   // Always open the app in a window.
-  ::OpenApplication(profile_, apps::AppLaunchParams(
-                                  extension->id(),
-                                  apps::LaunchContainer::kLaunchContainerWindow,
-                                  WindowOpenDisposition::NEW_WINDOW,
-                                  apps::mojom::LaunchSource::kFromKiosk));
+  ::OpenApplication(
+      profile_,
+      apps::AppLaunchParams(
+          extension->id(), apps::LaunchContainer::kLaunchContainerWindow,
+          WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromKiosk));
 
   WaitForAppWindow();
 }
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
index 688088c..0e228fe 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
@@ -281,7 +281,6 @@
   // If the UI is enabled, create the modal dialog.
   if (show_ui) {
     ContentAnalysisDelegate* delegate_ptr = delegate.get();
-
     int files_count = delegate_ptr->data_.paths.size();
 
     // This dialog is owned by the constrained_window code.
@@ -399,6 +398,22 @@
   return files_request_handler_.get();
 }
 
+bool ContentAnalysisDelegate::ShowFinalResultInDialog() {
+  if (!dialog_)
+    return false;
+
+  dialog_->ShowResult(final_result_);
+  return true;
+}
+
+bool ContentAnalysisDelegate::CancelDialog() {
+  if (!dialog_)
+    return false;
+
+  dialog_->CancelDialog();
+  return true;
+}
+
 void ContentAnalysisDelegate::PageRequestCallback(
     BinaryUploadService::Result result,
     enterprise_connectors::ContentAnalysisResponse response) {
@@ -553,11 +568,11 @@
 }
 
 bool ContentAnalysisDelegate::UpdateDialog() {
-  if (!dialog_)
-    return false;
-
-  dialog_->ShowResult(final_result_);
-  return true;
+  // Only show final result UI in the case of a cloud analysis.
+  // In the local case, the local agent does that.
+  return data_.settings.cloud_or_local_settings.is_cloud_analysis()
+             ? ShowFinalResultInDialog()
+             : CancelDialog();
 }
 
 void ContentAnalysisDelegate::MaybeCompleteScanRequest() {
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
index def25c63..e8ac0d3 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
@@ -212,6 +212,13 @@
 
   FilesRequestHandler* GetFilesRequestHandlerForTesting();
 
+  const Data& GetDataForTesting() { return data_; }
+
+  // Methods to either show the final result in the analysis dialog and to
+  // cancel the dialog.  These methods are protected and virtual for testing.
+  virtual bool ShowFinalResultInDialog();
+  virtual bool CancelDialog();
+
  private:
   // Uploads data for deep scanning.  Returns true if uploading is occurring in
   // the background and false if there is nothing to do. Sets `data_uploaded_`
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_unittest.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_unittest.cc
index 83b3be9..e363dc9b 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_unittest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_unittest.cc
@@ -89,6 +89,18 @@
 
 constexpr char kNothingEnabled[] = R"({ "service_provider": "google" })";
 
+constexpr char kLocalBlockingScansForDlpAndMalware[] = R"(
+{
+  "service_provider": "local_test",
+  "enable": [
+    {
+      "url_list": ["*"],
+      "tags": ["dlp", "malware"]
+    }
+  ],
+  "block_until_verdict": 1
+})";
+
 // Helpers to get text with sizes relative to the minimum required size of 100
 // bytes for scans to trigger.
 std::string large_text() {
@@ -1601,6 +1613,9 @@
   EXPECT_TRUE(called);
 }
 
+// test params:
+// 0: upload result from binary upload service.
+// 1: whether an cloud analysis is done.
 class ContentAnalysisDelegateResultHandlingTest
     : public BaseTest,
       public testing::WithParamInterface<
@@ -1611,8 +1626,10 @@
   void SetUp() override {
     BaseTest::SetUp();
     EnableFeatures();
-    safe_browsing::SetAnalysisConnector(profile_->GetPrefs(), FILE_ATTACHED,
-                                        kBlockingScansForDlpAndMalware);
+    safe_browsing::SetAnalysisConnector(
+        profile_->GetPrefs(), FILE_ATTACHED,
+        is_cloud() ? kBlockingScansForDlpAndMalware
+                   : kLocalBlockingScansForDlpAndMalware);
 
     ContentAnalysisDelegate::SetFactoryForTesting(base::BindRepeating(
         &FakeContentAnalysisDelegate::Create, run_loop_.QuitClosure(),
@@ -1620,12 +1637,15 @@
             &ContentAnalysisDelegateResultHandlingTest::ConnectorStatusCallback,
             base::Unretained(this)),
         kDmToken));
+    FakeContentAnalysisDelegate::ResetDialogFlags();
   }
 
   safe_browsing::BinaryUploadService::Result result() const {
     return std::get<0>(GetParam());
   }
 
+  bool is_cloud() const { return std::get<1>(GetParam()); }
+
   ContentAnalysisResponse ConnectorStatusCallback(const base::FilePath& path) {
     return FakeContentAnalysisDelegate::SuccessfulResponse({"dlp", "malware"});
   }
@@ -1662,6 +1682,9 @@
           }));
   RunUntilDone();
   EXPECT_TRUE(called);
+
+  EXPECT_EQ(is_cloud(), FakeContentAnalysisDelegate::WasDialogShown());
+  EXPECT_NE(is_cloud(), FakeContentAnalysisDelegate::WasDialogCanceled());
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
index 7c0b5df..6e9b7fd6 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
@@ -243,15 +243,6 @@
 
   constrained_window::ShowWebModalDialogViews(this, web_contents_);
 
-  // We need to UpdateDialog() right away if the dialog already has a result,
-  // because the dialog can only call UpdateDialog() after the dialog widget
-  // has been initialized by ShowWebModalDialogViews().
-  // We can't move it any earlier or it will crash when we try to get the
-  // color from the widget. (see b/232104687)
-  // if (!is_pending())
-  //  UpdateDialog();
-
-  // TODO(crbug.com/1337078): Remove if this doesn't fix the CQ
   if (is_warning() && bypass_requires_justification()) {
     bypass_justification_text_length_->SetEnabledColor(
         bypass_justification_text_length_->GetColorProvider()->GetColor(
@@ -381,7 +372,6 @@
     message_->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
     message_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
-    // TODO(crbug.com/1337078): Remove if this doesn't fix the CQ
     if (!is_pending())
       UpdateDialog();
   }
@@ -572,7 +562,6 @@
 }
 
 void ContentAnalysisDialog::SetupButtons() {
-  // TODO(domfc): Add "Learn more" button on scan failure.
   if (is_warning()) {
     // Include the Ok and Cancel buttons if there is a bypassable warning.
     DialogDelegate::SetButtons(ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK);
diff --git a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_delegate.cc
index 88b37a0..7b9060a 100644
--- a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_delegate.cc
+++ b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_delegate.cc
@@ -25,6 +25,8 @@
 safe_browsing::BinaryUploadService::Result
     FakeContentAnalysisDelegate::result_ =
         safe_browsing::BinaryUploadService::Result::SUCCESS;
+bool FakeContentAnalysisDelegate::dialog_shown_ = false;
+bool FakeContentAnalysisDelegate::dialog_canceled_ = false;
 
 FakeContentAnalysisDelegate::FakeContentAnalysisDelegate(
     base::RepeatingClosure delete_closure,
@@ -53,6 +55,22 @@
 }
 
 // static
+void FakeContentAnalysisDelegate::ResetDialogFlags() {
+  dialog_shown_ = false;
+  dialog_canceled_ = false;
+}
+
+// static
+bool FakeContentAnalysisDelegate::WasDialogShown() {
+  return dialog_shown_;
+}
+
+// static
+bool FakeContentAnalysisDelegate::WasDialogCanceled() {
+  return dialog_canceled_;
+}
+
+// static
 std::unique_ptr<ContentAnalysisDelegate> FakeContentAnalysisDelegate::Create(
     base::RepeatingClosure delete_closure,
     StatusCallback status_callback,
@@ -203,7 +221,10 @@
     const base::FilePath& path,
     std::unique_ptr<safe_browsing::BinaryUploadService::Request> request) {
   DCHECK(!path.empty());
-  DCHECK_EQ(dm_token_, request->device_token());
+  if (GetDataForTesting()
+          .settings.cloud_or_local_settings.is_cloud_analysis()) {
+    DCHECK_EQ(dm_token_, request->device_token());
+  }
 
   // Simulate a response.
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
@@ -226,4 +247,14 @@
       response_delay);
 }
 
+bool FakeContentAnalysisDelegate::ShowFinalResultInDialog() {
+  dialog_shown_ = true;
+  return ContentAnalysisDelegate::ShowFinalResultInDialog();
+}
+
+bool FakeContentAnalysisDelegate::CancelDialog() {
+  dialog_canceled_ = true;
+  return ContentAnalysisDelegate::CancelDialog();
+}
+
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_delegate.h b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_delegate.h
index 369942b2..a5585218 100644
--- a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_delegate.h
+++ b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_delegate.h
@@ -82,6 +82,10 @@
   static void SetResponseResult(
       safe_browsing::BinaryUploadService::Result result);
 
+  static void ResetDialogFlags();
+  static bool WasDialogShown();
+  static bool WasDialogCanceled();
+
  private:
   // Simulates a response from the binary upload service.  the |path| argument
   // is used to call |status_callback_| to determine if the path should succeed
@@ -97,6 +101,8 @@
   void UploadPageForDeepScanning(
       std::unique_ptr<safe_browsing::BinaryUploadService::Request> request)
       override;
+  bool ShowFinalResultInDialog() override;
+  bool CancelDialog() override;
 
   // Fake upload callback for deep scanning. Virtual to be overridden by other
   // fakes.
@@ -106,6 +112,9 @@
       std::unique_ptr<safe_browsing::BinaryUploadService::Request> request);
 
   static safe_browsing::BinaryUploadService::Result result_;
+  static bool dialog_shown_;
+  static bool dialog_canceled_;
+
   base::RepeatingClosure delete_closure_;
   StatusCallback status_callback_;
   std::string dm_token_;
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index 3599c6d9..92e2908 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -168,19 +168,16 @@
     return function->GetError();
   }
 
-  void WaitForTwoResults(ExtensionFunction* function,
-                         base::Value* first_result,
-                         base::Value* second_result) {
+  void WaitForOneResult(ExtensionFunction* function, base::Value* result) {
     RunMessageLoopUntilResponse();
     EXPECT_TRUE(function->GetError().empty())
         << "Unexpected error: " << function->GetError();
     EXPECT_NE(nullptr, function->GetResultList());
 
     const auto& result_list = *function->GetResultList();
-    EXPECT_EQ(2ul, result_list.size());
+    EXPECT_EQ(1ul, result_list.size());
 
-    *first_result = result_list[0].Clone();
-    *second_result = result_list[1].Clone();
+    *result = result_list[0].Clone();
   }
 
  private:
@@ -206,11 +203,8 @@
     return async_function_runner_->WaitForError(function);
   }
 
-  void WaitForTwoResults(ExtensionFunction* function,
-                         base::Value* first_result,
-                         base::Value* second_result) {
-    return async_function_runner_->WaitForTwoResults(function, first_result,
-                                                     second_result);
+  void WaitForOneResult(ExtensionFunction* function, base::Value* result) {
+    return async_function_runner_->WaitForOneResult(function, result);
   }
 
  private:
@@ -990,29 +984,19 @@
                                Browser* browser,
                                std::string* access_token,
                                std::set<std::string>* granted_scopes) {
-    EXPECT_TRUE(
-        utils::RunFunction(function, args, browser, api_test_utils::NONE));
+    std::unique_ptr<base::Value> result_value =
+        utils::RunFunctionAndReturnSingleResult(function, args, browser);
+    ASSERT_TRUE(result_value);
+    std::unique_ptr<api::identity::GetAuthTokenResult> result =
+        api::identity::GetAuthTokenResult::FromValue(*result_value);
+    ASSERT_TRUE(result);
 
-    EXPECT_TRUE(function->GetError().empty())
-        << "Unexpected error: " << function->GetError();
-    EXPECT_NE(nullptr, function->GetResultList());
-
-    const auto& result_list = *function->GetResultList();
-    EXPECT_EQ(2ul, result_list.size());
-
-    const auto& access_token_value = result_list[0];
-    const auto& granted_scopes_value = result_list[1];
-    EXPECT_TRUE(access_token_value.is_string());
-    EXPECT_TRUE(granted_scopes_value.is_list());
-
-    std::set<std::string> scopes;
-    for (const auto& scope : granted_scopes_value.GetListDeprecated()) {
-      EXPECT_TRUE(scope.is_string());
-      scopes.insert(scope.GetString());
-    }
-
-    *access_token = access_token_value.GetString();
-    *granted_scopes = std::move(scopes);
+    EXPECT_NE(nullptr, result->token);
+    *access_token = *result->token;
+    EXPECT_NE(nullptr, result->granted_scopes);
+    std::set<std::string> granted_scopes_map(result->granted_scopes->begin(),
+                                             result->granted_scopes->end());
+    *granted_scopes = std::move(granted_scopes_map);
   }
 
   void WaitForGetAuthTokenResults(
@@ -1020,25 +1004,22 @@
       std::string* access_token,
       std::set<std::string>* granted_scopes,
       AsyncFunctionRunner* function_runner = nullptr) {
-    base::Value access_token_value;
-    base::Value granted_scopes_value;
+    base::Value result_value;
     if (function_runner == nullptr) {
-      WaitForTwoResults(function, &access_token_value, &granted_scopes_value);
+      WaitForOneResult(function, &result_value);
     } else {
-      function_runner->WaitForTwoResults(function, &access_token_value,
-                                         &granted_scopes_value);
+      function_runner->WaitForOneResult(function, &result_value);
     }
-    EXPECT_TRUE(access_token_value.is_string());
-    EXPECT_TRUE(granted_scopes_value.is_list());
+    std::unique_ptr<api::identity::GetAuthTokenResult> result =
+        api::identity::GetAuthTokenResult::FromValue(result_value);
+    ASSERT_TRUE(result);
 
-    std::set<std::string> scopes;
-    for (const auto& scope : granted_scopes_value.GetListDeprecated()) {
-      EXPECT_TRUE(scope.is_string());
-      scopes.insert(scope.GetString());
-    }
-
-    *access_token = access_token_value.GetString();
-    *granted_scopes = std::move(scopes);
+    ASSERT_NE(nullptr, result->token);
+    *access_token = *result->token;
+    ASSERT_NE(nullptr, result->granted_scopes);
+    std::set<std::string> granted_scopes_map(result->granted_scopes->begin(),
+                                             result->granted_scopes->end());
+    *granted_scopes = std::move(granted_scopes_map);
   }
 
  private:
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 24c2561d..b377740b4 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
@@ -325,12 +325,13 @@
     const std::set<std::string>& granted_scopes) {
   RecordFunctionResult(IdentityGetAuthTokenError(), remote_consent_approved_);
 
-  base::Value::List granted_scopes_value;
-  for (const auto& scope : granted_scopes)
-    granted_scopes_value.Append(scope);
+  api::identity::GetAuthTokenResult result;
+  result.token = std::make_unique<std::string>(access_token);
+  result.granted_scopes = std::make_unique<std::vector<std::string>>(
+      granted_scopes.begin(), granted_scopes.end());
 
-  CompleteAsyncRun(TwoArguments(base::Value(access_token),
-                                base::Value(std::move(granted_scopes_value))));
+  CompleteAsyncRun(
+      OneArgument(base::Value::FromUniquePtrValue(result.ToValue())));
 }
 
 void IdentityGetAuthTokenFunction::CompleteFunctionWithError(
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 3963e956..fdf0443 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -373,7 +373,7 @@
   apps::AppServiceProxyFactory::GetForProfile(profile)->LaunchAppWithParams(
       apps::AppLaunchParams(app_id, launch_container,
                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                            apps::mojom::LaunchSource::kFromManagementApi));
+                            apps::LaunchSource::kFromManagementApi));
 }
 
 void OnWebAppInstallCompleted(InstallOrLaunchWebAppCallback callback,
@@ -443,7 +443,7 @@
   apps::AppServiceProxyFactory::GetForProfile(profile)->LaunchAppWithParams(
       apps::AppLaunchParams(extension->id(), launch_container,
                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                            apps::mojom::LaunchSource::kFromManagementApi));
+                            apps::LaunchSource::kFromManagementApi));
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   ash::DemoSession::RecordAppLaunchSourceIfInDemoMode(
diff --git a/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
index 1edef20..299ca5d 100644
--- a/chrome/browser/extensions/api/notifications/notifications_apitest.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
@@ -168,8 +168,7 @@
         ->BrowserAppLauncher()
         ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
             extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-            WindowOpenDisposition::NEW_WINDOW,
-            apps::mojom::LaunchSource::kFromTest));
+            WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
   }
 
   std::unique_ptr<NotificationDisplayServiceTester> display_service_tester_;
diff --git a/chrome/browser/extensions/browsertest_util.cc b/chrome/browser/extensions/browsertest_util.cc
index 5a6de648..62de0e8 100644
--- a/chrome/browser/extensions/browsertest_util.cc
+++ b/chrome/browser/extensions/browsertest_util.cc
@@ -63,7 +63,7 @@
                       extension_app->id(),
                       apps::LaunchContainer::kLaunchContainerWindow,
                       WindowOpenDisposition::CURRENT_TAB,
-                      apps::mojom::LaunchSource::kFromTest)));
+                      apps::LaunchSource::kFromTest)));
 
   Browser* const browser = browser_change_observer.Wait();
   DCHECK(browser);
diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc
index 235aa5e..7f62854 100644
--- a/chrome/browser/extensions/extension_apitest.cc
+++ b/chrome/browser/extensions/extension_apitest.cc
@@ -149,10 +149,9 @@
   if (!url_to_open.is_empty()) {
     OpenURL(url_to_open, run_options.open_in_incognito);
   } else if (run_options.launch_as_platform_app) {
-    apps::AppLaunchParams params(extension->id(),
-                                 apps::LaunchContainer::kLaunchContainerNone,
-                                 WindowOpenDisposition::NEW_WINDOW,
-                                 apps::mojom::LaunchSource::kFromTest);
+    apps::AppLaunchParams params(
+        extension->id(), apps::LaunchContainer::kLaunchContainerNone,
+        WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
     params.command_line = *base::CommandLine::ForCurrentProcess();
     apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
         ->BrowserAppLauncher()
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index bde30815..b62c707 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -509,7 +509,7 @@
       content::NotificationService::AllSources());
   apps::AppLaunchParams params(
       app->id(), apps::LaunchContainer::kLaunchContainerNone,
-      WindowOpenDisposition::NEW_WINDOW, apps::mojom::LaunchSource::kFromTest);
+      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
   params.command_line = *base::CommandLine::ForCurrentProcess();
   apps::AppServiceProxyFactory::GetForProfile(profile())
       ->BrowserAppLauncher()
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index fb019649..cd78a27 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -909,6 +909,16 @@
     "expiry_milestone": 110
   },
   {
+    "name": "clipboard-unsanitized-content",
+    "owners": [
+      "ansollan@microsoft.com",
+      "asully@chromium.org",
+      "snianu@microsoft.com",
+      "chrome-owp-storage@google.com"
+    ],
+    "expiry_milestone": 110
+  },
+  {
     "name": "closed-tab-cache",
     "owners": [
       "altimin@chromium.org",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index acf0128f..648134d 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -102,6 +102,12 @@
     "Broker file operations on disk cache running in the Network Service. This "
     "is no-op when the Network Service is running in the browser process.";
 
+const char kClipboardUnsanitizedContentName[] =
+    "Clipboard unsanitized read and write";
+const char kClipboardUnsanitizedContentDescription[] =
+    "Allows reading/writing unsanitized content from/to the clipboard. "
+    "Currently, it is only applicable to HTML format. See crbug.com/1268679.";
+
 const char kCSSContainerQueriesName[] = "Enable CSS Container Queries";
 const char kCSSContainerQueriesDescription[] =
     "Enables support for @container, inline-size and block-size values for the "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index e171126..b70fd15d 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -82,6 +82,9 @@
 
 extern const char kCOLRV1FontsDescription[];
 
+extern const char kClipboardUnsanitizedContentName[];
+extern const char kClipboardUnsanitizedContentDescription[];
+
 extern const char kCSSContainerQueriesName[];
 extern const char kCSSContainerQueriesDescription[];
 
diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader.h b/chrome/browser/navigation_predictor/anchor_element_preloader.h
index 91d6e66b..361c0f12 100644
--- a/chrome/browser/navigation_predictor/anchor_element_preloader.h
+++ b/chrome/browser/navigation_predictor/anchor_element_preloader.h
@@ -28,7 +28,8 @@
   // element.
 
   // The number of allowed anchor element preloading attempts has been exceeded.
-  kLimitExceeded = content::kPreloadingFailureReasonContentEnd,
+  kLimitExceeded = static_cast<int>(
+      content::PreloadingFailureReason::kPreloadingFailureReasonCommonEnd),
 };
 
 // Helper function to convert AnchorPreloadingFailureReason to
diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
index f2a90df..b24334af 100644
--- a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
+++ b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
@@ -100,6 +100,13 @@
     }
   }
 
+  void GiveItSomeTime(const base::TimeDelta& t) {
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, run_loop.QuitClosure(), t);
+    run_loop.Run();
+  }
+
   // predictors::PreconnectManager::Observer
   // We observe DNS preresolution instead of preconnect, because test
   // servers all resolve to localhost and Chrome won't preconnect
@@ -317,6 +324,9 @@
   SimulateMouseDownElementWithId("anchor1");
   EXPECT_EQ(0, preresolve_count_);
 
+  // Give some time for Preloading APIs creation.
+  GiveItSomeTime(base::Milliseconds(100));
+
   histogram_tester()->ExpectTotalCount(
       kPreloadingAnchorElementPreloaderPreloadingTriggered, 0);
 
diff --git a/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc b/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc
index 13332ef..e2b10457 100644
--- a/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc
+++ b/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
 #include "chrome/browser/plugins/plugin_prefs.h"
 #include "content/public/browser/plugin_service.h"
+#include "content/public/common/webplugininfo.h"
 #endif
 
 using testing::NiceMock;
diff --git a/chrome/browser/policy/test/full_screen_allowed_policy_browsertest.cc b/chrome/browser/policy/test/full_screen_allowed_policy_browsertest.cc
index 682b4a1f..836b2fdf 100644
--- a/chrome/browser/policy/test/full_screen_allowed_policy_browsertest.cc
+++ b/chrome/browser/policy/test/full_screen_allowed_policy_browsertest.cc
@@ -107,8 +107,7 @@
       ->BrowserAppLauncher()
       ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
           extension->id(), apps::LaunchContainer::kLaunchContainerNone,
-          WindowOpenDisposition::NEW_WINDOW,
-          apps::mojom::LaunchSource::kFromTest));
+          WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
   extensions::AppWindow* window = add_window_observer.WaitForAppWindow();
   ASSERT_TRUE(window);
 
diff --git a/chrome/browser/predictors/autocomplete_action_predictor.cc b/chrome/browser/predictors/autocomplete_action_predictor.cc
index 8e89fc5..317426b 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor.cc
+++ b/chrome/browser/predictors/autocomplete_action_predictor.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/predictors/predictor_database_factory.h"
 #include "chrome/browser/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
 #include "chrome/browser/prefetch/prefetch_prefs.h"
+#include "chrome/browser/preloading/chrome_preloading.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_utils.h"
 #include "chrome/browser/profiles/profile.h"
@@ -34,6 +35,7 @@
 #include "components/omnibox/browser/base_search_provider.h"
 #include "components/omnibox/browser/omnibox_log.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/preloading_data.h"
 #include "content/public/browser/web_contents_delegate.h"
 
 namespace {
@@ -194,10 +196,25 @@
     const gfx::Size& size) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (prerender_utils::IsDirectUrlInputPrerenderEnabled()) {
+    auto* preloading_data =
+        content::PreloadingData::GetOrCreateForWebContents(&web_contents);
+
+    content::PreloadingURLMatchCallback same_url_matcher =
+        content::PreloadingData::GetSameURLMatcher(url);
+
+    // Create new PreloadingAttempt and pass all the values corresponding to
+    // this prerendering attempt.
+    content::PreloadingAttempt* preloading_attempt =
+        preloading_data->AddPreloadingAttempt(
+            ToPreloadingPredictor(
+                ChromePreloadingPredictor::kOmniboxDirectURLInput),
+            content::PreloadingType::kPrerender, std::move(same_url_matcher));
+
     PrerenderManager::CreateForWebContents(&web_contents);
     auto* prerender_manager = PrerenderManager::FromWebContents(&web_contents);
     direct_url_input_prerender_handle_ =
-        prerender_manager->StartPrerenderDirectUrlInput(url);
+        prerender_manager->StartPrerenderDirectUrlInput(url,
+                                                        *preloading_attempt);
   } else if (base::FeatureList::IsEnabled(
                  features::kOmniboxTriggerForNoStatePrefetch)) {
     content::SessionStorageNamespace* session_storage_namespace =
@@ -229,7 +246,8 @@
 AutocompleteActionPredictor::Action
 AutocompleteActionPredictor::RecommendAction(
     const std::u16string& user_text,
-    const AutocompleteMatch& match) const {
+    const AutocompleteMatch& match,
+    content::WebContents* web_contents) const {
   bool is_in_db = false;
   const double confidence = CalculateConfidence(user_text, match, &is_in_db);
   DCHECK(confidence >= 0.0 && confidence <= 1.0);
@@ -255,6 +273,25 @@
   if (action == ACTION_PRERENDER && AutocompleteMatch::IsSearchType(match.type))
     action = ACTION_PRECONNECT;
 
+  // During startup/shutdown it could be possible that the Omnibox doesn't have
+  // an attached WebContents yet. In that case, don't create PreloadingData and
+  // don't add PreloadingPrediction.
+  if (web_contents) {
+    // Create new PreloadingPrediction class and pass all the fields.
+    content::PreloadingURLMatchCallback same_url_matcher =
+        content::PreloadingData::GetSameURLMatcher(match.destination_url);
+
+    auto* preloading_data =
+        content::PreloadingData::GetOrCreateForWebContents(web_contents);
+
+    // We multiply confidence by 100 to pass the percentage and cast it into int
+    // for logs.
+    preloading_data->AddPreloadingPrediction(
+        ToPreloadingPredictor(
+            ChromePreloadingPredictor::kOmniboxDirectURLInput),
+        static_cast<int64_t>(confidence * 100), std::move(same_url_matcher));
+  }
+
   return action;
 }
 
diff --git a/chrome/browser/predictors/autocomplete_action_predictor.h b/chrome/browser/predictors/autocomplete_action_predictor.h
index 0857387..7b686ea 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor.h
+++ b/chrome/browser/predictors/autocomplete_action_predictor.h
@@ -104,13 +104,14 @@
   void ClearTransitionalMatches();
 
   // Returns the recommended action given |user_text|, the text the user has
-  // entered in the Omnibox, and |match|, the suggestion from Autocomplete.
-  // This method uses information from the ShortcutsBackend including how much
-  // of the matching entry the user typed, and how long it's been since the user
-  // visited the matching URL, to calculate a score between 0 and 1. This score
-  // is then mapped to an Action.
+  // entered in the Omnibox associated with |web_contents|, and |match|, the
+  // suggestion from Autocomplete. This method uses information from the
+  // ShortcutsBackend including how much of the matching entry the user typed,
+  // and how long it's been since the user visited the matching URL, to
+  // calculate a score between 0 and 1. This score is then mapped to an Action.
   Action RecommendAction(const std::u16string& user_text,
-                         const AutocompleteMatch& match) const;
+                         const AutocompleteMatch& match,
+                         content::WebContents* web_contents) const;
 
   // Begins prerendering or prefetch with `url`. The `size` gives the initial
   // size for the target prefetch. The predictor will run at most one prerender
@@ -130,6 +131,14 @@
   // Should be called when a URL is opened from the omnibox.
   void OnOmniboxOpenedUrl(const OmniboxLog& log);
 
+  // Uses local caches to calculate an exact percentage prediction that the user
+  // will take a particular match given what they have typed. |is_in_db| is set
+  // to differentiate trivial zero results resulting from a match not being
+  // found from actual zero results where the calculation returns 0.0.
+  double CalculateConfidence(const std::u16string& user_text,
+                             const AutocompleteMatch& match,
+                             bool* is_in_db) const;
+
   bool initialized() { return initialized_; }
 
  private:
@@ -225,14 +234,6 @@
   // Registers for notifications and sets the |initialized_| flag.
   void FinishInitialization();
 
-  // Uses local caches to calculate an exact percentage prediction that the user
-  // will take a particular match given what they have typed. |is_in_db| is set
-  // to differentiate trivial zero results resulting from a match not being
-  // found from actual zero results where the calculation returns 0.0.
-  double CalculateConfidence(const std::u16string& user_text,
-                             const AutocompleteMatch& match,
-                             bool* is_in_db) const;
-
   // Calculates the confidence for an entry in the DBCacheMap.
   double CalculateConfidenceForDbEntry(DBCacheMap::const_iterator iter) const;
 
diff --git a/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc b/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc
index 9965922..9360f8a7 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc
+++ b/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/preloading/chrome_preloading.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_service.h"
@@ -29,14 +30,24 @@
 #include "components/no_state_prefetch/browser/no_state_prefetch_manager.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_result.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/preloading_test_util.h"
+#include "content/public/test/test_renderer_host.h"
 #include "content/public/test/test_utils.h"
+#include "content/public/test/web_contents_tester.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::ASCIIToUTF16;
+using content::WebContents;
 using predictors::AutocompleteActionPredictor;
 
+using UkmEntry = ukm::TestUkmRecorder::HumanReadableUkmEntry;
+using ukm::builders::Preloading_Prediction;
+
 namespace {
 
 struct TestUrlInfo {
@@ -106,6 +117,14 @@
         HistoryServiceFactory::GetDefaultFactory());
     profile_ = profile_builder.Build();
 
+    web_contents_ = content::WebContentsTester::CreateTestWebContents(
+        profile_.get(), nullptr);
+    ukm_entry_builder_ =
+        std::make_unique<content::test::PreloadingPredictionUkmEntryBuilder>(
+            ToPreloadingPredictor(
+                ChromePreloadingPredictor::kOmniboxDirectURLInput));
+    test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
+
     predictor_ = std::make_unique<AutocompleteActionPredictor>(profile_.get());
     profile_->BlockUntilHistoryProcessesPendingRequests();
     content::RunAllTasksUntilIdle();
@@ -116,6 +135,7 @@
   }
 
   ~AutocompleteActionPredictorTest() override {
+    web_contents_.reset();
     // Wait for all pending tasks on the DB sequence.
     content::RunAllTasksUntilIdle();
     // Since we instantiated the predictor instead of going through a factory
@@ -174,6 +194,8 @@
     return row.id;
   }
 
+  WebContents* web_contents() { return web_contents_.get(); }
+
   void UpdateRow(const AutocompleteActionPredictorTable::Row& row) {
     AutocompleteActionPredictor::DBCacheKey key = { row.user_text, row.url };
     ASSERT_TRUE(db_cache()->find(key) != db_cache()->end());
@@ -284,10 +306,24 @@
     return AutocompleteActionPredictor::kMaximumStringLength;
   }
 
+  ukm::TestAutoSetUkmRecorder* test_ukm_recorder() {
+    return test_ukm_recorder_.get();
+  }
+
+  const content::test::PreloadingPredictionUkmEntryBuilder&
+  ukm_entry_builder() {
+    return *ukm_entry_builder_;
+  }
+
  private:
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<TestingProfile> profile_;
   std::unique_ptr<AutocompleteActionPredictor> predictor_;
+  std::unique_ptr<content::test::PreloadingPredictionUkmEntryBuilder>
+      ukm_entry_builder_;
+  std::unique_ptr<WebContents> web_contents_;
+  content::RenderViewHostTestEnabler rvh_test_enabler_;
+  std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
 };
 
 
@@ -479,15 +515,53 @@
 TEST_F(AutocompleteActionPredictorTest, RecommendActionURL) {
   ASSERT_NO_FATAL_FAILURE(AddAllRows());
 
+  // Navigate to kInitial URL.
+  GURL kInitialUrl("https://example.com");
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(kInitialUrl);
+  content::RenderFrameHostTester::For(web_contents()->GetPrimaryMainFrame())
+      ->InitializeRenderFrameIfNeeded();
+
   AutocompleteMatch match;
   match.type = AutocompleteMatchType::HISTORY_URL;
 
   for (size_t i = 0; i < std::size(TestUrlDb()); ++i) {
     match.destination_url = GURL(TestUrlDb()[i].url);
     EXPECT_EQ(TestUrlDb()[i].expected_action,
-              predictor()->RecommendAction(TestUrlDb()[i].user_text, match))
+              predictor()->RecommendAction(TestUrlDb()[i].user_text, match,
+                                           web_contents()))
         << "Unexpected action for " << match.destination_url;
   }
+
+  // Calculate confidence_interval for the first entry to cross-check with
+  // metrics.
+  bool is_in_db = false;
+  match.destination_url = GURL(TestUrlDb()[0].url);
+  double confidence = predictor()->CalculateConfidence(TestUrlDb()[0].user_text,
+                                                       match, &is_in_db);
+
+  // Set the first url in the database as the destination url to cross-check the
+  // metrics for the first Preloading.Prediction UKM.
+  GURL kDestinationUrl(TestUrlDb()[0].url);
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(kDestinationUrl);
+  ukm::SourceId ukm_source_id =
+      web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId();
+
+  // Check that we have recorded Preloading.Prediction for all entries in the
+  // TestUrlDb.
+  auto ukm_entries = test_ukm_recorder()->GetEntries(
+      Preloading_Prediction::kEntryName,
+      content::test::kPreloadingPredictionUkmMetrics);
+  EXPECT_EQ(ukm_entries.size(), std::size(TestUrlDb()));
+  // Cross-check that we have logged the correct metrics for Prediction,
+  // confidence, accurate_prediction on successful activation.
+  UkmEntry expected_entry = ukm_entry_builder().BuildEntry(
+      ukm_source_id, /*confidence=*/confidence * 100,
+      /*accurate_prediction=*/true);
+  EXPECT_EQ(ukm_entries[0], expected_entry)
+      << content::test::ActualVsExpectedUkmEntryToString(ukm_entries[0],
+                                                         expected_entry);
 }
 
 TEST_F(AutocompleteActionPredictorTest, RecommendActionSearch) {
@@ -503,8 +577,8 @@
          AutocompleteActionPredictor::ACTION_PRERENDER)
             ? AutocompleteActionPredictor::ACTION_PRECONNECT
             : TestUrlDb()[i].expected_action;
-    EXPECT_EQ(expected_action,
-              predictor()->RecommendAction(TestUrlDb()[i].user_text, match))
+    EXPECT_EQ(expected_action, predictor()->RecommendAction(
+                                   TestUrlDb()[i].user_text, match, nullptr))
         << "Unexpected action for " << match.destination_url;
   }
 }
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
index 2022fdee..d8653367 100644
--- a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
@@ -336,7 +336,7 @@
   }
   AddCacheEntry(navigation_url, request_it->second->prefetch_url());
   request_it->second->MarkPrefetchAsPrerenderActivated();
-  prefetches_.erase(request_it);
+  DeletePrefetch(search_terms);
 }
 
 std::unique_ptr<SearchPrefetchURLLoader>
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.h b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.h
index e3ae385f..0a57969 100644
--- a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.h
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.h
@@ -195,6 +195,8 @@
   void AddCacheEntry(const GURL& navigation_url, const GURL& prefetch_url);
 
   // Removes the prefetch and prefetch timers associated with |search_terms|.
+  // Note: Always call this method to remove prefetch requests from memory
+  // cache; Do not delete it from `prefetches_` directly.
   void DeletePrefetch(std::u16string search_terms);
 
   // Records metrics around the error rate of prefetches. When |error| is true,
diff --git a/chrome/browser/prefetch/search_prefetch/search_preload_unified_browsertest.cc b/chrome/browser/prefetch/search_prefetch/search_preload_unified_browsertest.cc
index a7370b70..5aef8a2 100644
--- a/chrome/browser/prefetch/search_prefetch/search_preload_unified_browsertest.cc
+++ b/chrome/browser/prefetch/search_prefetch/search_preload_unified_browsertest.cc
@@ -446,11 +446,16 @@
 // corresponding prefetch request succeeds.
 IN_PROC_BROWSER_TEST_F(SearchPreloadUnifiedBrowserTest,
                        PrerenderHintReceivedBeforeSucceed) {
+  base::HistogramTester histogram_tester;
   const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html");
   ASSERT_TRUE(GetActiveWebContents());
   ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), kInitialUrl));
   SetUpContext();
 
+  // Snapshot those samples recorded before the main test.
+  histogram_tester.ExpectTotalCount(
+      "Omnibox.SearchPrefetch.PrefetchServingReason", 1);
+
   std::string search_query = "pre";
   std::string prerender_query = "prerender";
   GURL expected_prefetch_url =
@@ -466,10 +471,12 @@
 
   // The suggestion service should hint expected_prerender_url, and prerendering
   // for this url should start.
-
   registry_observer.WaitForTrigger(expected_prerender_url);
   prerender_helper().WaitForPrerenderLoadCompletion(*GetActiveWebContents(),
                                                     expected_prerender_url);
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.PrefetchServingReason.Prerender",
+      SearchPrefetchServingReason::kPrerendered, 1);
 
   // Prefetch should be triggered as well.
   absl::optional<SearchPrefetchStatus> prefetch_status =
@@ -485,7 +492,14 @@
       *GetActiveWebContents(), expected_prerender_url);
   NavigateToPrerenderedResult(expected_prerender_url);
   prerender_observer.WaitForActivation();
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.PrefetchFinalStatus.SuggestionPrefetch",
+      SearchPrefetchStatus::kPrerenderActivated, 1);
 
+  // On prerender activation, `URLLoaderRequestInterceptor` would not be called,
+  // so no more sample should be recorded.
+  histogram_tester.ExpectTotalCount(
+      "Omnibox.SearchPrefetch.PrefetchServingReason", 1);
   EXPECT_EQ(1, prerender_helper().GetRequestCount(expected_prefetch_url));
   EXPECT_EQ(0, prerender_helper().GetRequestCount(expected_prerender_url));
 }
@@ -540,6 +554,9 @@
       *GetActiveWebContents(), expected_prerender_url);
   NavigateToPrerenderedResult(expected_prerender_url);
   prerender_observer.WaitForActivation();
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.PrefetchFinalStatus.SuggestionPrefetch",
+      SearchPrefetchStatus::kPrerenderActivated, 1);
 
   // No prerender requests went through network.
   EXPECT_EQ(1, prerender_helper().GetRequestCount(expected_prefetch_url));
@@ -569,6 +586,9 @@
   EXPECT_TRUE(prefetch_status.has_value());
   WaitUntilStatusChangesTo(base::ASCIIToUTF16(prerender_query),
                            {SearchPrefetchStatus::kRequestFailed});
+
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.FetchResult.SuggestionPrefetch", false, 1);
   EXPECT_FALSE(prerender_manager()->HasSearchResultPagePrerendered());
 }
 
@@ -648,7 +668,7 @@
   EXPECT_EQ(prefetch_status.value(), SearchPrefetchStatus::kPrerendered);
   content::test::PrerenderHostObserver prerender_observer(
       *GetActiveWebContents(), expected_prerender_url);
-  histogram_tester.ExpectBucketCount(
+  histogram_tester.ExpectUniqueSample(
       "Omnibox.SearchPrefetch.PrefetchServingReason.Prerender",
       SearchPrefetchServingReason::kPrerendered, 1);
 
@@ -659,6 +679,9 @@
 
   // 4. The prerender will be destroyed automatically.
   prerender_observer.WaitForDestroyed();
+  histogram_tester.ExpectUniqueSample(
+      internal::kHistogramPrerenderPredictionStatusDefaultSearchEngine,
+      PrerenderPredictionStatus::kCancelled, 1);
 }
 
 // Tests the activated prerendered page records navigation timings correctly.
@@ -953,7 +976,7 @@
 // able to interact with Android UI.
 // TODO(https://crbug.com/1342481): On LacrOS, the window's bound changes
 // unexpectedly, and it stops auto completing.
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(SearchPreloadUnifiedBrowserTest, TriggerAndActivate) {
   base::HistogramTester histogram_tester;
   const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html");
@@ -1000,15 +1023,102 @@
   EXPECT_EQ(prefetch_status.value(), SearchPrefetchStatus::kPrerendered);
   EXPECT_EQ(1, prerender_helper().GetRequestCount(expected_prefetch_url));
   EXPECT_EQ(0, prerender_helper().GetRequestCount(expected_prerender_url));
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.PrefetchServingReason.Prerender",
+      SearchPrefetchServingReason::kPrerendered, 1);
 
   // 4. Click and activate.
   content::test::PrerenderHostObserver prerender_observer(
       *GetActiveWebContents(), expected_prerender_url);
   omnibox->model()->AcceptInput(WindowOpenDisposition::CURRENT_TAB);
   prerender_observer.WaitForActivation();
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.PrefetchFinalStatus.SuggestionPrefetch",
+      SearchPrefetchStatus::kPrerenderActivated, 1);
   EXPECT_EQ(1, prerender_helper().GetRequestCount(expected_prefetch_url));
   EXPECT_EQ(0, prerender_helper().GetRequestCount(expected_prerender_url));
 }
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+
+// Tests the metrics for analyzing the unideal scenario that prerender fails
+// after taking response away. Without prerender, these prefetches could have
+// helped improve the performance of loading SRPs, so it is necessary to
+// understand the percentage of failing ones.
+IN_PROC_BROWSER_TEST_F(SearchPreloadUnifiedBrowserTest,
+                       PrerenderFailAfterResponseServed) {
+  base::HistogramTester histogram_tester;
+  const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html");
+  ASSERT_TRUE(GetActiveWebContents());
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), kInitialUrl));
+  SetUpContext();
+  content::test::PrerenderHostRegistryObserver registry_observer(
+      *GetActiveWebContents());
+
+  // 1. Type the first query.
+  std::string search_query_1 = "pre";
+  std::string prerender_query = "prerender";
+  GURL expected_prefetch_url =
+      GetSearchUrl(prerender_query, UrlType::kPrefetch);
+  GURL expected_prerender_url =
+      GetSearchUrl(prerender_query, UrlType::kPrerender);
+  GURL expected_real_url = GetSearchUrl(prerender_query, UrlType::kReal);
+
+  // 2. Prepare some context.
+  AutocompleteInput input(
+      base::ASCIIToUTF16(prerender_query), metrics::OmniboxEventProto::BLANK,
+      ChromeAutocompleteSchemeClassifier(browser()->profile()));
+  LocationBar* location_bar = browser()->window()->GetLocationBar();
+  OmniboxView* omnibox = location_bar->GetOmniboxView();
+  AutocompleteController* autocomplete_controller =
+      omnibox->model()->autocomplete_controller();
+
+  // Prevent the stop timer from killing the hints fetch early.
+  autocomplete_controller->SetStartStopTimerDurationForTesting(
+      base::Seconds(10));
+
+  // 3. Trigger prerender and prefetch.
+  autocomplete_controller->Start(input);
+  ui_test_utils::WaitForAutocompleteDone(browser());
+  ChangeAutocompleteResult(search_query_1, prerender_query,
+                           PrerenderHint::kEnabled, PrefetchHint::kEnabled);
+  registry_observer.WaitForTrigger(expected_prerender_url);
+  prerender_helper().WaitForPrerenderLoadCompletion(*GetActiveWebContents(),
+                                                    expected_prerender_url);
+  EXPECT_TRUE(prerender_manager()->HasSearchResultPagePrerendered());
+  absl::optional<SearchPrefetchStatus> prefetch_status =
+      search_prefetch_service()->GetSearchPrefetchStatusForTesting(
+          base::ASCIIToUTF16(prerender_query));
+  EXPECT_TRUE(prefetch_status.has_value());
+  EXPECT_EQ(prefetch_status.value(), SearchPrefetchStatus::kPrerendered);
+  EXPECT_EQ(1, prerender_helper().GetRequestCount(expected_prefetch_url));
+  EXPECT_EQ(0, prerender_helper().GetRequestCount(expected_prerender_url));
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.PrefetchServingReason.Prerender",
+      SearchPrefetchServingReason::kPrerendered, 1);
+
+  // 4. Fail the prerender by navigating it to another page.
+  content::test::PrerenderHostObserver prerender_observer(
+      *GetActiveWebContents(), expected_prerender_url);
+  int host_id = prerender_helper().GetHostForUrl(expected_prerender_url);
+  ASSERT_NE(host_id, content::RenderFrameHost::kNoFrameTreeNodeId);
+  prerender_helper().NavigatePrerenderedPage(host_id, expected_prefetch_url);
+  prerender_observer.WaitForDestroyed();
+  EXPECT_EQ(1, prerender_helper().GetRequestCount(expected_prefetch_url));
+  EXPECT_EQ(0, prerender_helper().GetRequestCount(expected_prerender_url));
+
+  // 5. Click the result.
+  content::TestNavigationObserver navigation_observer(GetActiveWebContents(),
+                                                      1);
+  omnibox->model()->AcceptInput(WindowOpenDisposition::CURRENT_TAB);
+  navigation_observer.Wait();
+
+  // 6. Fire the timer to make all prefetch requests expire
+  search_prefetch_service()->FireAllExpiryTimerForTesting();
+  histogram_tester.ExpectUniqueSample(
+      "Omnibox.SearchPrefetch.PrefetchFinalStatus.SuggestionPrefetch",
+      SearchPrefetchStatus::kPrerenderedAndClicked, 1);
+  EXPECT_EQ(1, prerender_helper().GetRequestCount(expected_prefetch_url));
+  EXPECT_EQ(1, prerender_helper().GetRequestCount(expected_real_url));
+}
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace
diff --git a/chrome/browser/preloading/chrome_preloading.h b/chrome/browser/preloading/chrome_preloading.h
index 17b2302..880154d8 100644
--- a/chrome/browser/preloading/chrome_preloading.h
+++ b/chrome/browser/preloading/chrome_preloading.h
@@ -20,12 +20,16 @@
   // When the preloading URL is predicted from the Omnibox Direct URL Input
   // (DUI). This is used to perform various preloading operations like prefetch
   // and prerender to load Omnibox predicted URLs faster.
-  kOmniboxDirectURLInput = content::kPreloadingPredictorContentEnd,
+  kOmniboxDirectURLInput = static_cast<int>(
+      content::PreloadingPredictor::kPreloadingPredictorContentEnd),
 
   // When a pointerdown (e.g. mousedown or touchstart) event happens on an
   // anchor element with an href value pointing to an HTTP(S) origin, we may
   // attempt to preload the link.
-  kPointerDownOnAnchor = content::kPreloadingPredictorContentEnd + 1,
+  kPointerDownOnAnchor =
+      static_cast<int>(
+          content::PreloadingPredictor::kPreloadingPredictorContentEnd) +
+      1,
 
   // TODO(crbug.com/1309934): Integrate more Preloading predictors with
   // Preloading logging APIs.
@@ -44,7 +48,8 @@
   // element.
 
   // Chrome was unable to get a LoadingPredictor object for the user profile.
-  kUnableToGetLoadingPredictor = content::kPreloadingEligibilityContentEnd,
+  kUnableToGetLoadingPredictor = static_cast<int>(
+      content::PreloadingEligibility::kPreloadingEligibilityContentEnd),
 };
 
 // Helper method to convert ChromePreloadingEligibility to
diff --git a/chrome/browser/prerender/omnibox_prerender_browsertest.cc b/chrome/browser/prerender/omnibox_prerender_browsertest.cc
index 3c82130..b95f1b0 100644
--- a/chrome/browser/prerender/omnibox_prerender_browsertest.cc
+++ b/chrome/browser/prerender/omnibox_prerender_browsertest.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
 #include "chrome/browser/prefetch/prefetch_prefs.h"
 #include "chrome/browser/prefetch/search_prefetch/field_trial_settings.h"
+#include "chrome/browser/preloading/chrome_preloading.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -30,10 +31,13 @@
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/preloading_test_util.h"
 #include "content/public/test/prerender_test_util.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/page_transition_types.h"
 
@@ -46,6 +50,9 @@
 
 namespace {
 
+using UkmEntry = ukm::TestUkmRecorder::HumanReadableUkmEntry;
+using ukm::builders::Preloading_Attempt;
+
 class OmniboxPrerenderBrowserTest : public PlatformBrowserTest {
  public:
   OmniboxPrerenderBrowserTest()
@@ -65,6 +72,12 @@
     host_resolver()->AddRule("*", "127.0.0.1");
     embedded_test_server()->ServeFilesFromDirectory(
         base::PathService::CheckedGet(chrome::DIR_TEST_DATA));
+    test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
+    ukm_entry_builder_ =
+        std::make_unique<content::test::PreloadingAttemptUkmEntryBuilder>(
+            content::PreloadingType::kPrerender,
+            ToPreloadingPredictor(
+                ChromePreloadingPredictor::kOmniboxDirectURLInput));
     ASSERT_TRUE(embedded_test_server()->Start());
   }
 
@@ -94,8 +107,19 @@
         profile);
   }
 
+  ukm::TestAutoSetUkmRecorder* test_ukm_recorder() {
+    return test_ukm_recorder_.get();
+  }
+
+  const content::test::PreloadingAttemptUkmEntryBuilder& ukm_entry_builder() {
+    return *ukm_entry_builder_;
+  }
+
  private:
   content::test::PrerenderTestHelper prerender_helper_;
+  std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
+  std::unique_ptr<content::test::PreloadingAttemptUkmEntryBuilder>
+      ukm_entry_builder_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
@@ -133,16 +157,21 @@
 
 // Tests that Prerender2 cannot be triggered when preload setting is disabled.
 IN_PROC_BROWSER_TEST_F(OmniboxPrerenderBrowserTest, DisableNetworkPrediction) {
+  const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html");
+  content::WebContents* web_contents = GetActiveWebContents();
+
   // Disable network prediction.
   PrefService* prefs = GetProfile()->GetPrefs();
   prefetch::SetPreloadPagesState(prefs,
                                  prefetch::PreloadPagesState::kNoPreloading);
   ASSERT_FALSE(prefetch::IsSomePreloadingEnabled(*prefs));
 
+  // Navigate to initial URL.
+  ASSERT_TRUE(content::NavigateToURL(web_contents, kInitialUrl));
+
   // Attempt to prerender a direct URL input.
   auto* predictor = GetAutocompleteActionPredictor();
   ASSERT_TRUE(predictor);
-  content::WebContents* web_contents = GetActiveWebContents();
   GURL prerender_url = embedded_test_server()->GetURL("/simple.html");
   predictor->StartPrerendering(prerender_url, *web_contents, gfx::Size(50, 50));
 
@@ -151,6 +180,30 @@
   int host_id = prerender_helper().GetHostForUrl(prerender_url);
   EXPECT_EQ(host_id, content::RenderFrameHost::kNoFrameTreeNodeId);
 
+  {
+    // Navigate to a different URL other than the prerender_url to flush the
+    // metrics.
+    GURL kUrl_a = embedded_test_server()->GetURL("a.com", "/title1.html");
+    ASSERT_TRUE(content::NavigateToURL(web_contents, kUrl_a));
+
+    ukm::SourceId ukm_source_id =
+        web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId();
+    auto ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName,
+        content::test::kPreloadingAttemptUkmMetrics);
+    EXPECT_EQ(ukm_entries.size(), 1u);
+
+    UkmEntry expected_entry = ukm_entry_builder().BuildEntry(
+        ukm_source_id, content::PreloadingEligibility::kPreloadingDisabled,
+        content::PreloadingHoldbackStatus::kUnspecified,
+        content::PreloadingTriggeringOutcome::kUnspecified,
+        content::PreloadingFailureReason::kUnspecified,
+        /*accurate=*/false);
+    EXPECT_EQ(ukm_entries[0], expected_entry)
+        << content::test::ActualVsExpectedUkmEntryToString(ukm_entries[0],
+                                                           expected_entry);
+  }
+
   // Re-enable the setting.
   prefetch::SetPreloadPagesState(
       prefs, prefetch::PreloadPagesState::kStandardPreloading);
@@ -165,6 +218,30 @@
   registry_observer.WaitForTrigger(prerender_url);
   host_id = prerender_helper().GetHostForUrl(prerender_url);
   EXPECT_NE(host_id, content::RenderFrameHost::kNoFrameTreeNodeId);
+
+  {
+    // Navigate to prerender_url.
+    ASSERT_TRUE(content::NavigateToURL(web_contents, prerender_url));
+
+    ukm::SourceId ukm_source_id =
+        web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId();
+    auto ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName,
+        content::test::kPreloadingAttemptUkmMetrics);
+
+    // There should be 2 entries after we attempt Prerender again.
+    EXPECT_EQ(ukm_entries.size(), 2u);
+
+    UkmEntry expected_entry = ukm_entry_builder().BuildEntry(
+        ukm_source_id, content::PreloadingEligibility::kEligible,
+        content::PreloadingHoldbackStatus::kAllowed,
+        content::PreloadingTriggeringOutcome::kSuccess,
+        content::PreloadingFailureReason::kUnspecified,
+        /*accurate=*/true);
+    EXPECT_EQ(ukm_entries[1], expected_entry)
+        << content::test::ActualVsExpectedUkmEntryToString(ukm_entries[1],
+                                                           expected_entry);
+  }
 }
 
 // Verifies that prerendering functions in document are properly exposed.
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index f3259af..1f0b3f3 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -117,7 +117,8 @@
           prerender_url, content::PrerenderTriggerType::kEmbedder,
           prerender_utils::kDirectUrlInputMetricSuffix,
           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+          nullptr);
   EXPECT_TRUE(prerender_handle);
   content::test::PrerenderTestHelper::WaitForPrerenderLoadCompletion(
       *GetActiveWebContents(), prerender_url);
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index c6d99be3..170ca3c 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -28,12 +28,14 @@
 #include "components/search_engines/template_url_service.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/preloading_data.h"
 #include "content/public/browser/prerender_handle.h"
 #include "content/public/browser/replaced_navigation_entry_data.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "net/base/url_util.h"
+#include "third_party/blink/public/common/features.h"
 #include "url/gurl.h"
 
 namespace internal {
@@ -45,6 +47,8 @@
 
 namespace {
 
+using content::PreloadingTriggeringOutcome;
+
 bool IsJavascriptDisabled(content::WebContents& web_contents, const GURL& url) {
   Profile* profile =
       Profile::FromBrowserContext(web_contents.GetBrowserContext());
@@ -142,7 +146,17 @@
                                        base::Unretained(this)));
   }
 
-  ~SearchPrerenderTask() = default;
+  ~SearchPrerenderTask() {
+    // Record whether or not the prediction is correct when prerendering for
+    // search suggestion was started. The value `kNotStarted` is recorded in
+    // AutocompleteControllerAndroid::OnSuggestionSelected() or
+    // ChromeOmniboxClient::OnURLOpenedFromOmnibox() if there is no started
+    // prerender.
+    DCHECK_NE(prediction_status_, PrerenderPredictionStatus::kNotStarted);
+    base::UmaHistogramEnumeration(
+        internal::kHistogramPrerenderPredictionStatusDefaultSearchEngine,
+        prediction_status_);
+  }
 
   // Not copyable or movable.
   SearchPrerenderTask(const SearchPrerenderTask&) = delete;
@@ -240,6 +254,15 @@
         delta, base::Milliseconds(1), base::Seconds(60), /*buckets=*/50);
   }
 
+  void set_prediction_status(PrerenderPredictionStatus prediction_status) {
+    // If the final status was set, do nothing because the status has been
+    // finalized.
+    if (prediction_status_ != PrerenderPredictionStatus::kUnused)
+      return;
+    DCHECK_NE(prediction_status, PrerenderPredictionStatus::kUnused);
+    prediction_status_ = prediction_status;
+  }
+
  private:
   // Called by OneShotTimer. Will cancel the ongoing prerender to ensure the
   // content displayed to users is up-to-date.
@@ -255,6 +278,11 @@
   // Stops the ongoing prerender when the prerendered result is out-of-date.
   base::OneShotTimer expiry_timer_;
 
+  // A task is associated with a prediction, this tracks the correctness of the
+  // prediction.
+  PrerenderPredictionStatus prediction_status_ =
+      PrerenderPredictionStatus::kUnused;
+
   // Stores the search term that `search_prerender_handle_` is prerendering.
   const std::u16string prerendered_search_terms_;
 };
@@ -302,10 +330,29 @@
 }
 
 base::WeakPtr<content::PrerenderHandle>
-PrerenderManager::StartPrerenderDirectUrlInput(const GURL& prerendering_url) {
+PrerenderManager::StartPrerenderDirectUrlInput(
+    const GURL& prerendering_url,
+    content::PreloadingAttempt& preloading_attempt) {
   if (direct_url_input_prerender_handle_) {
     if (direct_url_input_prerender_handle_->GetInitialPrerenderingUrl() ==
         prerendering_url) {
+      // In case a prerender is already present for the URL, prerendering is
+      // eligible but mark triggering outcome as a duplicate.
+      preloading_attempt.SetEligibility(
+          content::PreloadingEligibility::kEligible);
+
+      // Check and set the PreloadingHoldbackStatus before setting the
+      // TriggeringOutcome.
+      if (base::GetFieldTrialParamByFeatureAsBool(
+              blink::features::kPrerender2, "prerender_holdback", false)) {
+        preloading_attempt.SetHoldbackStatus(
+            content::PreloadingHoldbackStatus::kHoldback);
+      } else {
+        preloading_attempt.SetHoldbackStatus(
+            content::PreloadingHoldbackStatus::kAllowed);
+      }
+      preloading_attempt.SetTriggeringOutcome(
+          PreloadingTriggeringOutcome::kDuplicate);
       return direct_url_input_prerender_handle_->GetWeakPtr();
     }
 
@@ -318,7 +365,9 @@
       prerendering_url, content::PrerenderTriggerType::kEmbedder,
       prerender_utils::kDirectUrlInputMetricSuffix,
       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+      &preloading_attempt);
+
   if (direct_url_input_prerender_handle_) {
     return direct_url_input_prerender_handle_->GetWeakPtr();
   }
@@ -397,9 +446,12 @@
     const std::u16string& search_terms) {
   if (search_prerender_task_ &&
       search_prerender_task_->prerendered_search_terms() == search_terms) {
-    // TODO(https://crbug.com/1295170): Record
-    // PrerenderPredictionStatus::kCancelled here. And double check if we update
-    // kNotStarted.
+    // TODO(https://crbug.com/1295170): Now there is no kUnused record: all the
+    // unused tasks are canceled before navigation happens. Consider recording
+    // the result upon opening the URL rather than waiting for the navigation
+    // finishes.
+    search_prerender_task_->set_prediction_status(
+        PrerenderPredictionStatus::kCancelled);
     search_prerender_task_.reset();
   }
 }
@@ -442,21 +494,16 @@
     // TODO(https://crbug.com/1278634): Move all operations below into a
     // dedicated method of SearchPrerenderTask.
 
-    // Record whether or not the prediction is correct when prerendering for
-    // search suggestion was started. The value `kNotStarted` is recorded in
-    // AutocompleteControllerAndroid::OnSuggestionSelected() or
-    // ChromeOmniboxClient::OnURLOpenedFromOmnibox().
     bool is_search_destination_match = IsSearchDestinationMatch(
         search_prerender_task_->prerendered_search_terms(), *web_contents(),
         opened_url);
-    base::UmaHistogramEnumeration(
-        internal::kHistogramPrerenderPredictionStatusDefaultSearchEngine,
-        is_search_destination_match ? PrerenderPredictionStatus::kHitFinished
-                                    : PrerenderPredictionStatus::kUnused);
+
     if (is_search_destination_match) {
       // We may want to record this metric on AutocompleteMatch selected relying
       // on GetMatchSelectionTimestamp. But this is for rough estimation so it
       // may not need the precise data.
+      search_prerender_task_->set_prediction_status(
+          PrerenderPredictionStatus::kHitFinished);
       search_prerender_task_->RecordLifeTimeMetric();
     }
 
@@ -480,9 +527,7 @@
   if (search_prerender_task_->prerendered_search_terms() == search_terms) {
     return false;
   }
-
-  base::UmaHistogramEnumeration(
-      internal::kHistogramPrerenderPredictionStatusDefaultSearchEngine,
+  search_prerender_task_->set_prediction_status(
       PrerenderPredictionStatus::kCancelled);
   search_prerender_task_.reset();
   return true;
@@ -497,13 +542,15 @@
       base::BindRepeating(&IsSearchDestinationMatch, search_terms,
                           std::ref(*web_contents()));
 
+  // TODO(crbug.com/1325073): Integrate DSE Prerender logging with Preloading
+  // APIs.
   std::unique_ptr<content::PrerenderHandle> prerender_handle =
       web_contents()->StartPrerendering(
           prerendering_url, content::PrerenderTriggerType::kEmbedder,
           prerender_utils::kDefaultSearchEngineMetricSuffix,
           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_GENERATED |
                                     ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
-          std::move(url_match_predicate));
+          nullptr, std::move(url_match_predicate));
 
   if (prerender_handle) {
     search_prerender_task_ = std::make_unique<SearchPrerenderTask>(
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index 70c269128..fd3c270 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -76,10 +76,13 @@
   // to the cancellation of the previous prerender if the given url is different
   // from the on-going one. If the url given is already on-going, this function
   // will return the weak pointer to the on-going prerender handle.
+  // PreloadingAttempt represents the attempt corresponding to this prerender to
+  // log the necessary metrics.
   // TODO(https://crbug.com/1278634): Merge the start method with DSE interface
   // using AutocompleteMatch as the parameter instead of GURL.
   base::WeakPtr<content::PrerenderHandle> StartPrerenderDirectUrlInput(
-      const GURL& prerendering_url);
+      const GURL& prerendering_url,
+      content::PreloadingAttempt& preloading_attempt);
 
   // Returns true if the current tab prerendered a search result for omnibox
   // inputs.
diff --git a/chrome/browser/prerender/prerender_omnibox_ui_browsertest.cc b/chrome/browser/prerender/prerender_omnibox_ui_browsertest.cc
index 51c4c0b..3fda3b9 100644
--- a/chrome/browser/prerender/prerender_omnibox_ui_browsertest.cc
+++ b/chrome/browser/prerender/prerender_omnibox_ui_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
 #include "chrome/browser/prefetch/search_prefetch/search_prefetch_service.h"
 #include "chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.h"
+#include "chrome/browser/preloading/chrome_preloading.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_utils.h"
 #include "chrome/browser/profiles/profile.h"
@@ -42,14 +43,21 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/preloading_test_util.h"
 #include "content/public/test/prerender_test_util.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace {
 
+using UkmEntry = ukm::TestUkmRecorder::HumanReadableUkmEntry;
+using ukm::builders::Preloading_Attempt;
+
 class AutocompleteActionPredictorObserverImpl
     : public predictors::AutocompleteActionPredictor::Observer {
  public:
@@ -99,6 +107,13 @@
     host_resolver()->AddRule("*", "127.0.0.1");
     embedded_test_server()->ServeFilesFromDirectory(
         base::PathService::CheckedGet(chrome::DIR_TEST_DATA));
+    test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
+    ukm_entry_builder_ =
+        std::make_unique<content::test::PreloadingAttemptUkmEntryBuilder>(
+            content::PreloadingType::kPrerender,
+            ToPreloadingPredictor(
+                ChromePreloadingPredictor::kOmniboxDirectURLInput));
+
     ASSERT_TRUE(embedded_test_server()->Start());
   }
 
@@ -114,6 +129,14 @@
     return prerender_helper_;
   }
 
+  ukm::TestAutoSetUkmRecorder* test_ukm_recorder() {
+    return test_ukm_recorder_.get();
+  }
+
+  const content::test::PreloadingAttemptUkmEntryBuilder& ukm_entry_builder() {
+    return *ukm_entry_builder_;
+  }
+
   // Returns last committed page transition type. This value is only
   // meaningful after calling Observe(GetActiveWebContents()) in the test case
   // and after DidFinishNavigation.
@@ -213,6 +236,9 @@
   content::test::PrerenderTestHelper prerender_helper_;
   base::test::ScopedFeatureList scoped_feature_list_;
   ui::PageTransition last_finished_page_transition_type_;
+  std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
+  std::unique_ptr<content::test::PreloadingAttemptUkmEntryBuilder>
+      ukm_entry_builder_;
   bool is_prerendering_page_;
 };
 
@@ -284,6 +310,35 @@
   EXPECT_TRUE(IsPrerenderingNavigation());
   EXPECT_EQ(GetActiveWebContents()->GetLastCommittedURL(), kNewUrl);
 
+  {
+    // Check that we store two entries for both new and old entry.
+    ukm::SourceId ukm_source_id =
+        GetActiveWebContents()->GetPrimaryMainFrame()->GetPageUkmSourceId();
+    auto ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName,
+        content::test::kPreloadingAttemptUkmMetrics);
+    EXPECT_EQ(ukm_entries.size(), 2u);
+
+    std::vector<UkmEntry> expected_entries = {
+        ukm_entry_builder().BuildEntry(
+            ukm_source_id, content::PreloadingEligibility::kEligible,
+            content::PreloadingHoldbackStatus::kAllowed,
+            content::PreloadingTriggeringOutcome::kRunning,
+            content::PreloadingFailureReason::kUnspecified,
+            /*accurate=*/false),
+        ukm_entry_builder().BuildEntry(
+            ukm_source_id, content::PreloadingEligibility::kEligible,
+            content::PreloadingHoldbackStatus::kAllowed,
+            content::PreloadingTriggeringOutcome::kSuccess,
+            content::PreloadingFailureReason::kUnspecified,
+            /*accurate=*/true),
+    };
+    EXPECT_THAT(ukm_entries,
+                testing::UnorderedElementsAreArray(expected_entries))
+        << content::test::ActualVsExpectedUkmEntriesToString(ukm_entries,
+                                                             expected_entries);
+  }
+
   // Prerender was attempted twice and the first one was cancelled.
   histogram_tester.ExpectBucketCount(
       internal::kHistogramPrerenderPredictionStatusDirectUrlInput,
@@ -326,6 +381,34 @@
   EXPECT_TRUE(IsPrerenderingNavigation());
   EXPECT_EQ(GetActiveWebContents()->GetLastCommittedURL(), kPrerenderingUrl);
 
+  {
+    ukm::SourceId ukm_source_id =
+        GetActiveWebContents()->GetPrimaryMainFrame()->GetPageUkmSourceId();
+    auto ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName,
+        content::test::kPreloadingAttemptUkmMetrics);
+    EXPECT_EQ(ukm_entries.size(), 2u);
+
+    std::vector<UkmEntry> expected_entries = {
+        ukm_entry_builder().BuildEntry(
+            ukm_source_id, content::PreloadingEligibility::kEligible,
+            content::PreloadingHoldbackStatus::kAllowed,
+            content::PreloadingTriggeringOutcome::kSuccess,
+            content::PreloadingFailureReason::kUnspecified,
+            /*accurate=*/true),
+        ukm_entry_builder().BuildEntry(
+            ukm_source_id, content::PreloadingEligibility::kEligible,
+            content::PreloadingHoldbackStatus::kAllowed,
+            content::PreloadingTriggeringOutcome::kDuplicate,
+            content::PreloadingFailureReason::kUnspecified,
+            /*accurate=*/true),
+    };
+    EXPECT_THAT(ukm_entries,
+                testing::UnorderedElementsAreArray(expected_entries))
+        << content::test::ActualVsExpectedUkmEntriesToString(ukm_entries,
+                                                             expected_entries);
+  }
+
   histogram_tester.ExpectUniqueSample(
       internal::kHistogramPrerenderPredictionStatusDirectUrlInput,
       PrerenderPredictionStatus::kHitFinished, 1);
@@ -335,6 +418,60 @@
       PrerenderPredictionStatus::kNotStarted, 1);
 }
 
+class PrerenderPreloaderHoldbackBrowserTest
+    : public PrerenderOmniboxUIBrowserTest {
+ public:
+  PrerenderPreloaderHoldbackBrowserTest() {
+    feature_list_.InitWithFeaturesAndParameters(
+        {{blink::features::kPrerender2, {{"prerender_holdback", "true"}}},
+         {features::kOmniboxTriggerForPrerender2, {{}}}},
+        {/* disabled_features */});
+  }
+  ~PrerenderPreloaderHoldbackBrowserTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PrerenderPreloaderHoldbackBrowserTest,
+                       PrerenderHoldbackTest) {
+  Observe(GetActiveWebContents());
+  const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html");
+  ASSERT_TRUE(GetActiveWebContents());
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), kInitialUrl));
+
+  // Attempt to prerender a direct URL input with prerender holdback.
+  ASSERT_TRUE(GetAutocompleteActionPredictor());
+  WaitForAutocompleteActionPredictorInitialization();
+  const GURL kPrerenderingUrl =
+      embedded_test_server()->GetURL("/empty.html?prerender");
+  GetAutocompleteActionPredictor()->StartPrerendering(
+      kPrerenderingUrl, *GetActiveWebContents(), gfx::Size(50, 50));
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), kPrerenderingUrl));
+
+  {
+    ukm::SourceId ukm_source_id =
+        GetActiveWebContents()->GetPrimaryMainFrame()->GetPageUkmSourceId();
+    auto ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName,
+        content::test::kPreloadingAttemptUkmMetrics);
+
+    // PreloadingHoldbackStatus should be set to kHoldback.
+    std::vector<UkmEntry> expected_entries = {
+        ukm_entry_builder().BuildEntry(
+            ukm_source_id, content::PreloadingEligibility::kEligible,
+            content::PreloadingHoldbackStatus::kHoldback,
+            content::PreloadingTriggeringOutcome::kUnspecified,
+            content::PreloadingFailureReason::kUnspecified,
+            /*accurate=*/true),
+    };
+    EXPECT_THAT(ukm_entries,
+                testing::UnorderedElementsAreArray(expected_entries))
+        << content::test::ActualVsExpectedUkmEntriesToString(ukm_entries,
+                                                             expected_entries);
+  }
+}
+
 // Tests that NavigationHandle::IsRendererInitiated() returns RendererInitiated
 // = false correctly.
 IN_PROC_BROWSER_TEST_F(PrerenderOmniboxUIBrowserTest,
diff --git a/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc b/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
index dfd2b61..8616e1b0 100644
--- a/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
+++ b/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
@@ -38,6 +38,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/webplugininfo.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 48e0269..7e14b01 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -3385,7 +3385,7 @@
 
   apps::AppLaunchParams launch_params(
       *app_id, apps::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::CURRENT_TAB, apps::mojom::LaunchSource::kFromMenu);
+      WindowOpenDisposition::CURRENT_TAB, apps::LaunchSource::kFromMenu);
   launch_params.override_url = params_.link_url;
   apps::AppServiceProxyFactory::GetForProfile(GetProfile())
       ->LaunchAppWithParams(std::move(launch_params));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 9b2a8fb..531e228 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -31,7 +31,6 @@
   "background/chromevox.js",
   "background/focus_bounds.js",
   "background/logging/log_store.js",
-  "background/output/output_types.js",
   "common/abstract_earcons.js",
   "common/background_bridge.js",
   "common/braille/braille_interface.js",
@@ -110,6 +109,7 @@
   "background/output/output_format_tree.js",
   "background/output/output_logger.js",
   "background/output/output_role_info.js",
+  "background/output/output_types.js",
   "background/page_load_sound_handler.js",
   "background/panel/i_search.js",
   "background/panel/i_search_handler.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index c6a464b..da1f6cf 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -27,6 +27,7 @@
 import {MathHandler} from './math_handler.js';
 import {MediaAutomationHandler} from './media_automation_handler.js';
 import {Output} from './output/output.js';
+import {OutputEventType} from './output/output_types.js';
 import {PageLoadSoundHandler} from './page_load_sound_handler.js';
 import {PanelBackground} from './panel/panel_background.js';
 import {ChromeVoxPrefs} from './prefs.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index 9d551cd..4af719d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -55,6 +55,8 @@
     await importModule(
         'PointerHandler', '/chromevox/background/pointer_handler.js');
     await importModule('Cursor', '/common/cursors/cursor.js');
+    await importModule(
+        'OutputAction', '/chromevox/background/output/output_types.js');
 
     this.forceContextualLastOutput();
   }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js
index d13f791..a285a47 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_command_handler.js
@@ -13,6 +13,7 @@
 import {DesktopAutomationInterface} from '../desktop_automation_interface.js';
 import {EventSourceState} from '../event_source.js';
 import {Output} from '../output/output.js';
+import {OutputNodeSpan, OutputSelectionSpan} from '../output/output_types.js';
 
 const RoleType = chrome.automation.RoleType;
 const StateType = chrome.automation.StateType;
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 da06fe1..7ee4765 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -28,6 +28,7 @@
 import {EventSourceState} from './event_source.js';
 import {GestureInterface} from './gesture_interface.js';
 import {Output} from './output/output.js';
+import {OutputEventType} from './output/output_types.js';
 import {PhoneticData} from './phonetic_data.js';
 import {ChromeVoxPrefs} from './prefs.js';
 import {SmartStickyMode} from './smart_sticky_mode.js';
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 581dd02..543624a2 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
@@ -18,6 +18,7 @@
 import {TextEditHandler} from './editing/editing.js';
 import {EventSourceState} from './event_source.js';
 import {Output} from './output/output.js';
+import {OutputEventType} from './output/output_types.js';
 
 const ActionType = chrome.automation.ActionType;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js
index d748018..df38e745 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editable_line.js
@@ -11,6 +11,7 @@
 import {Cursor, CURSOR_NODE_INDEX, CursorMovement, CursorUnit} from '../../../common/cursors/cursor.js';
 import {CursorRange} from '../../../common/cursors/range.js';
 import {Output} from '../output/output.js';
+import {OutputEventType, OutputNodeSpan} from '../output/output_types.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
index 06208a6..7f9ad07 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
@@ -15,6 +15,7 @@
 import {ChromeVoxState, ChromeVoxStateObserver} from '../chromevox_state.js';
 import {Color} from '../color.js';
 import {Output} from '../output/output.js';
+import {OutputEventType, OutputNodeSpan} from '../output/output_types.js';
 
 import {EditableLine} from './editable_line.js';
 import {ChromeVoxEditableTextBase, TextChangeEvent} from './editable_text_base.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
index 02b377cf..9ae71db 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
@@ -9,6 +9,7 @@
 import {CursorRange} from '../../../common/cursors/range.js';
 import {Output} from '../output/output.js';
 import {OutputRoleInfo} from '../output/output_role_info.js';
+import {OutputEventType} from '../output/output_types.js';
 
 import {EditableLine} from './editable_line.js';
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js
index 2bd9de0..6350340 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js
@@ -20,6 +20,8 @@
     await importModule(
         'IntentHandler', '/chromevox/background/editing/intent_handler.js');
     await importModule('Output', '/chromevox/background/output/output.js');
+    await importModule(
+        'OutputEventType', '/chromevox/background/output/output_types.js');
 
     window.Dir = constants.Dir;
     window.IntentTextBoundaryType = chrome.automation.IntentTextBoundaryType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/find_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/find_handler.js
index af736e5..6612116 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/find_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/find_handler.js
@@ -9,6 +9,7 @@
 
 import {ChromeVoxState} from './chromevox_state.js';
 import {Output} from './output/output.js';
+import {OutputEventType} from './output/output_types.js';
 
 const TreeChangeObserverFilter = chrome.automation.TreeChangeObserverFilter;
 
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 8a62250d..b38229a 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
@@ -11,6 +11,7 @@
 import {BaseAutomationHandler} from './base_automation_handler.js';
 import {ChromeVoxState} from './chromevox_state.js';
 import {Output} from './output/output.js';
+import {OutputEventType} from './output/output_types.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js
index 384054f7..d5b8ad88 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js
@@ -9,6 +9,7 @@
 
 import {ChromeVoxState} from './chromevox_state.js';
 import {Output} from './output/output.js';
+import {OutputEventType} from './output/output_types.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index 1e48fc2..8b8b312 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -31,13 +31,6 @@
 goog.require('LogType');
 goog.require('Msgs');
 goog.require('NavBraille');
-goog.require('OutputAction');
-goog.require('OutputContextOrder');
-goog.require('OutputEarconAction');
-goog.require('OutputEventType');
-goog.require('OutputNodeSpan');
-goog.require('OutputSelectionSpan');
-goog.require('OutputSpeechProperties');
 goog.require('PanelNodeMenuData');
 goog.require('PanelTabMenuItemData');
 goog.require('QueueMode');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
index b9be4fd7..203b9fb7 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
@@ -14,10 +14,11 @@
 import {PhoneticData} from '../phonetic_data.js';
 
 import {OutputAncestryInfo} from './output_ancestry_info.js';
-import {OutputFormatParser, OutputFormatParserObserver} from './output_format_parser.js';
+import {OutputFormatParser} from './output_format_parser.js';
 import {OutputFormatTree} from './output_format_tree.js';
 import {OutputRulesStr} from './output_logger.js';
 import {OutputRoleInfo} from './output_role_info.js';
+import {OutputAction, OutputContextOrder, OutputEarconAction, OutputEventType, OutputNodeSpan, OutputSelectionSpan, OutputSpeechProperties} from './output_types.js';
 
 const AriaCurrentState = chrome.automation.AriaCurrentState;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_ancestry_info.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_ancestry_info.js
index 21523cb0..90d4fd3 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_ancestry_info.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_ancestry_info.js
@@ -7,6 +7,7 @@
  * chains given the current node.
  */
 import {OutputRoleInfo} from './output_role_info.js';
+import {OutputContextOrder} from './output_types.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 const Dir = constants.Dir;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js
index edb8bd0..92e6dcc 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js
@@ -6,6 +6,8 @@
  * @fileoverview Roel information for the Output module.
  */
 
+import {OutputContextOrder} from './output_types.js';
+
 /**
  * Metadata about supported automation roles.
  * @const {Object<{msgId: string,
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_test.js
index e9407b5a..21405afd 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_test.js
@@ -105,6 +105,9 @@
         'OutputRoleInfo', '/chromevox/background/output/output_role_info.js');
     await importModule('CursorRange', '/common/cursors/range.js');
     await importModule('Cursor', '/common/cursors/cursor.js');
+    await importModule(
+        ['OutputEarconAction', 'OutputNodeSpan', 'OutputSelectionSpan'],
+        '/chromevox/background/output/output_types.js');
 
     window.Dir = AutomationUtil.Dir;
     this.forceContextualLastOutput();
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_types.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_types.js
index bcc32927..6bcd027 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_types.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_types.js
@@ -6,19 +6,11 @@
  * @fileoverview Definitions of all types related to output.
  */
 
-goog.provide('OutputAction');
-goog.provide('OutputContextOrder');
-goog.provide('OutputEarconAction');
-goog.provide('OutputEventType');
-goog.provide('OutputNodeSpan');
-goog.provide('OutputSelectionSpan');
-goog.provide('OutputSpeechProperties');
-
 /**
  * The ordering of contextual output.
  * @enum {string}
  */
-OutputContextOrder = {
+export const OutputContextOrder = {
   // The (ancestor) context comes before the node output.
   FIRST: 'first',
   // The (ancestor) context comes before the node output when moving forward,
@@ -35,7 +27,7 @@
 /**
  * Used to annotate utterances with speech properties.
  */
-OutputSpeechProperties = class {
+export class OutputSpeechProperties {
   constructor() {
     /** @private {!Object} */
     this.properties_ = {};
@@ -56,21 +48,21 @@
     }
     return clone;
   }
-};
+}
 
 /**
  * Custom actions performed while rendering an output string.
  */
-OutputAction = class {
+export class OutputAction {
   constructor() {}
 
   run() {}
-};
+}
 
 /**
  * Action to play an earcon.
  */
-OutputEarconAction = class extends OutputAction {
+export class OutputEarconAction extends OutputAction {
   /**
    * @param {string} earconId
    * @param {chrome.automation.Rect=} opt_location
@@ -93,12 +85,12 @@
   toJSON() {
     return {earconId: this.earconId};
   }
-};
+}
 
 /**
  * Annotation for text with a selection inside it.
  */
-OutputSelectionSpan = class {
+export class OutputSelectionSpan {
   /**
    * @param {number} startIndex
    * @param {number} endIndex
@@ -108,14 +100,14 @@
     this.startIndex = startIndex < endIndex ? startIndex : endIndex;
     this.endIndex = endIndex > startIndex ? endIndex : startIndex;
   }
-};
+}
 
 /**
  * Wrapper for automation nodes as annotations.  Since the
  * {@code AutomationNode} constructor isn't exposed in the API, this class is
  * used to allow instanceof checks on these annotations.
  */
-OutputNodeSpan = class {
+export class OutputNodeSpan {
   /**
    * @param {!AutomationNode} node
    * @param {number=} opt_offset Offsets into the node's text. Defaults to 0.
@@ -124,12 +116,12 @@
     this.node = node;
     this.offset = opt_offset ? opt_offset : 0;
   }
-};
+}
 
 /**
  * Possible events handled by ChromeVox internally.
  * @enum {string}
  */
-OutputEventType = {
+export const OutputEventType = {
   NAVIGATE: 'navigate'
 };
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
index c2c3b8c5..b7650c47 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
@@ -9,6 +9,7 @@
 import {CursorRange} from '../../../common/cursors/range.js';
 import {PanelBridge} from '../../common/panel_bridge.js';
 import {ChromeVoxState, ChromeVoxStateObserver} from '../chromevox_state.js';
+import {OutputEventType} from '../output/output_types.js';
 
 import {ISearch} from './i_search.js';
 import {ISearchHandler} from './i_search_handler.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
index eb09353..a8347460 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
@@ -10,6 +10,7 @@
 import {PanelBridge} from '../../common/panel_bridge.js';
 import {ChromeVoxState} from '../chromevox_state.js';
 import {Output} from '../output/output.js';
+import {OutputEventType} from '../output/output_types.js';
 
 const AutomationNode = chrome.automation.AutomationNode;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
index dfa8fc9..fb999bc 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
@@ -12,6 +12,7 @@
 import {ChromeVoxState, ChromeVoxStateObserver} from './chromevox_state.js';
 import {DesktopAutomationHandler} from './desktop_automation_handler.js';
 import {Output} from './output/output.js';
+import {OutputEventType} from './output/output_types.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationNode = chrome.automation.AutomationNode;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/kbexplorer_loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/kbexplorer_loader.js
index 5efb8360..69f3138 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/kbexplorer_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/learn_mode/kbexplorer_loader.js
@@ -14,5 +14,4 @@
 goog.require('LibLouis');
 goog.require('Msgs');
 goog.require('NavBraille');
-goog.require('OutputContextOrder');
 goog.require('Spannable');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
index e940265..eadb35f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
@@ -18,13 +18,6 @@
 goog.require('LogStore');
 goog.require('Msgs');
 goog.require('NavBraille');
-goog.require('OutputAction');
-goog.require('OutputContextOrder');
-goog.require('OutputEarconAction');
-goog.require('OutputEventType');
-goog.require('OutputNodeSpan');
-goog.require('OutputSelectionSpan');
-goog.require('OutputSpeechProperties');
 goog.require('PanelNodeMenuData');
 goog.require('PanelNodeMenuItemData');
 goog.require('QueueMode');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js
index 378ee34..0999998 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js
@@ -123,7 +123,8 @@
           '/chromevox/background/gesture_command_handler.js');
       await importModule(
           'OutputRoleInfo', '/chromevox/background/output/output_role_info.js');
-
+      await importModule(
+          'OutputContextOrder', '/chromevox/background/output/output_types.js');
       // For tests, enable announcement of events we trigger via automation.
       BaseAutomationHandler.announceActions = true;
 
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
index 395fb4e0..6d3f6c4d8 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
@@ -35,6 +35,7 @@
           '/emoji_14_0_ordering_remaining.json',
         ],
         [CategoryEnum.EMOTICON]: ['/emoticon_ordering.json'],
+        [CategoryEnum.SYMBOL]: ['/symbol_ordering.json'],
       },
     };
   }
diff --git a/chrome/browser/resources/chromeos/emoji_picker/metadata_extension.js b/chrome/browser/resources/chromeos/emoji_picker/metadata_extension.js
index a65f95c..d2ef6a15 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/metadata_extension.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/metadata_extension.js
@@ -74,7 +74,11 @@
     icon: 'emoji_picker:insert_emoticon',
     active: true,
   },
-  // TODO(b/213141035): insert symbols metadata back.
+  {
+    name: 'symbol',
+    icon: 'emoji_picker_v2:symbol_omega',
+    active: false,
+  },
   {
     name: 'emoticon',
     icon: 'emoji_picker_v2:emoticon_gssmiley',
@@ -139,6 +143,14 @@
     {name: 'Pointing',},
     {name: 'Sparkling',},
   ],
+  'symbol': [
+    {name: 'Arrows', pagination: 1,},
+    {name: 'Bullet/Stars',},
+    {name: 'Currency',},
+    {name: 'Letterlike', pagination: 2,},
+    {name: 'Math',},
+    {name: 'Miscellaneous',},
+  ],
 };
 
 // TODO(b/233271528): Remove the list and load it from the input data.
diff --git a/chrome/browser/resources/chromeos/emoji_picker/types.js b/chrome/browser/resources/chromeos/emoji_picker/types.js
index f84330a..5e70844 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/types.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/types.js
@@ -53,5 +53,6 @@
  */
 export const CategoryEnum = {
   EMOJI: 'emoji',
-  EMOTICON: 'emoticon'
+  EMOTICON: 'emoticon',
+  SYMBOL: 'symbol',
 };
diff --git a/chrome/browser/resources/chromeos/notification_tester/notification_tester.html b/chrome/browser/resources/chromeos/notification_tester/notification_tester.html
index 333451e1..0ac3f0c9935 100644
--- a/chrome/browser/resources/chromeos/notification_tester/notification_tester.html
+++ b/chrome/browser/resources/chromeos/notification_tester/notification_tester.html
@@ -4,6 +4,7 @@
     flex-direction: column;
     height: 100%;
     overflow: hidden;
+    prefers-color-scheme: light;
   }
 
   app-header {
@@ -64,6 +65,39 @@
     justify-content: center;
   }
 
+  cr-input {
+    --cr-input-background-color: rgba(255, 255, 255, 0);
+    --cr-input-padding-bottom: 0;
+    --cr-input-padding-top: 0;
+    --cr-primary-text-color: auto;
+    --cr-input-error-display: none;
+    background: inherit;
+    border: 2px solid #666;
+    border-radius: .25rem;
+  }
+
+  .delayed-notification {
+    gap: 1%;
+    margin-top: 3px;
+  }
+
+  #delayed-notification-input {
+    width: 3rem;
+  }
+
+  .error {
+    color: rgb(217, 48, 37);
+    font-size: 0.9rem;
+    max-width: 32rem;
+  }
+
+  .countdown {
+    color: rgb(0, 0, 0);
+    font-size: 1rem;
+    margin-bottom: 0;
+    margin-top: 0;
+  }
+
   cr-radio-button {
     padding: 0 auto;
     --cr-radio-button-label-spacing: 10px;
@@ -81,7 +115,7 @@
     font-size: 1rem;
     font-weight: bold;
     margin-inline-end: 0.25em;
-    margin-top: 1em;
+    margin-top: 0.5em;
     padding: 0.25em 0.5em;
     width: max-content;
   }
@@ -164,12 +198,6 @@
         </cr-checkbox>
       </div>
 
-      <div class="form-item">
-        <label for="renotify">Renotify</label>
-        <cr-checkbox id="renotify" checked="{{notifMetadata.richDataRenotify}}">
-        </cr-checkbox>
-      </div>
-
       <h3> Type Specific Fields </h3>
       <template is="dom-if" if="[[showTypeSpecificDesc]]">
         <p> Change the notification type to 'Progress' or 'Multiple' to see more
@@ -234,9 +262,33 @@
           <cr-radio-button name="3" label="3"></cr-radio-button>
         </cr-radio-group>
       </div>
-
     </div>
     <div class="config-form">
+      <h2> Generation Settings </h2>
+
+      <div class="form-item delayed-notification">
+        <p class="countdown">Generate after</p>
+        <template is="dom-if" if="[[!generatingDelayedNotification]]">
+          <cr-input id="delayed-notification-input" type="number" min="0"
+            max="10" placeholder="0" value="{{notificationDelayTime}}"
+            auto-validate="true" invalid="{{delayTimeInvalid}}">
+          </cr-input>
+        </template>
+        <div class="countdown" hidden$="[[!generatingDelayedNotification]]">
+          <strong>[[countdownDisplayTime]]</strong>
+        </div>
+        <p class="countdown">seconds</p>
+        <div class="error" hidden$="[[!delayTimeInvalid]]">
+          Error: Enter a number from 0
+          to 10.
+        </div>
+      </div>
+
+      <div class="form-item error">
+        Note: Notifications generated while a delayed notification is
+        underway may not appear synchronously.
+      </div>
+
       <button type="button" id="generateNotifBtn" on-click="onClickGenerate">
         Generate </button>
       <button type="button" id="resetFormBtn" on-click="onClickReset">
diff --git a/chrome/browser/resources/chromeos/notification_tester/notification_tester.js b/chrome/browser/resources/chromeos/notification_tester/notification_tester.js
index ea971fd..83e3631 100644
--- a/chrome/browser/resources/chromeos/notification_tester/notification_tester.js
+++ b/chrome/browser/resources/chromeos/notification_tester/notification_tester.js
@@ -7,6 +7,7 @@
 import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.m.js';
 import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
 import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -36,7 +37,6 @@
             notifierType: 'System',
             richDataNeverTimeout: false,
             richDataPinned: false,
-            richDataRenotify: false,
             richDataProgress: '-1',
             richDataShowSettings: false,
             richDataShowSnooze: false,
@@ -47,26 +47,42 @@
         },
       },
       /*
-      @type {!boolean}
+      @type {boolean}
       */
       showTypeSpecificDesc: {type: Boolean},
       /*
-      @type {!boolean}
+      @type {boolean}
       */
       showProgressOptions: {type: Boolean},
       /*
-      @type {!boolean}
+      @type {boolean}
       */
       showMultiOptions: {type: Boolean},
       /*
-      @type {!boolean}
+      @type {boolean}
       */
       showDisplaySource: {type: Boolean},
       /*
-      @type {!boolean}
+      @type {boolean}
       */
       showOriginURL: {type: Boolean},
       /*
+      @type {boolean}
+      */
+      generatingDelayedNotification: {type: Boolean, value: false},
+      /*
+      @type {number}
+      */
+      countdownDisplayTime: {type: String},
+      /*
+      @type {boolean}
+      */
+      delayTimeInvalid: {type: Boolean},
+      /*
+      @type {string}
+      */
+      notificationDelayTime: {type: String, value: '0'},
+      /*
        * @private
        */
       titleSelectList: {
@@ -165,7 +181,30 @@
         parseInt(this.notifMetadata.richDataProgress, BASE_TEN);
 
     // Send notification data to C++
-    chrome.send('generateNotificationForm', [this.notifMetadata]);
+    const NUM_MS_IN_S = 1000;
+    if (!this.delayTimeInvalid) {
+      // If the user enters 0 or leaves the delay time field blank, generate
+      // notifications synchronously.
+      const timedInputValueNumber =
+          parseInt(this.notificationDelayTime, BASE_TEN);
+      if (timedInputValueNumber === 0 ||
+          this.notificationDelayTime.length === 0) {
+        chrome.send('generateNotificationForm', [this.notifMetadata]);
+      } else {
+        this.startDelayedNotificationCountdown(timedInputValueNumber);
+
+        // Enable synchronous generation while delayed notification generation
+        // is underway.
+        this.notificationDelayTime = '';
+        // Create a deep copy of the current state of this.notifMetadata to
+        // ensure it won't be modified before chrome.send() is called.
+        const notifMetadataCopy =
+            JSON.parse(JSON.stringify(this.notifMetadata));
+        setTimeout(
+            chrome.send, timedInputValueNumber * NUM_MS_IN_S,
+            'generateNotificationForm', [notifMetadataCopy]);
+      }
+    }
   }
 
   onClickReset() {
@@ -186,7 +225,6 @@
         'notifMetadata.richDataPriority',
         NotificationPriority.DEFAULT_PRIORITY);
     this.set('notifMetadata.richDataPinned', false);
-    this.set('notifMetadata.richDataRenotify', false);
     this.set('notifMetadata.richDataShowSnooze', false);
     this.set('notifMetadata.richDataShowSettings', false);
     this.set('notifMetadata.richDataProgress', '-1');
@@ -218,6 +256,22 @@
 
     this.notifMetadata.notifierType = NotifierType.WEB_PAGE;
   }
+
+  // Start a countdown from the given number in seconds. Used for delayed
+  // notification generation.
+  startDelayedNotificationCountdown(startTime) {
+    // Note that setInterval initially delays before executing the function.
+    const ONE_SECOND = 1000;  // milliseconds.
+    this.countdownDisplayTime = startTime;
+    this.generatingDelayedNotification = true;
+    const countdownTimer = setInterval(() => {
+      this.countdownDisplayTime--;
+      if (this.countdownDisplayTime <= 0) {
+        clearInterval(countdownTimer);
+        this.generatingDelayedNotification = false;
+      }
+    }, ONE_SECOND);
+  }
 }
 
 customElements.define(NotificationTester.is, NotificationTester);
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/notification_tester/types.js b/chrome/browser/resources/chromeos/notification_tester/types.js
index 12214ad7..87f00f9 100644
--- a/chrome/browser/resources/chromeos/notification_tester/types.js
+++ b/chrome/browser/resources/chromeos/notification_tester/types.js
@@ -20,7 +20,6 @@
  *   richDataNeverTimeout: boolean,
  *   richDataPriority: number,
  *   richDataPinned: boolean,
- *   richDataRenotify: boolean,
  *   richDataShowSnooze: boolean,
  *   richDataShowSettings: boolean,
  *   richDataProgress: number,
diff --git a/chrome/browser/resources/new_tab_page/customize_shortcuts.html b/chrome/browser/resources/new_tab_page/customize_shortcuts.html
index 9ef2264..5c464cef 100644
--- a/chrome/browser/resources/new_tab_page/customize_shortcuts.html
+++ b/chrome/browser/resources/new_tab_page/customize_shortcuts.html
@@ -238,6 +238,16 @@
       width: 28px;
     }
   }
+
+  @media (forced-colors: active) {
+    .option-icon {
+      background-color: ButtonText;
+    }
+
+    .selected .option-icon {
+      background-color: SelectedItem;
+    }
+  }
 </style>
 <div id="options">
   <div id="optionCustomLinks"
diff --git a/chrome/browser/resources/new_tab_page/mini_page.html b/chrome/browser/resources/new_tab_page/mini_page.html
index c198c9d2..0e8947a 100644
--- a/chrome/browser/resources/new_tab_page/mini_page.html
+++ b/chrome/browser/resources/new_tab_page/mini_page.html
@@ -51,6 +51,7 @@
   }
 
   @media (forced-colors: active) {
+    :host([single-colored-logo]) .mini-header,
     .mini-shortcuts {
       background-color: ButtonText;
     }
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 2bc5fc51..83aa9af 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -100,13 +100,14 @@
   sources = [
     "$root_gen_dir/chrome/browser/ui/webui/settings/ash/search/search.mojom-lite.js",
     "$root_gen_dir/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom-lite.js",
-    "$root_gen_dir/chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom-lite.js",
   ]
   outputs = [ "$root_gen_dir/chrome/browser/ui/webui/settings/chromeos/search/{{source_file_part}}" ]
   deps = [ "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js" ]
 }
 
 # OS Settings specific mojo files, bundled in optimized builds.
+# TODO(crbug.com/1179821) remove this build rule once these lite bindings
+# have been converted to WebUI bindings
 preprocess_if_expr("preprocess_mojo") {
   deps = [
     ":ash_search_mojo_bindings",
@@ -120,7 +121,6 @@
     "constants/setting.mojom-lite.js",
     "search/search.mojom-lite.js",
     "search/search_result_icon.mojom-lite.js",
-    "search/user_action_recorder.mojom-lite.js",
   ]
 }
 
@@ -850,7 +850,10 @@
 }
 
 js_library("metrics_recorder") {
-  deps = [ "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js_library_for_compile" ]
+  deps = [
+    "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_webui_js",
+    "//chrome/browser/ui/webui/settings/chromeos/constants:mojom_webui_js",
+  ]
 }
 
 js_library("os_icons") {
@@ -889,7 +892,10 @@
 }
 
 js_library("pref_to_setting_metric_converter") {
-  deps = [ "//chrome/browser/ui/webui/settings/chromeos/constants:mojom_js_library_for_compile" ]
+  deps = [
+    "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_webui_js",
+    "//chrome/browser/ui/webui/settings/chromeos/constants:mojom_webui_js",
+  ]
 }
 
 js_library("route_observer_behavior") {
diff --git a/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_page.js b/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_page.js
index 3709a5a..7546cc4 100644
--- a/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_page.js
+++ b/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_page.js
@@ -290,8 +290,7 @@
   onBluetoothToggledByUser_() {
     // Record that the user manually enabled/disabled Bluetooth.
     recordSettingChange(
-        chromeos.settings.mojom.Setting.kBluetoothOnOff,
-        {boolValue: this.bluetoothToggleState_});
+        Setting.kBluetoothOnOff, {boolValue: this.bluetoothToggleState_});
   }
 
   /** @private */
diff --git a/chrome/browser/resources/settings/chromeos/device_page/power.js b/chrome/browser/resources/settings/chromeos/device_page/power.js
index 68f697f..9558a113 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/power.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/power.js
@@ -20,6 +20,7 @@
 import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {SettingChangeValue} from '../../mojom-webui/search/user_action_recorder.mojom-webui.js';
 import {Setting} from '../../mojom-webui/setting.mojom-webui.js';
 import {Route} from '../../router.js';
 import {DeepLinkingBehavior, DeepLinkingBehaviorInterface} from '../deep_linking_behavior.js';
@@ -337,10 +338,8 @@
         this.$.adaptiveChargingToggle.checked;
     this.browserProxy_.setAdaptiveCharging(enabled);
     recordSettingChange(
-        chromeos.settings.mojom.Setting.kAdaptiveCharging,
-        /** @type {!chromeos.settings.mojom.SettingChangeValue} */ ({
-          boolValue: enabled,
-        }));
+        Setting.kAdaptiveCharging,
+        /** @type {!SettingChangeValue} */ ({boolValue: enabled}));
   }
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js
index 2207ba1..be9c309 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js
@@ -17,6 +17,7 @@
 import {HTMLEscape} from 'chrome://resources/js/util.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {Setting} from '../../mojom-webui/setting.mojom-webui.js';
 import {recordSettingChange} from '../metrics_recorder.js';
 
 /**
@@ -177,9 +178,7 @@
     if (this.type ===
         OncMojo.getNetworkTypeString(
             chromeos.networkConfig.mojom.NetworkType.kWiFi)) {
-      recordSettingChange(
-          chromeos.settings.mojom.Setting.kWifiAddNetwork,
-          {stringValue: this.guid});
+      recordSettingChange(Setting.kWifiAddNetwork, {stringValue: this.guid});
     } else {
       recordSettingChange();
     }
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
index 4e6d168a..c49a243 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
@@ -856,8 +856,7 @@
       return;
     }
     recordSettingChange(
-        chromeos.settings.mojom.Setting.kWifiHidden,
-        {boolValue: !!this.hiddenPref_.value});
+        Setting.kWifiHidden, {boolValue: !!this.hiddenPref_.value});
     const config = this.getDefaultConfigProperties_();
     config.typeConfig.wifi.hiddenSsid = this.hiddenPref_.value ?
         chromeos.networkConfig.mojom.HiddenSsidMode.kEnabled :
@@ -1782,7 +1781,7 @@
 
     if (this.managedProperties_.type ===
         chromeos.networkConfig.mojom.NetworkType.kWiFi) {
-      recordSettingChange(chromeos.settings.mojom.Setting.kForgetWifiNetwork);
+      recordSettingChange(Setting.kForgetWifiNetwork);
     } else {
       recordSettingChange();
     }
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js
index 490182e1..25a294c 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_known_networks_page.js
@@ -339,7 +339,7 @@
     });
 
     if (this.networkType === chromeos.networkConfig.mojom.NetworkType.kWiFi) {
-      recordSettingChange(chromeos.settings.mojom.Setting.kForgetWifiNetwork);
+      recordSettingChange(Setting.kForgetWifiNetwork);
     } else {
       recordSettingChange();
     }
diff --git a/chrome/browser/resources/settings/chromeos/metrics_recorder.js b/chrome/browser/resources/settings/chromeos/metrics_recorder.js
index fc73206d..204420a 100644
--- a/chrome/browser/resources/settings/chromeos/metrics_recorder.js
+++ b/chrome/browser/resources/settings/chromeos/metrics_recorder.js
@@ -2,36 +2,35 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
-import '../constants/setting.mojom-lite.js';
-import '../search/user_action_recorder.mojom-lite.js';
-
 /**
  * @fileoverview
  * Provides functions used for recording user actions within settings.
  * Also provides a way to inject a test implementation for verifying
  * user action recording.
  */
-/** @type {?chromeos.settings.mojom.UserActionRecorderInterface} */
+
+import {SettingChangeValue, UserActionRecorder, UserActionRecorderInterface} from '../mojom-webui/search/user_action_recorder.mojom-webui.js';
+import {Setting} from '../mojom-webui/setting.mojom-webui.js';
+
+/** @type {?UserActionRecorderInterface} */
 let userActionRecorder = null;
 
 /**
- * @param {!chromeos.settings.mojom.UserActionRecorderInterface}
- *     testRecorder
+ * @param {!UserActionRecorderInterface} testRecorder
  */
 export function setUserActionRecorderForTesting(testRecorder) {
   userActionRecorder = testRecorder;
 }
 
 /**
- * @return {!chromeos.settings.mojom.UserActionRecorderInterface}
+ * @return {!UserActionRecorderInterface}
  */
 function getRecorder() {
   if (userActionRecorder) {
     return userActionRecorder;
   }
 
-  userActionRecorder = chromeos.settings.mojom.UserActionRecorder.getRemote();
+  userActionRecorder = UserActionRecorder.getRemote();
   return userActionRecorder;
 }
 
@@ -61,8 +60,8 @@
  * legacy code which has not yet been converted.
  * TODO(https://crbug.com/1133553): make |opt_setting| non-optional when
  * migration is complete.
- * @param {!chromeos.settings.mojom.Setting=} opt_setting
- * @param {!chromeos.settings.mojom.SettingChangeValue=} opt_value
+ * @param {!Setting=} opt_setting
+ * @param {!SettingChangeValue=} opt_value
  */
 export function recordSettingChange(opt_setting, opt_value) {
   if (opt_setting === undefined) {
diff --git a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
index 0345863e..03e442fd 100644
--- a/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_about_page/os_about_page.js
@@ -25,7 +25,6 @@
 import 'chrome://resources/cr_elements/icons.m.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import 'chrome://resources/polymer/v3_0/iron-media-query/iron-media-query.js';
-import '../../constants/setting.mojom-lite.js';
 
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
@@ -403,14 +402,14 @@
   /** @private */
   onDiagnosticsClick_() {
     this.aboutBrowserProxy_.openDiagnostics();
-    recordSettingChange(chromeos.settings.mojom.Setting.kDiagnostics);
+    recordSettingChange(Setting.kDiagnostics);
   }
 
   /** @private */
   onFirmwareUpdatesClick_() {
     assert(this.showFirmwareUpdatesApp_);
     this.aboutBrowserProxy_.openFirmwareUpdatesPage();
-    recordSettingChange(chromeos.settings.mojom.Setting.kFirmwareUpdates);
+    recordSettingChange(Setting.kFirmwareUpdates);
   }
 
   /** @private */
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notifications_subpage.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notifications_subpage.js
index 2a0f1d7..25f7bc2 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notifications_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notifications_subpage.js
@@ -182,8 +182,7 @@
     this.isDndEnabled_ = !this.isDndEnabled_;
     this.mojoInterfaceProvider_.setQuietMode(this.isDndEnabled_);
     recordSettingChange(
-        chromeos.settings.mojom.Setting.kDoNotDisturbOnOff,
-        {boolValue: this.isDndEnabled_});
+        Setting.kDoNotDisturbOnOff, {boolValue: this.isDndEnabled_});
   }
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
index 461e9af..f5d4bb2 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
@@ -254,8 +254,7 @@
    */
   addAccount_(event) {
     recordSettingChange(
-        chromeos.settings.mojom.Setting.kAddAccount,
-        {intValue: this.accounts_.length + 1});
+        Setting.kAddAccount, {intValue: this.accounts_.length + 1});
     this.browserProxy_.addAccount();
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/os_printing_page.js b/chrome/browser/resources/settings/chromeos/os_printing_page/os_printing_page.js
index 0d9fcbd..d93e31d9 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/os_printing_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/os_printing_page.js
@@ -110,7 +110,7 @@
   /** @private */
   onOpenScanningApp_() {
     this.browserProxy_.openScanningApp();
-    recordSettingChange(chromeos.settings.mojom.Setting.kScanningApp);
+    recordSettingChange(Setting.kScanningApp);
   }
 }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
index 0821019..265345008 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
@@ -29,6 +29,8 @@
 import {Debouncer, html, microTask, mixinBehaviors, PolymerElement, timeOut} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../../i18n_setup.js';
+import {SettingChangeValue} from '../../mojom-webui/search/user_action_recorder.mojom-webui.js';
+import {Setting} from '../../mojom-webui/setting.mojom-webui.js';
 import {Route, Router} from '../../router.js';
 import {setGlobalScrollTarget} from '../global_scroll_target_behavior.js';
 import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSettingChange} from '../metrics_recorder.js';
@@ -394,11 +396,7 @@
       return;
     }
 
-    const setting =
-        /** @type {!chromeos.settings.mojom.Setting} */ (settingMetric.setting);
-    const value = /** @type {!chromeos.settings.mojom.SettingChangeValue} */ (
-        settingMetric.value);
-    recordSettingChange(setting, value);
+    recordSettingChange(settingMetric.setting, settingMetric.value);
   }
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/pref_to_setting_metric_converter.js b/chrome/browser/resources/settings/chromeos/pref_to_setting_metric_converter.js
index 6c43860..b9d5dbb7 100644
--- a/chrome/browser/resources/settings/chromeos/pref_to_setting_metric_converter.js
+++ b/chrome/browser/resources/settings/chromeos/pref_to_setting_metric_converter.js
@@ -8,38 +8,35 @@
  * to pref-based settings.
  */
 
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
-import '../constants/setting.mojom-lite.js';
-import '../search/user_action_recorder.mojom-lite.js';
+import {SettingChangeValue} from '../mojom-webui/search/user_action_recorder.mojom-webui.js';
+import {Setting} from '../mojom-webui/setting.mojom-webui.js';
 
 export class PrefToSettingMetricConverter {
   /**
    * @param {string} prefKey
    * @param {*} prefValue
-   * @return {?{setting: !chromeos.settings.mojom.Setting, value:
-   *     !chromeos.settings.mojom.SettingChangeValue}}
+   * @return {?{setting: !Setting, value: !SettingChangeValue}}
    */
   convertPrefToSettingMetric(prefKey, prefValue) {
     switch (prefKey) {
       // device_page/keyboard.js
       case 'settings.language.send_function_keys':
         return {
-          setting: chromeos.settings.mojom.Setting.kKeyboardFunctionKeys,
+          setting: Setting.kKeyboardFunctionKeys,
           value: {boolValue: /** @type {boolean} */ (prefValue)},
         };
 
       // device_page/pointers.js
       case 'settings.touchpad.sensitivity2':
         return {
-          setting: chromeos.settings.mojom.Setting.kTouchpadSpeed,
+          setting: Setting.kTouchpadSpeed,
           value: {intValue: /** @type {number} */ (prefValue)},
         };
 
       // os_privacy_page/os_privacy_page.js
       case 'cros.device.peripheral_data_access_enabled':
         return {
-          setting:
-              chromeos.settings.mojom.Setting.kPeripheralDataAccessProtection,
+          setting: Setting.kPeripheralDataAccessProtection,
           value: {boolValue: /** @type {boolean} */ (prefValue)},
         };
 
diff --git a/chrome/browser/resources/settings/chromeos/settings_search_handler.js b/chrome/browser/resources/settings/chromeos/settings_search_handler.js
index d4cb61f04..e166bbe2 100644
--- a/chrome/browser/resources/settings/chromeos/settings_search_handler.js
+++ b/chrome/browser/resources/settings/chromeos/settings_search_handler.js
@@ -7,7 +7,6 @@
 import '../constants/routes.mojom-lite.js';
 import '../constants/setting.mojom-lite.js';
 import '../search/search_result_icon.mojom-lite.js';
-import '../search/user_action_recorder.mojom-lite.js';
 import '../search/search.mojom-lite.js';
 
 /**
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index b28169d..c0f98d1e 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2563,6 +2563,8 @@
       "webui/chromeos/cellular_setup/mobile_setup_ui.h",
       "webui/chromeos/certificate_manager_dialog_ui.cc",
       "webui/chromeos/certificate_manager_dialog_ui.h",
+      "webui/chromeos/cloud_upload/cloud_upload_dialog.cc",
+      "webui/chromeos/cloud_upload/cloud_upload_dialog.h",
       "webui/chromeos/connectivity_diagnostics_dialog.cc",
       "webui/chromeos/connectivity_diagnostics_dialog.h",
       "webui/chromeos/crostini_installer/crostini_installer_dialog.cc",
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
index 77d8c23..73f2238e 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
@@ -48,8 +48,6 @@
     public void testIsDirty() {
         ToolbarViewResourceAdapter adapter =
                 new ToolbarViewResourceAdapter(mToolbarContainer, false);
-        adapter.setOnResourceReadyCallback((resource) -> {});
-
         Assert.assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.BlockCaptureReason"));
@@ -81,7 +79,7 @@
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarBlockCaptureReason.UNKNOWN));
 
-        adapter.triggerBitmapCapture();
+        adapter.getBitmap();
         Assert.assertFalse(adapter.isDirty());
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
@@ -90,14 +88,19 @@
         Assert.assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.SnapshotDifference"));
-        Assert.assertEquals(1,
+
+        // Need to be careful here. #getBitmap() in debug builds will call isDirty. Reset histogram
+        // tracking to avoid being needing to depend on build type.
+        UmaRecorderHolder.resetForTesting();
+
+        Assert.assertEquals(0,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarBlockCaptureReason.UNKNOWN));
 
         adapter.forceInvalidate();
         Assert.assertTrue(adapter.isDirty());
-        Assert.assertEquals(2,
+        Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarBlockCaptureReason.UNKNOWN));
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc
index 6716bcb..fbd7b81 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -99,9 +99,8 @@
 
 void AppServiceAppResult::Open(int event_flags) {
   Launch(event_flags,
-         (is_recommendation()
-              ? apps::mojom::LaunchSource::kFromAppListRecommendation
-              : apps::mojom::LaunchSource::kFromAppListQuery));
+         (is_recommendation() ? apps::LaunchSource::kFromAppListRecommendation
+                              : apps::LaunchSource::kFromAppListQuery));
 }
 
 void AppServiceAppResult::GetContextMenuModel(GetMenuModelCallback callback) {
@@ -153,11 +152,11 @@
 }
 
 void AppServiceAppResult::ExecuteLaunchCommand(int event_flags) {
-  Launch(event_flags, apps::mojom::LaunchSource::kFromAppListQueryContextMenu);
+  Launch(event_flags, apps::LaunchSource::kFromAppListQueryContextMenu);
 }
 
 void AppServiceAppResult::Launch(int event_flags,
-                                 apps::mojom::LaunchSource launch_source) {
+                                 apps::LaunchSource launch_source) {
   if (id() == ash::kInternalAppIdContinueReading &&
       url_for_continuous_reading_.is_valid()) {
     apps::RecordAppLaunch(id(), launch_source);
@@ -207,7 +206,8 @@
     }
   }
 
-  proxy->Launch(app_id(), event_flags, launch_source,
+  proxy->Launch(app_id(), event_flags,
+                apps::ConvertLaunchSourceToMojomLaunchSource(launch_source),
                 apps::MakeWindowInfo(controller()->GetAppListDisplayId()));
 }
 
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.h b/chrome/browser/ui/app_list/search/app_service_app_result.h
index 482ec36..145fb26 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.h
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.h
@@ -12,10 +12,10 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "chrome/browser/ui/app_list/search/app_result.h"
 #include "components/favicon_base/favicon_types.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/icon_cache.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
-#include "components/services/app_service/public/mojom/types.mojom-forward.h"
 #include "url/gurl.h"
 
 class AppListControllerDelegate;
@@ -54,7 +54,7 @@
   void ExecuteLaunchCommand(int event_flags) override;
 
   ash::SearchResultType GetSearchResultType() const;
-  void Launch(int event_flags, apps::mojom::LaunchSource launch_source);
+  void Launch(int event_flags, apps::LaunchSource launch_source);
 
   int GetIconDimension(bool chip);
   void CallLoadIcon(bool chip, bool allow_placeholder_icon);
diff --git a/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc b/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc
index 2303e180..45dbfd6 100644
--- a/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc
+++ b/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <string>
 
+#include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "base/bind.h"
 #include "base/metrics/field_trial_params.h"
@@ -40,9 +41,6 @@
 // Maximum accepted size of an ItemSuggest response. 1MB.
 constexpr int kMaxResponseSize = 1024 * 1024;
 
-// TODO(crbug.com/1034842): Investigate:
-//  - enterprise policies that should limit this traffic.
-//  - settings that should disable drive results.
 constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
     net::DefineNetworkTrafficAnnotation("launcher_item_suggest", R"(
       semantics {
@@ -72,6 +70,16 @@
   return profile->GetPrefs()->GetBoolean(drive::prefs::kDisableDrive);
 }
 
+base::Time GetLastRequestTime(Profile* profile) {
+  return profile->GetPrefs()->GetTime(
+      chromeos::prefs::kLauncherLastContinueRequestTime);
+}
+
+void SetLastRequestTime(Profile* profile, const base::Time& time) {
+  profile->GetPrefs()->SetTime(
+      chromeos::prefs::kLauncherLastContinueRequestTime, time);
+}
+
 //------------------
 // Metrics utilities
 //------------------
@@ -168,6 +176,7 @@
 constexpr base::FeatureParam<std::string> ItemSuggestCache::kServerUrl;
 constexpr base::FeatureParam<std::string> ItemSuggestCache::kModelName;
 constexpr base::FeatureParam<bool> ItemSuggestCache::kMultipleQueriesPerSession;
+constexpr base::FeatureParam<int> ItemSuggestCache::kLongDelayMinutes;
 
 ItemSuggestCache::Result::Result(
     const std::string& id,
@@ -193,12 +202,10 @@
 ItemSuggestCache::ItemSuggestCache(
     Profile* profile,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    base::RepeatingCallback<void()> on_results_updated,
-    base::TimeDelta min_time_between_updates)
+    base::RepeatingCallback<void()> on_results_updated)
     : made_request_(false),
       enabled_(kEnabled.Get()),
       server_url_(kServerUrl.Get()),
-      min_time_between_updates_(min_time_between_updates),
       multiple_queries_per_session_(
           app_list_features::IsSuggestedFilesEnabled() ||
           kMultipleQueriesPerSession.Get()),
@@ -239,14 +246,19 @@
   return base::ReplaceStringPlaceholders(kRequestBody, {model}, nullptr);
 }
 
+base::TimeDelta ItemSuggestCache::GetDelay() {
+  bool use_long_delay = profile_->GetPrefs()->GetBoolean(
+      chromeos::prefs::kLauncherUseLongContinueDelay);
+  return base::Minutes(use_long_delay ? kLongDelayMinutes.Get()
+                                      : kShortDelayMinutes);
+}
+
 void ItemSuggestCache::UpdateCache() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   update_start_time_ = base::TimeTicks::Now();
 
-  const auto& now = base::Time::Now();
-  if (now - time_of_last_update_ < min_time_between_updates_)
+  if (base::Time::Now() - GetLastRequestTime(profile_) < GetDelay())
     return;
-  time_of_last_update_ = now;
 
   // Make no requests and exit in these cases:
   // - Item suggest has been disabled via experiment.
@@ -298,6 +310,7 @@
 
   // Make a new request. This destroys any existing |url_loader_| which will
   // cancel that request if it is in-progress.
+  SetLastRequestTime(profile_, base::Time::Now());
   made_request_ = true;
   url_loader_ = MakeRequestLoader(token_info.token);
   url_loader_->SetRetryOptions(0, network::SimpleURLLoader::RETRY_NEVER);
@@ -321,6 +334,8 @@
       if (net_error == net::ERR_INSUFFICIENT_RESOURCES) {
         LogStatus(Status::kResponseTooLarge);
       } else {
+        // Note that requests ending in kNetError don't count towards
+        // ItemSuggest QPS, but the last request time is still updated.
         LogStatus(Status::kNetError);
       }
     } else {
@@ -365,6 +380,11 @@
     LogStatus(Status::kJsonConversionFailure);
   } else if (results->results.empty()) {
     LogStatus(Status::kNoResultsInResponse);
+    if (!results_) {
+      // Make sure that |results_| is non-null to indicate that an update was
+      // successful.
+      results_ = std::move(results.value());
+    }
   } else {
     LogStatus(Status::kOk);
     LogLatency(base::TimeTicks::Now() - update_start_time_);
diff --git a/chrome/browser/ui/app_list/search/files/item_suggest_cache.h b/chrome/browser/ui/app_list/search/files/item_suggest_cache.h
index 60ba6e3..d27420f 100644
--- a/chrome/browser/ui/app_list/search/files/item_suggest_cache.h
+++ b/chrome/browser/ui/app_list/search/files/item_suggest_cache.h
@@ -57,14 +57,14 @@
   ItemSuggestCache(
       Profile* profile,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      base::RepeatingCallback<void()> on_results_updated,
-      base::TimeDelta min_time_between_updates);
+      base::RepeatingCallback<void()> on_results_updated);
   ~ItemSuggestCache();
 
   ItemSuggestCache(const ItemSuggestCache&) = delete;
   ItemSuggestCache& operator=(const ItemSuggestCache&) = delete;
 
-  // Returns the results currently in the cache.
+  // Returns the results currently in the cache. A null result indicates that
+  // the cache has not been successfully updated.
   absl::optional<ItemSuggestCache::Results> GetResults();
 
   // Updates the cache by calling ItemSuggest.
@@ -119,9 +119,20 @@
   static constexpr base::FeatureParam<bool> kMultipleQueriesPerSession{
       &kExperiment, "multiple_queries_per_session", true};
 
+  // The minimum time between queries if a short delay is being used.
+  static constexpr int kShortDelayMinutes = 10;
+  // The minimum time between queries if a long delay is being used. If not set,
+  // the short delay value is used as a default instead.
+  static constexpr base::FeatureParam<int> kLongDelayMinutes{
+      &kExperiment, "long_delay_minutes", kShortDelayMinutes};
+
   // Returns the body for the itemsuggest request. Affected by |kExperiment|.
   std::string GetRequestBody();
 
+  // Calculates the minimum time required to wait after the previous request.
+  // Affected by |kExperiment|.
+  base::TimeDelta GetDelay();
+
   void OnTokenReceived(GoogleServiceAuthError error,
                        signin::AccessTokenInfo token_info);
   void OnSuggestionsReceived(const std::unique_ptr<std::string> json_response);
@@ -131,10 +142,6 @@
 
   absl::optional<Results> results_;
 
-  // Records the time of the last call to UpdateResults(), used to limit the
-  // number of queries to the ItemSuggest backend.
-  base::Time time_of_last_update_;
-
   // Start time for latency metrics.
   base::TimeTicks update_start_time_;
 
@@ -144,7 +151,6 @@
 
   const bool enabled_;
   const GURL server_url_;
-  const base::TimeDelta min_time_between_updates_;
   // Whether we should query item suggest more than once per session.
   const bool multiple_queries_per_session_;
 
diff --git a/chrome/browser/ui/app_list/search/files/item_suggest_cache_unittest.cc b/chrome/browser/ui/app_list/search/files/item_suggest_cache_unittest.cc
index 2026c2f..e55dead 100644
--- a/chrome/browser/ui/app_list/search/files/item_suggest_cache_unittest.cc
+++ b/chrome/browser/ui/app_list/search/files/item_suggest_cache_unittest.cc
@@ -211,7 +211,7 @@
       feature_, {{"enabled", "false"}});
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   itemSuggestCache->UpdateCache();
   task_environment_.RunUntilIdle();
   histogram_tester_.ExpectUniqueSample(
@@ -222,7 +222,7 @@
   profile_->GetPrefs()->SetBoolean(drive::prefs::kDisableDrive, true);
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   itemSuggestCache->UpdateCache();
   task_environment_.RunUntilIdle();
   histogram_tester_.ExpectUniqueSample(
@@ -235,7 +235,7 @@
       {{"server_url", "http://appsitemsuggest-pa.googleapis.com/v1/items"}});
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   itemSuggestCache->UpdateCache();
   task_environment_.RunUntilIdle();
   histogram_tester_.ExpectUniqueSample(
@@ -247,7 +247,7 @@
       feature_, {{"server_url", "https://foo.com"}});
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   itemSuggestCache->UpdateCache();
   task_environment_.RunUntilIdle();
   histogram_tester_.ExpectUniqueSample(
@@ -257,7 +257,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCacheServerNoAuthToken) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   itemSuggestCache->UpdateCache();
   task_environment_.RunUntilIdle();
   histogram_tester_.ExpectUniqueSample(
@@ -267,7 +267,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCacheInsufficientResourcesError) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -286,7 +286,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCacheNetError) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -305,7 +305,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCache5kkError) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -328,7 +328,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCache4kkError) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -351,7 +351,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCache3kkError) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -374,7 +374,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCacheEmptyResponse) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -391,7 +391,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCacheInvalidResponse) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -410,7 +410,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCacheConversionFailure) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -434,7 +434,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCacheConversionEmptyResults) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -459,7 +459,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCacheSavesResults) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
@@ -483,7 +483,7 @@
 TEST_F(ItemSuggestCacheTest, UpdateCacheSmallTimeBetweenUpdates) {
   std::unique_ptr<ItemSuggestCache> itemSuggestCache =
       std::make_unique<ItemSuggestCache>(profile_, shared_url_loader_factory_,
-                                         base::DoNothing(), base::Minutes(10));
+                                         base::DoNothing());
   identity_test_env_->MakePrimaryAccountAvailable(kEmail,
                                                   signin::ConsentLevel::kSync);
   identity_test_env_->SetAutomaticIssueOfAccessTokens(true);
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
index 2df74ec..2bbb6d4 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
@@ -48,6 +48,11 @@
 // ItemSuggestCache.
 constexpr base::TimeDelta kFirstUpdateDelay = base::Seconds(10);
 
+// The minimum number of results required to keep using the short delay. This
+// means that results are refreshed more often if there are enough high-quality
+// results returned.
+constexpr size_t kShortDelayQuota = 3u;
+
 // Outcome of a call to DriverZeroStateProvider::StartZeroState. These values
 // persist to logs. Entries should not be renumbered and numeric values should
 // never be reused.
@@ -86,6 +91,11 @@
                           latency);
 }
 
+void SetUseLongDelay(Profile* profile, bool use_long_delay) {
+  profile->GetPrefs()->SetBoolean(
+      chromeos::prefs::kLauncherUseLongContinueDelay, use_long_delay);
+}
+
 // Given an absolute path representing a file in the user's Drive, returns a
 // reparented version of the path within the user's drive fs mount.
 base::FilePath ReparentToDriveMount(
@@ -147,11 +157,7 @@
           profile,
           std::move(url_loader_factory),
           base::BindRepeating(&ZeroStateDriveProvider::OnCacheUpdated,
-                              base::Unretained(this)),
-          base::Minutes(base::GetFieldTrialParamByFeatureAsInt(
-              ash::features::kProductivityLauncher,
-              "itemsuggest_query_cooldown",
-              10))),
+                              base::Unretained(this))),
       max_last_modified_time_(base::Days(base::GetFieldTrialParamByFeatureAsInt(
           ash::features::kProductivityLauncher,
           "max_last_modified_time",
@@ -260,8 +266,6 @@
   if (!enabled_)
     return;
 
-  // TODO(crbug.com/1034842): Add query latency metrics.
-
   // Exit if drive fs isn't mounted, as we launch results via drive fs.
   if (!drive_service_ || !drive_service_->IsMounted()) {
     LogStatus(Status::kDriveFSNotMounted);
@@ -281,6 +285,12 @@
   if (!cache_results_) {
     LogStatus(Status::kNoResults);
     return;
+  } else if (cache_results_->results.empty()) {
+    LogStatus(Status::kNoResults);
+    // An empty but non-null value indicates that the cache was updated
+    // successfully, and no results were returned.
+    SetUseLongDelay(profile_, true);
+    return;
   }
 
   std::vector<std::string> item_ids;
@@ -357,6 +367,11 @@
 
   cache_results_.reset();
 
+  // If there aren't enough results, use a long delay and vice versa. Note that
+  // the delay is only updated if cache results are non-null, indicating that
+  // the cache has been updated.
+  SetUseLongDelay(profile_, provider_results.size() < kShortDelayQuota);
+
   SwapResults(&provider_results);
 
   LogStatus(Status::kOk);
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index 7c8bfcc..3a87fc78 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -67,6 +67,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/services/app_service/public/cpp/types_util.h"
@@ -512,8 +513,7 @@
   DCHECK(IsCameraAppEnabled());
   ChromeCameraAppUIDelegate::CameraAppDialog::ShowIntent(
       queries, arc::GetArcWindow(task_id));
-  apps::RecordAppLaunch(web_app::kCameraAppId,
-                        apps::mojom::LaunchSource::kFromArc);
+  apps::RecordAppLaunch(web_app::kCameraAppId, apps::LaunchSource::kFromArc);
 }
 
 void ChromeNewWindowClient::CloseCameraApp() {
diff --git a/chrome/browser/ui/ash/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
index 145afcf..5d74d8c 100644
--- a/chrome/browser/ui/ash/clipboard_history_browsertest.cc
+++ b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
@@ -1149,9 +1149,8 @@
 
 // Verify that the clipboard data history is recorded as expected in the
 // Multiuser environment.
-// TODO(http://crbug.com/1341601): Flakily crashes under ChromeOS
 IN_PROC_BROWSER_TEST_F(ClipboardHistoryMultiProfileBrowserTest,
-                       DISABLED_VerifyClipboardHistoryAcrossMultiUser) {
+                       VerifyClipboardHistoryAcrossMultiUser) {
   EXPECT_TRUE(GetClipboardItems().empty());
 
   // Store text when the user1 is active.
diff --git a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
index 5ef415b1..8ba2c5f 100644
--- a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
@@ -15,6 +15,7 @@
 #include "ash/public/cpp/desk_template.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/test/shell_test_api.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desk.h"
@@ -23,6 +24,8 @@
 #include "ash/wm/desks/templates/saved_desk_metrics_util.h"
 #include "ash/wm/desks/templates/saved_desk_test_util.h"
 #include "ash/wm/desks/templates/saved_desk_util.h"
+#include "ash/wm/overview/overview_grid.h"
+#include "ash/wm/overview/overview_session.h"
 #include "ash/wm/overview/overview_test_util.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -236,7 +239,7 @@
   web_app::AppId app_id = *ash::GetAppIdForSystemWebApp(profile, app_type);
   apps::AppLaunchParams params(
       app_id, apps::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW, apps::mojom::LaunchSource::kFromTest);
+      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
   params.restore_id = app_type == ash::SystemWebAppType::SETTINGS
                           ? kSettingsWindowId
                           : kHelpWindowId;
@@ -400,7 +403,8 @@
  public:
   DesksTemplatesClientTest() {
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{ash::features::kDesksTemplates},
+        /*enabled_features=*/{ash::features::kDesksTemplates,
+                              ash::features::kEnableSavedDesks},
         /*disabled_features=*/{ash::features::kDeskTemplateSync});
   }
   DesksTemplatesClientTest(const DesksTemplatesClientTest&) = delete;
@@ -2300,6 +2304,49 @@
   EXPECT_EQ(1, desks_controller->desks().size());
 }
 
+// Tests that if we've been in the library, then switched to a different desk,
+// and then save the desk, that the desk is closed. Regression test for
+// https://crbug.com/1329350.
+IN_PROC_BROWSER_TEST_F(DesksTemplatesClientTest,
+                       SystemUIReEnterLibraryAndSaveDesk) {
+  // Create a template that has a window because the "Save desk for later"
+  // button is not enabled on empty desks.
+  ash::ToggleOverview();
+  ash::WaitForOverviewEnterAnimation();
+
+  ClickSaveDeskAsTemplateButton();
+
+  ash::DesksController* desks_controller = ash::DesksController::Get();
+  const auto& desks = desks_controller->desks();
+  ASSERT_EQ(1ul, desks.size());
+  ASSERT_TRUE(desks[0]->ContainsAppWindows());
+
+  // Click on the "Use template" button to launch the template.
+  ClickFirstTemplateItem();
+
+  // Verify that a new desk has been created and activated, and that it has app
+  // windows.
+  ASSERT_EQ(2ul, desks.size());
+  ASSERT_EQ(1, desks_controller->GetActiveDeskIndex());
+  ASSERT_TRUE(desks_controller->active_desk()->ContainsAppWindows());
+
+  // Now save the desk. This should close the desk.
+  auto* overview_grid = ash::GetOverviewSession()->GetGridWithRootWindow(
+      ash::Shell::GetPrimaryRootWindow());
+  ASSERT_TRUE(overview_grid);
+  auto* save_desk_button = overview_grid->GetSaveDeskForLaterButton();
+  ASSERT_TRUE(save_desk_button);
+
+  // Wait for the bounds to finish animating.
+  ash::ShellTestApi().WaitForWindowFinishAnimating(
+      save_desk_button->GetWidget()->GetNativeWindow());
+  ClickButton(save_desk_button);
+  ash::WaitForDesksTemplatesUI();
+
+  // Verify that we're back to one desk.
+  EXPECT_EQ(1ul, desks.size());
+}
+
 // Tests trying to remove an invalid desk Id should return error.
 IN_PROC_BROWSER_TEST_F(DesksTemplatesClientTest, RemoveWithInvalidDeskId) {
   auto* desks_controller = ash::DesksController::Get();
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
index 99ecdca..444de4a 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
@@ -1694,10 +1694,9 @@
       last_loaded_extension_id(), extensions::ExtensionRegistry::ENABLED);
   EXPECT_TRUE(extension);
   apps::AppServiceProxyFactory::GetForProfile(profile())->LaunchAppWithParams(
-      apps::AppLaunchParams(extension->id(),
-                            apps::LaunchContainer::kLaunchContainerTab,
-                            WindowOpenDisposition::NEW_WINDOW,
-                            apps::mojom::LaunchSource::kFromTest));
+      apps::AppLaunchParams(
+          extension->id(), apps::LaunchContainer::kLaunchContainerTab,
+          WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest));
 
   EXPECT_EQ(++browsers, BrowserShortcutMenuItemCount(false));
   EXPECT_EQ(++tabs, BrowserShortcutMenuItemCount(true));
@@ -3083,7 +3082,7 @@
  public:
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures(
-        {ash::features::kFilesSWA, features::kEnableAllSystemWebApps}, {});
+        {ash::features::kFilesSWA, ash::features::kEnableAllSystemWebApps}, {});
     ShelfPlatformAppBrowserTest::SetUp();
   }
 
diff --git a/chrome/browser/ui/ash/shelf/shelf_controller_helper.cc b/chrome/browser/ui/ash/shelf/shelf_controller_helper.cc
index 45a10f3..7774b11a 100644
--- a/chrome/browser/ui/ash/shelf/shelf_controller_helper.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_controller_helper.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/types_util.h"
 #include "content/public/browser/navigation_entry.h"
@@ -191,7 +192,9 @@
 
   apps::AppLaunchParams params = CreateAppLaunchParamsWithEventFlags(
       profile_, extension, event_flags,
-      ShelfLaunchSourceToAppsLaunchSource(source), display_id);
+      apps::ConvertMojomLaunchSourceToLaunchSource(
+          ShelfLaunchSourceToAppsLaunchSource(source)),
+      display_id);
   if ((source == ash::LAUNCH_FROM_APP_LIST ||
        source == ash::LAUNCH_FROM_APP_LIST_SEARCH) &&
       app_id == extensions::kWebStoreAppId) {
diff --git a/chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.cc b/chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.cc
index 84e48f2..394f31d 100644
--- a/chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.cc
+++ b/chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "chrome/common/webui_url_constants.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/display/scoped_display_for_new_windows.h"
@@ -112,7 +113,7 @@
   // TODO(crbug/1113502): Plumb through better launch sources from callsites.
   apps::AppLaunchParams params = apps::CreateAppIdLaunchParamsWithEventFlags(
       app_id.value(), /*event_flags=*/0,
-      apps::mojom::LaunchSource::kFromChromeInternal, display_id,
+      apps::LaunchSource::kFromChromeInternal, display_id,
       /*fallback_container=*/
       web_app::ConvertDisplayModeToAppLaunchContainer(display_mode));
 
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index f8ce67b..0c55b60 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -1268,7 +1268,7 @@
               extension_app->id(),
               apps::LaunchContainer::kLaunchContainerWindow,
               WindowOpenDisposition::NEW_WINDOW,
-              apps::mojom::LaunchSource::kFromTest));
+              apps::LaunchSource::kFromTest));
   ASSERT_TRUE(app_window);
 
   DevToolsWindow* devtools_window =
@@ -1452,7 +1452,7 @@
               extension_app->id(),
               apps::LaunchContainer::kLaunchContainerWindow,
               WindowOpenDisposition::NEW_WINDOW,
-              apps::mojom::LaunchSource::kFromTest));
+              apps::LaunchSource::kFromTest));
   ASSERT_TRUE(app_window);
 
   // Apps launched in a window from the NTP have an extensions tab helper with
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index f23cf5af..c7edc84 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -701,7 +701,7 @@
     }
     apps::AppLaunchParams params = apps::AppLaunchParams(
         app_id, launch_container, WindowOpenDisposition::NEW_WINDOW,
-        apps::mojom::LaunchSource::kFromKeyboard);
+        apps::LaunchSource::kFromKeyboard);
     apps::AppServiceProxyFactory::GetForProfile(profile)
         ->BrowserAppLauncher()
         ->LaunchAppWithParams(std::move(params));
@@ -714,7 +714,7 @@
   if (extension && extension->is_hosted_app()) {
     const auto app_launch_params = CreateAppLaunchParamsUserContainer(
         profile, extension, WindowOpenDisposition::NEW_WINDOW,
-        apps::mojom::LaunchSource::kFromKeyboard);
+        apps::LaunchSource::kFromKeyboard);
     OpenApplicationWindow(
         profile, app_launch_params,
         extensions::AppLaunchInfo::GetLaunchWebURL(extension));
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index 7ffd5ef..b014f76 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -139,13 +139,14 @@
     const std::u16string& device_identifier,
     content::BluetoothDelegate::PairPromptCallback close_callback);
 
-// Shows the dialog to let user confirm to pair with the device
-// identified by |device_identifier|. |device_identifier| is the most
-// appropriate string to display to the user for device identification
-// (e.g. name, MAC address).
+// Show a user prompt for pairing a Bluetooth device. |device_identifier|
+// is the most appropriate string to display for device identification
+// (e.g. name, MAC address). The |pin| is displayed (if specified),
+// so the user can confirm a matching value is displayed on the device.
 void ShowBluetoothDevicePairConfirmDialog(
     content::WebContents* web_contents,
     const std::u16string& device_identifier,
+    const absl::optional<std::u16string> pin,
     content::BluetoothDelegate::PairPromptCallback close_callback);
 #endif  // PAIR_BLUETOOTH_ON_DEMAND()
 
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
index 8fd176c7..dc922914 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
@@ -75,7 +75,7 @@
           ->LaunchAppWithParams(apps::AppLaunchParams(
               app_->id(), apps::LaunchContainer::kLaunchContainerNone,
               WindowOpenDisposition::NEW_WINDOW,
-              apps::mojom::LaunchSource::kFromTest));
+              apps::LaunchSource::kFromTest));
       app_loaded_observer.Wait();
     }
   }
diff --git a/chrome/browser/ui/extensions/app_launch_params.cc b/chrome/browser/ui/extensions/app_launch_params.cc
index 6c41c464..bc6f0b7c 100644
--- a/chrome/browser/ui/extensions/app_launch_params.cc
+++ b/chrome/browser/ui/extensions/app_launch_params.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
@@ -19,7 +18,7 @@
     Profile* profile,
     const extensions::Extension* extension,
     WindowOpenDisposition disposition,
-    apps::mojom::LaunchSource launch_source) {
+    apps::LaunchSource launch_source) {
   // Look up the app preference to find out the right launch container. Default
   // is to launch as a regular tab.
   apps::LaunchContainer container =
@@ -32,7 +31,7 @@
     Profile* profile,
     const extensions::Extension* extension,
     int event_flags,
-    apps::mojom::LaunchSource launch_source,
+    apps::LaunchSource launch_source,
     int64_t display_id) {
   apps::LaunchContainer fallback_container =
       extensions::GetLaunchContainer(ExtensionPrefs::Get(profile), extension);
diff --git a/chrome/browser/ui/extensions/app_launch_params.h b/chrome/browser/ui/extensions/app_launch_params.h
index d2424d9..e760812 100644
--- a/chrome/browser/ui/extensions/app_launch_params.h
+++ b/chrome/browser/ui/extensions/app_launch_params.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_EXTENSIONS_APP_LAUNCH_PARAMS_H_
 
 #include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/mojom/types.mojom-forward.h"
 #include "ui/base/window_open_disposition.h"
 
@@ -21,7 +22,7 @@
     Profile* profile,
     const extensions::Extension* extension,
     WindowOpenDisposition disposition,
-    apps::mojom::LaunchSource launch_source);
+    apps::LaunchSource launch_source);
 
 // Helper to create AppLaunchParams, falling back to
 // extensions::GetLaunchContainer() with no modifiers.
@@ -29,7 +30,7 @@
     Profile* profile,
     const extensions::Extension* extension,
     int event_flags,
-    apps::mojom::LaunchSource launch_source,
+    apps::LaunchSource launch_source,
     int64_t display_id);
 
 #endif  // CHROME_BROWSER_UI_EXTENSIONS_APP_LAUNCH_PARAMS_H_
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc
index 86087bb2..243035b 100644
--- a/chrome/browser/ui/extensions/application_launch.cc
+++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -472,8 +472,7 @@
   apps::AppLaunchParams launch_params(
       std::string(),  // this is a URL app. No app id.
       apps::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW,
-      apps::mojom::LaunchSource::kFromCommandLine);
+      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromCommandLine);
   launch_params.override_url = url;
 
   WebContents* tab = OpenApplicationWindow(profile, launch_params, url);
diff --git a/chrome/browser/ui/extensions/application_launch_browsertest.cc b/chrome/browser/ui/extensions/application_launch_browsertest.cc
index 062c69a..859a3c8 100644
--- a/chrome/browser/ui/extensions/application_launch_browsertest.cc
+++ b/chrome/browser/ui/extensions/application_launch_browsertest.cc
@@ -34,10 +34,10 @@
   EXPECT_EQ(display1, screen->GetDisplayForNewWindows().id());
 
   // Create browser2 on display 2.
-  apps::AppLaunchParams params(
-      "app_id", apps::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW,
-      apps::mojom::LaunchSource::kFromAppListGrid, display2);
+  apps::AppLaunchParams params("app_id",
+                               apps::LaunchContainer::kLaunchContainerWindow,
+                               WindowOpenDisposition::NEW_WINDOW,
+                               apps::LaunchSource::kFromAppListGrid, display2);
   Browser* browser2 =
       CreateApplicationWindow(browser()->profile(), params, GURL());
   gfx::NativeWindow window2 = browser2->window()->GetNativeWindow();
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index 5e00f2b2..cf07b09 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -329,6 +329,7 @@
   AutocompleteActionPredictor::Action recommended_action =
       AutocompleteActionPredictor::ACTION_NONE;
   if (user_input_in_progress) {
+    content::WebContents* web_contents = controller_->GetWebContents();
     AutocompleteActionPredictor* action_predictor =
         predictors::AutocompleteActionPredictorFactory::GetForProfile(profile_);
     action_predictor->RegisterTransitionalMatches(user_text, result);
@@ -338,8 +339,8 @@
     // but only get it if the user has actually typed something to avoid
     // constructing it before it's needed. Note: This event is triggered as
     // part of startup when the initial tab transitions to the start page.
-    recommended_action =
-        action_predictor->RecommendAction(user_text, current_match);
+    recommended_action = action_predictor->RecommendAction(
+        user_text, current_match, web_contents);
   }
 
   UMA_HISTOGRAM_ENUMERATION("AutocompleteActionPredictor.Action",
diff --git a/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
index f6adf28..be2a1fa 100644
--- a/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
+++ b/chrome/browser/ui/permission_bubble/permission_bubble_browser_test_util.cc
@@ -85,7 +85,7 @@
 
   apps::AppLaunchParams params(
       extension->id(), apps::LaunchContainer::kLaunchContainerPanelDeprecated,
-      WindowOpenDisposition::NEW_WINDOW, apps::mojom::LaunchSource::kFromTest);
+      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
 
   content::WebContents* app_contents =
       apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
diff --git a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
index 5a09502..792aad6 100644
--- a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
+++ b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
@@ -110,7 +110,7 @@
           ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
               settings_app_id, apps::LaunchContainer::kLaunchContainerWindow,
               WindowOpenDisposition::NEW_WINDOW,
-              apps::mojom::LaunchSource::kFromCommandLine));
+              apps::LaunchSource::kFromCommandLine));
   ash::FlushSystemWebAppLaunchesForTesting(browser()->profile());
   EXPECT_EQ(contents,
             settings_browser->tab_strip_model()->GetActiveWebContents());
diff --git a/chrome/browser/ui/views/bluetooth_device_credentials_view.h b/chrome/browser/ui/views/bluetooth_device_credentials_view.h
index f720f2b..9af503a 100644
--- a/chrome/browser/ui/views/bluetooth_device_credentials_view.h
+++ b/chrome/browser/ui/views/bluetooth_device_credentials_view.h
@@ -43,6 +43,8 @@
   views::View* GetInitiallyFocusedView() override;
 
  private:
+  // Runs the |close_callback_| with the PairPromptResult if the dialog is
+  // accepted.
   void OnDialogAccepted();
   // TextfieldController:
   void ContentsChanged(views::Textfield* sender,
diff --git a/chrome/browser/ui/views/bluetooth_device_pair_confirm_view.cc b/chrome/browser/ui/views/bluetooth_device_pair_confirm_view.cc
index cbb6d73..f17d117 100644
--- a/chrome/browser/ui/views/bluetooth_device_pair_confirm_view.cc
+++ b/chrome/browser/ui/views/bluetooth_device_pair_confirm_view.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/views/bluetooth_device_pair_confirm_view.h"
 
-#include <cwctype>
-
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
@@ -15,7 +13,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/color/color_id.h"
-#include "ui/compositor/layer.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/flex_layout.h"
@@ -27,9 +24,10 @@
 void ShowBluetoothDevicePairConfirmDialog(
     content::WebContents* web_contents,
     const std::u16string& device_identifier,
+    const absl::optional<std::u16string> pin,
     BluetoothDelegate::PairPromptCallback close_callback) {
   // This dialog owns itself. DialogDelegateView will delete |dialog| instance.
-  auto* dialog = new BluetoothDevicePairConfirmView(device_identifier,
+  auto* dialog = new BluetoothDevicePairConfirmView(device_identifier, pin,
                                                     std::move(close_callback));
   constrained_window::ShowWebModalDialogViews(dialog, web_contents);
 }
@@ -38,8 +36,10 @@
 
 BluetoothDevicePairConfirmView::BluetoothDevicePairConfirmView(
     const std::u16string& device_identifier,
+    const absl::optional<std::u16string> pin,
     BluetoothDelegate::PairPromptCallback close_callback)
-    : close_callback_(std::move(close_callback)) {
+    : close_callback_(std::move(close_callback)),
+      display_pin_(pin.has_value()) {
   SetModalType(ui::MODAL_TYPE_CHILD);
   set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
       views::DialogContentType::kText, views::DialogContentType::kText));
@@ -54,30 +54,14 @@
   SetCancelCallback(base::BindOnce(canceled, base::Unretained(this)));
   SetCloseCallback(base::BindOnce(canceled, base::Unretained(this)));
   SetButtonEnabled(ui::DIALOG_BUTTON_OK, true);
-  InitControls(device_identifier);
+  InitControls(device_identifier, pin);
 }
 
 BluetoothDevicePairConfirmView::~BluetoothDevicePairConfirmView() = default;
 
 void BluetoothDevicePairConfirmView::InitControls(
-    const std::u16string& device_identifier) {
-  //
-  // Create the following layout:
-  //
-  // ┌───────────────┬──────────────────────────────────────────────────────────────┐
-  // │               │ Pair Confirmation                                            │
-  // │ ┌───────────┐ │                                                              │
-  // │ │           │ │ Bluetooth device <device name> would like permission to pair.│
-  // │ │ Bluetooth │ │                                                              │
-  // │ │    icon   │ │                                                              │
-  // │ │           │ │                                                              │
-  // │ └───────────┘ │                                        ┌──────┐  ┌────────┐  │
-  // │               │                                        │  OK  │  │ Cancel │  │
-  // │               │                                        └──────┘  └────────┘  │
-  // └───────────────┴──────────────────────────────────────────────────────────────┘
-  //
-  //
-
+    const std::u16string& device_identifier,
+    const absl::optional<std::u16string> pin) {
   SetLayoutManager(std::make_unique<views::FlexLayout>())
       ->SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
 
@@ -96,6 +80,7 @@
   icon_view_ = AddChildView(std::move(icon_view));
 
   auto contents_wrapper = std::make_unique<views::View>();
+
   contents_wrapper->SetProperty(
       views::kMarginsKey,
       gfx::Insets::VH(vertical_spacing, horizontal_spacing));
@@ -109,13 +94,20 @@
                                views::MaximumFlexSizeRule::kUnbounded));
 
   {
-    auto passkey_prompt_label =
-        std::make_unique<views::Label>(l10n_util::GetStringFUTF16(
-            IDS_BLUETOOTH_DEVICE_PAIR_CONFIRM_LABEL, device_identifier));
+    // Display the pin if provided, so the user can verify if a matching
+    // pin is shown on the device. Otherwise, user verification is solely
+    // based upon recognition of the device identifier.
+    auto prompt_label =
+        display_pin_
+            ? std::make_unique<views::Label>(l10n_util::GetStringFUTF16(
+                  IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_LABEL, pin.value(),
+                  device_identifier))
+            : std::make_unique<views::Label>(l10n_util::GetStringFUTF16(
+                  IDS_BLUETOOTH_DEVICE_PAIR_CONFIRM_LABEL, device_identifier));
 
-    passkey_prompt_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    passkey_prompt_label->SetMultiLine(true);
-    contents_wrapper->AddChildView(std::move(passkey_prompt_label));
+    prompt_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    prompt_label->SetMultiLine(true);
+    contents_wrapper->AddChildView(std::move(prompt_label));
   }
 
   contents_wrapper_ = AddChildView(std::move(contents_wrapper));
@@ -129,7 +121,10 @@
 }
 
 std::u16string BluetoothDevicePairConfirmView::GetWindowTitle() const {
-  return l10n_util::GetStringUTF16(IDS_BLUETOOTH_DEVICE_PAIR_CONFIRM_TITLE);
+  return display_pin_ ? l10n_util::GetStringUTF16(
+                            IDS_BLUETOOTH_DEVICE_PASSKEY_CONFIRM_TITLE)
+                      : l10n_util::GetStringUTF16(
+                            IDS_BLUETOOTH_DEVICE_PAIR_CONFIRM_TITLE);
 }
 
 void BluetoothDevicePairConfirmView::OnDialogAccepted() {
diff --git a/chrome/browser/ui/views/bluetooth_device_pair_confirm_view.h b/chrome/browser/ui/views/bluetooth_device_pair_confirm_view.h
index d17b3db0..74718f0 100644
--- a/chrome/browser/ui/views/bluetooth_device_pair_confirm_view.h
+++ b/chrome/browser/ui/views/bluetooth_device_pair_confirm_view.h
@@ -9,19 +9,17 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/ui/browser_dialogs.h"
 #include "content/public/browser/bluetooth_delegate.h"
-#include "ui/views/controls/textfield/textfield.h"
-#include "ui/views/controls/textfield/textfield_controller.h"
 #include "ui/views/window/dialog_delegate.h"
 
-// A dialog allowing the user to enter Bluetooth credentials (i.e. a PIN).
-class BluetoothDevicePairConfirmView : public views::DialogDelegateView,
-                                       public views::TextfieldController {
+// A dialog allowing the user to confirm Bluetooth device pairing with or
+// without PIN being displayed.
+class BluetoothDevicePairConfirmView : public views::DialogDelegateView {
  public:
   METADATA_HEADER(BluetoothDevicePairConfirmView);
   BluetoothDevicePairConfirmView(
       const std::u16string& device_identifier,
+      const absl::optional<std::u16string> pin,
       content::BluetoothDelegate::PairPromptCallback close_callback);
   BluetoothDevicePairConfirmView(const BluetoothDevicePairConfirmView&) =
       delete;
@@ -30,20 +28,24 @@
   ~BluetoothDevicePairConfirmView() override;
 
   // Initialize the controls on the dialog.
-  void InitControls(const std::u16string& device_identifier);
+  void InitControls(const std::u16string& device_identifier,
+                    const absl::optional<std::u16string> pin);
 
   // View:
   gfx::Size CalculatePreferredSize() const override;
 
+  // WidgetDelegate:
   std::u16string GetWindowTitle() const override;
 
  private:
+  // Runs the |close_callback_| with the PairPromptResult if the dialog is
+  // accepted.
   void OnDialogAccepted();
 
   content::BluetoothDelegate::PairPromptCallback close_callback_;
-  raw_ptr<views::Textfield> passkey_text_ = nullptr;
   raw_ptr<views::View> icon_view_ = nullptr;
   raw_ptr<views::View> contents_wrapper_ = nullptr;
+  bool display_pin_ = false;
   base::WeakPtrFactory<BluetoothDevicePairConfirmView> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ui/views/bluetooth_device_pair_confirm_view_browsertest.cc b/chrome/browser/ui/views/bluetooth_device_pair_confirm_view_browsertest.cc
index 4dd96851..e22eeb39 100644
--- a/chrome/browser/ui/views/bluetooth_device_pair_confirm_view_browsertest.cc
+++ b/chrome/browser/ui/views/bluetooth_device_pair_confirm_view_browsertest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
+
 #include "base/callback_helpers.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/browser.h"
@@ -17,10 +19,13 @@
 namespace {
 
 constexpr char16_t kDeviceIdentifier[] = u"test-device";
+constexpr char16_t kPasskey[] = u"123456";
 
 }  // namespace
 
-class BluetoothDevicePairConfirmViewBrowserTest : public DialogBrowserTest {
+class BluetoothDevicePairConfirmViewBrowserTest
+    : public DialogBrowserTest,
+      public testing::WithParamInterface<bool> {
  public:
   BluetoothDevicePairConfirmViewBrowserTest() = default;
   BluetoothDevicePairConfirmViewBrowserTest(
@@ -29,16 +34,24 @@
       const BluetoothDevicePairConfirmViewBrowserTest&) = delete;
   ~BluetoothDevicePairConfirmViewBrowserTest() override = default;
 
+  bool DisplayPasskey() { return GetParam(); }
+
   void ShowUi(const std::string& name) override {
+    auto passkey = DisplayPasskey() ? absl::optional<std::u16string>(kPasskey)
+                                    : absl::nullopt;
+
     chrome::ShowBluetoothDevicePairConfirmDialog(
         browser()->tab_strip_model()->GetActiveWebContents(), kDeviceIdentifier,
-        base::NullCallback());
+        passkey, base::NullCallback());
   }
 };
 
-IN_PROC_BROWSER_TEST_F(BluetoothDevicePairConfirmViewBrowserTest,
+IN_PROC_BROWSER_TEST_P(BluetoothDevicePairConfirmViewBrowserTest,
                        InvokeUi_default) {
   ShowAndVerifyUi();
 }
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         BluetoothDevicePairConfirmViewBrowserTest,
+                         testing::Bool());
 #endif  // PAIR_BLUETOOTH_ON_DEMAND()
diff --git a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
index 8180d17..0c0fa50 100644
--- a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
+++ b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
@@ -207,7 +207,7 @@
       ->LaunchAppWithParams(apps::AppLaunchParams(
           extension->id(), apps::LaunchContainer::kLaunchContainerWindow,
           WindowOpenDisposition::NEW_FOREGROUND_TAB,
-          apps::mojom::LaunchSource::kFromTest));
+          apps::LaunchSource::kFromTest));
 
   // Check that the new browser has an app name.
   // The launch should have created a new browser.
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
index 1943782..716cb74 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
@@ -440,8 +440,8 @@
       animation_mode_reset_;
 };
 
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-// Flaky on Mac and Win. See https://crbug.com/1330302.
+#if BUILDFLAG(IS_MAC)
+// Flaky on Mac. See https://crbug.com/1330302.
 #define MAYBE_InvokeUi_default DISABLED_InvokeUi_default
 #else
 #define MAYBE_InvokeUi_default InvokeUi_default
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index ab09ec6a..9336243b 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -449,7 +449,7 @@
 
   widget_observation_.Reset();
 
-  if (is_dragging_window())
+  if (current_state_ == DragState::kDraggingWindow)
     GetAttachedBrowserWidget()->EndMoveLoop();
 
   if (event_source_ == EVENT_SOURCE_TOUCH) {
@@ -578,12 +578,28 @@
                                : nullptr;
 }
 
-bool TabDragController::IsDraggingTab(content::WebContents* contents) {
-  for (auto& drag_data : drag_data_) {
-    if (drag_data.contents == contents)
-      return true;
-  }
-  return false;
+void TabDragController::TabWasAdded() {
+  // Stop dragging when a new tab is added and dragging a window, unless we're
+  // doing so ourselves (e.g. while attaching to a new browser). Doing otherwise
+  // results in a confusing state if the user attempts to reattach. We could
+  // allow this and update ourselves during the add, but this comes up
+  // infrequently enough that it's not worth the complexity.
+  if (current_state_ == DragState::kDraggingWindow && !is_mutating_)
+    EndDrag(END_DRAG_COMPLETE);
+}
+
+void TabDragController::OnTabWillBeRemoved(content::WebContents* contents) {
+  // End the drag before we remove a tab that's being dragged, to avoid
+  // complex special cases that could result.
+  if (!CanRemoveTabDuringDrag(contents))
+    EndDrag(END_DRAG_COMPLETE);
+}
+
+bool TabDragController::CanRemoveTabDuringDrag(
+    content::WebContents* contents) const {
+  // Tab removal can happen without interrupting dragging only if either a) the
+  // tab isn't part of the drag or b) we're doing the removal ourselves.
+  return !IsDraggingTab(contents) || is_mutating_;
 }
 
 void TabDragController::Drag(const gfx::Point& point_in_screen) {
@@ -2108,6 +2124,14 @@
   }
 }
 
+bool TabDragController::IsDraggingTab(content::WebContents* contents) const {
+  for (auto& drag_data : drag_data_) {
+    if (drag_data.contents == contents)
+      return true;
+  }
+  return false;
+}
+
 views::Widget* TabDragController::GetAttachedBrowserWidget() {
   return attached_context_->GetWidget();
 }
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h
index b22df90..33b4ded 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.h
@@ -118,27 +118,26 @@
   // Returns true if a drag started.
   bool started_drag() const { return current_state_ != DragState::kNotStarted; }
 
-  // Returns true if mutating the TabStripModel.
-  bool is_mutating() const { return is_mutating_; }
-
-  // Returns true if we've detached from a tabstrip and are running a nested
-  // move message loop.
-  bool is_dragging_window() const {
-    return current_state_ == DragState::kDraggingWindow;
-  }
-
   // Returns the tab group being dragged, if any. Will only return a value if
   // the user is dragging a tab group header, not an individual tab or tabs from
   // a group.
   const absl::optional<tab_groups::TabGroupId>& group() const { return group_; }
 
-  // Returns true if currently dragging a tab with |contents|.
-  bool IsDraggingTab(content::WebContents* contents);
-
   bool IsRemovingLastTabForRevert() const {
     return is_removing_last_tab_for_revert_;
   }
 
+  // Call when a tab was just added to the attached tabstrip. May end the drag.
+  void TabWasAdded();
+
+  // Call when `contents` is about to be removed from the attached tabstrip. May
+  // end the drag.
+  void OnTabWillBeRemoved(content::WebContents* contents);
+
+  // Returns true if removing `contents` from the attached tabstrip is fine, and
+  // false if that would be problematic for the drag session.
+  bool CanRemoveTabDuringDrag(content::WebContents* contents) const;
+
   // Invoked to drag to the new location, in screen coordinates.
   void Drag(const gfx::Point& point_in_screen);
 
@@ -454,6 +453,9 @@
     return header_drag_ ? drag_data_.size() - 1 : drag_data_.size();
   }
 
+  // Returns true if currently dragging a tab with |contents|.
+  bool IsDraggingTab(content::WebContents* contents) const;
+
   // Returns the index of the first Tab, since the first dragging view may
   // instead be a TabGroupHeader.
   int first_tab_index() { return header_drag_ ? 1 : 0; }
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index e97d79e..1c9534a 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -196,17 +196,19 @@
     return drag_controller_ && drag_controller_->started_drag();
   }
 
-  bool IsMutating() const {
-    return drag_controller_ && drag_controller_->is_mutating();
+  void TabWasAdded() {
+    if (drag_controller_)
+      drag_controller_->TabWasAdded();
   }
 
-  bool IsDraggingWindow() const {
-    return drag_controller_ && drag_controller_->is_dragging_window();
+  void OnTabWillBeRemoved(content::WebContents* contents) {
+    if (drag_controller_)
+      drag_controller_->OnTabWillBeRemoved(contents);
   }
 
-  bool IsDraggingTab(content::WebContents* contents) const {
-    return contents && drag_controller_ &&
-           drag_controller_->IsDraggingTab(contents);
+  bool CanRemoveTabIfDragging(content::WebContents* contents) const {
+    return drag_controller_ ? drag_controller_->CanRemoveTabDuringDrag(contents)
+                            : true;
   }
 
   void MaybeStartDrag(TabSlotView* source,
@@ -1000,17 +1002,11 @@
   for (TabStripObserver& observer : observers_)
     observer.OnTabAdded(model_index);
 
-  // Stop dragging when a new tab is added and dragging a window. Doing
-  // otherwise results in a confusing state if the user attempts to reattach. We
-  // could allow this and make TabDragController update itself during the add,
-  // but this comes up infrequently enough that it's not worth the complexity.
-  //
   // At the start of AddTabAt() the model and tabs are out sync. Any queries to
   // find a tab given a model index can go off the end of |tabs_|. As such, it
   // is important that we complete the drag *after* adding the tab so that the
   // model and tabstrip are in sync.
-  if (!drag_context_->IsMutating() && drag_context_->IsDraggingWindow())
-    EndDrag(END_DRAG_COMPLETE);
+  drag_context_->TabWasAdded();
 
   Profile* profile = controller_->GetProfile();
   if (profile) {
@@ -1049,11 +1045,10 @@
 void TabStrip::RemoveTabAt(content::WebContents* contents,
                            int model_index,
                            bool was_active) {
-  // OnTabWillBeRemoved should have ended any ongoing drags already - unless the
-  // call is coming from inside the house! (i.e. the TabDragController is doing
-  // the removing as part of reverting a drag)
-  DCHECK(!drag_context_->IsDragSessionActive() ||
-         drag_context_->GetDragController()->is_mutating());
+  // OnTabWillBeRemoved should have ended any ongoing drags containing
+  // `contents` already - unless the call is coming from inside the house! (i.e.
+  // the TabDragController is doing the removing as part of reverting a drag)
+  DCHECK(drag_context_->CanRemoveTabIfDragging(contents));
   tab_container_->RemoveTab(model_index, was_active);
 
   UpdateHoverCard(nullptr, HoverCardUpdateType::kTabRemoved);
@@ -1066,10 +1061,7 @@
 
 void TabStrip::OnTabWillBeRemoved(content::WebContents* contents,
                                   int model_index) {
-  // End the drag before we remove a tab that's being dragged, to avoid complex
-  // special cases that could result.
-  if (!drag_context_->IsMutating() && drag_context_->IsDraggingTab(contents))
-    EndDrag(END_DRAG_COMPLETE);
+  drag_context_->OnTabWillBeRemoved(contents);
 }
 
 void TabStrip::SetTabData(int model_index, TabRendererData data) {
diff --git a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
index 45e2747b..09f596e6 100644
--- a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
+++ b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
@@ -561,7 +561,7 @@
     auto launch_params = apps::AppLaunchParams(
         app_id, apps::LaunchContainer::kLaunchContainerWindow,
         WindowOpenDisposition::CURRENT_TAB,
-        apps::mojom::LaunchSource::kFromAppListGrid);
+        apps::LaunchSource::kFromAppListGrid);
 
     content::TestNavigationObserver navigation_observer(
         installation_->GetAppUrl());
@@ -677,7 +677,7 @@
     auto launch_params = apps::AppLaunchParams(
         app_id2, apps::LaunchContainer::kLaunchContainerWindow,
         WindowOpenDisposition::CURRENT_TAB,
-        apps::mojom::LaunchSource::kFromAppListGrid);
+        apps::LaunchSource::kFromAppListGrid);
     content::WebContents* web_contents =
         apps::AppServiceProxyFactory::GetForProfile(profile2)
             ->BrowserAppLauncher()
@@ -689,7 +689,7 @@
     auto launch_params = apps::AppLaunchParams(
         app_id1, apps::LaunchContainer::kLaunchContainerWindow,
         WindowOpenDisposition::CURRENT_TAB,
-        apps::mojom::LaunchSource::kFromAppListGrid);
+        apps::LaunchSource::kFromAppListGrid);
     content::WebContents* web_contents =
         apps::AppServiceProxyFactory::GetForProfile(profile1)
             ->BrowserAppLauncher()
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
index 60fb018..c06f40a 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -198,7 +198,7 @@
           ->BrowserAppLauncher()
           ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
               app_id, apps::LaunchContainer::kLaunchContainerWindow,
-              disposition, apps::mojom::LaunchSource::kFromTest));
+              disposition, apps::LaunchSource::kFromTest));
   EXPECT_TRUE(web_contents);
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
   EXPECT_TRUE(AppBrowserController::IsForWebApp(browser, app_id));
@@ -225,7 +225,7 @@
           ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
               app_id, apps::LaunchContainer::kLaunchContainerTab,
               WindowOpenDisposition::NEW_FOREGROUND_TAB,
-              apps::mojom::LaunchSource::kFromTest));
+              apps::LaunchSource::kFromTest));
   DCHECK(web_contents);
 
   EXPECT_EQ(app_id, *WebAppTabHelper::GetAppId(web_contents));
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index a2cb9b3..4f29db14 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -1877,7 +1877,7 @@
   const AppId app_id = InstallPWA(app_url);
   apps::AppLaunchParams params(
       app_id, apps::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW, apps::mojom::LaunchSource::kFromTest);
+      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
 
   BrowserHandler handler(nullptr, std::string());
   handler.Close();
diff --git a/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc b/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
index abb2cb3..bf6383e 100644
--- a/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
@@ -167,7 +167,7 @@
 
   apps::AppLaunchParams params(
       app_id, apps::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW, apps::mojom::LaunchSource::kFromTest);
+      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
   content::WebContents* contents =
       apps::AppServiceProxyFactory::GetForProfile(profile())
           ->BrowserAppLauncher()
diff --git a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
index 3a2f3fc0..280221f 100644
--- a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
@@ -189,7 +189,7 @@
                            apps::LaunchContainer::kLaunchContainerWindow) {
     web_contents_ = LaunchApplication(
         profile(), app_id, expected_launch_url, launch_container,
-        apps::mojom::LaunchSource::kFromFileManager, files);
+        apps::LaunchSource::kFromFileManager, files);
     destroyed_watcher_ =
         std::make_unique<content::WebContentsDestroyedWatcher>(web_contents_);
   }
@@ -255,8 +255,7 @@
       const GURL& expected_launch_url,
       const apps::LaunchContainer launch_container =
           apps::LaunchContainer::kLaunchContainerWindow,
-      const apps::mojom::LaunchSource launch_source =
-          apps::mojom::LaunchSource::kFromTest,
+      const apps::LaunchSource launch_source = apps::LaunchSource::kFromTest,
       const std::vector<base::FilePath>& files =
           std::vector<base::FilePath>()) {
     apps::AppLaunchParams params(app_id, launch_container,
diff --git a/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc b/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
index aec877f..4d16242 100644
--- a/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
+++ b/chrome/browser/ui/web_applications/web_app_guest_session_browsertest_chromeos.cc
@@ -46,10 +46,9 @@
       ->InstallSystemAppsForTesting();
 
   Profile* profile = browser()->profile();
-  apps::AppLaunchParams params(web_app::kOsSettingsAppId,
-                               apps::LaunchContainer::kLaunchContainerWindow,
-                               WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                               apps::mojom::LaunchSource::kFromTest);
+  apps::AppLaunchParams params(
+      web_app::kOsSettingsAppId, apps::LaunchContainer::kLaunchContainerWindow,
+      WindowOpenDisposition::NEW_FOREGROUND_TAB, apps::LaunchSource::kFromTest);
 
   content::WebContents* contents =
       apps::AppServiceProxyFactory::GetForProfile(profile)
diff --git a/chrome/browser/ui/web_applications/web_app_launch_manager.cc b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
index 3d9ddcf..73e2b21e 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_manager.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
@@ -77,21 +77,20 @@
                 protocol_handler_launch_url.has_value() + !launch_files.empty(),
             1);
 
-  apps::mojom::LaunchSource launch_source =
-      apps::mojom::LaunchSource::kFromCommandLine;
+  apps::LaunchSource launch_source = apps::LaunchSource::kFromCommandLine;
 
   if (url_handler_launch_url.has_value()) {
-    launch_source = apps::mojom::LaunchSource::kFromUrlHandler;
+    launch_source = apps::LaunchSource::kFromUrlHandler;
   } else if (!launch_files.empty()) {
     DCHECK(file_launch_url.has_value());
-    launch_source = apps::mojom::LaunchSource::kFromFileManager;
+    launch_source = apps::LaunchSource::kFromFileManager;
   }
 
   if (base::FeatureList::IsEnabled(features::kDesktopPWAsRunOnOsLogin) &&
       command_line.HasSwitch(switches::kAppRunOnOsLoginMode)) {
-    launch_source = apps::mojom::LaunchSource::kFromOsLogin;
+    launch_source = apps::LaunchSource::kFromOsLogin;
   } else if (protocol_handler_launch_url.has_value()) {
-    launch_source = apps::mojom::LaunchSource::kFromProtocolHandler;
+    launch_source = apps::LaunchSource::kFromProtocolHandler;
   }
 
   apps::AppLaunchParams params(
diff --git a/chrome/browser/ui/web_applications/web_app_launch_manager_unittest.cc b/chrome/browser/ui/web_applications/web_app_launch_manager_unittest.cc
index 3807db4..b3edf3e 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_manager_unittest.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_manager_unittest.cc
@@ -76,7 +76,7 @@
     apps::AppLaunchParams params(kTestAppId,
                                  apps::LaunchContainer::kLaunchContainerWindow,
                                  WindowOpenDisposition::NEW_WINDOW,
-                                 apps::mojom::LaunchSource::kFromCommandLine);
+                                 apps::LaunchSource::kFromCommandLine);
 
     params.current_directory = base::FilePath(kCurrentDirectory);
     params.command_line = command_line;
@@ -159,8 +159,7 @@
   apps::AppLaunchParams expected_results =
       CreateLaunchParams(command_line, std::vector<base::FilePath>(),
                          absl::nullopt, protocol_handler_launch_url);
-  expected_results.launch_source =
-      apps::mojom::LaunchSource::kFromProtocolHandler;
+  expected_results.launch_source = apps::LaunchSource::kFromProtocolHandler;
 
   testing::StrictMock<MockWebAppLaunchManager> manager(profile());
   EXPECT_CALL(manager, LaunchWebApplication(testing::_, testing::_))
@@ -192,8 +191,7 @@
   apps::AppLaunchParams expected_results =
       CreateLaunchParams(command_line, std::vector<base::FilePath>(),
                          absl::nullopt, protocol_handler_launch_url);
-  expected_results.launch_source =
-      apps::mojom::LaunchSource::kFromProtocolHandler;
+  expected_results.launch_source = apps::LaunchSource::kFromProtocolHandler;
 
   testing::StrictMock<MockWebAppLaunchManager> manager(profile());
   EXPECT_CALL(manager, LaunchWebApplication(testing::_, testing::_))
diff --git a/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc b/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc
index 43d85ff21..9376174c 100644
--- a/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc
@@ -139,7 +139,7 @@
 
   apps::AppLaunchParams params(
       app_id, apps::LaunchContainer::kLaunchContainerWindow,
-      WindowOpenDisposition::NEW_WINDOW, apps::mojom::LaunchSource::kFromTest);
+      WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
 
   UninstallWebApp(app_id);
   content::WebContents* const web_contents =
diff --git a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
index eb0b550..a66b5a2 100644
--- a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
@@ -95,7 +95,7 @@
                                              apps::mojom::IntentPtr&& intent) {
   apps::AppLaunchParams params = apps::CreateAppLaunchParamsForIntent(
       app_id,
-      /*event_flags=*/0, apps::mojom::LaunchSource::kFromSharesheet,
+      /*event_flags=*/0, apps::LaunchSource::kFromSharesheet,
       display::kDefaultDisplayId, apps::LaunchContainer::kLaunchContainerWindow,
       std::move(intent), profile);
 
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 375d8fcc5..472b2218 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/ui/webui/autofill_and_password_manager_internals/password_manager_internals_ui.h"
 #include "chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h"
 #include "chrome/browser/ui/webui/browsing_topics/browsing_topics_internals_ui.h"
+#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h"
 #include "chrome/browser/ui/webui/components/components_ui.h"
 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
 #include "chrome/browser/ui/webui/crashes_ui.h"
@@ -949,6 +950,12 @@
     return &NewComponentUI<ash::file_manager::FileManagerUI,
                            ChromeFileManagerUIDelegate>;
   }
+  if (url.host_piece() == chrome::kChromeUICloudUploadHost) {
+    if (!ash::features::IsUploadOfficeToCloudEnabled()) {
+      return nullptr;
+    }
+    return &NewWebUI<chromeos::cloud_upload::CloudUploadDialogUI>;
+  }
   if (url.host_piece() == chrome::kChromeUINotificationTesterHost)
     return &NewWebUI<chromeos::NotificationTesterUI>;
   if (url.host_piece() == chrome::kChromeUIAccountManagerErrorHost)
@@ -1575,6 +1582,7 @@
       GURL(chrome::kChromeUIEmojiPickerURL),
       GURL(chrome::kOsUIEmojiPickerURL),
       GURL(ash::file_manager::kChromeUIFileManagerURL),
+      GURL(chrome::kChromeUICloudUploadURL),
       GURL(chrome::kChromeUIFlagsURL),
       GURL(chrome::kOsUIFlagsURL),
       GURL(chrome::kOsUIGpuURL),
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/OWNERS b/chrome/browser/ui/webui/chromeos/cloud_upload/OWNERS
new file mode 100644
index 0000000..73220a8
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/cloud_upload/OWNERS
@@ -0,0 +1 @@
+file://ui/file_manager/OWNERS
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.cc b/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.cc
new file mode 100644
index 0000000..204533d8
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.cc
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h"
+
+#include "base/logging.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/webui_url_constants.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace chromeos::cloud_upload {
+
+// static
+bool CloudUploadDialog::Show() {
+  // Allow no more than one upload dialog at a time. In the case of multiple
+  // upload requests, they should either be handled simultaneously or queued.
+  if (SystemWebDialogDelegate::HasInstance(
+          GURL(chrome::kChromeUICloudUploadURL))) {
+    return false;
+  }
+
+  // The pointer is managed by an instance of `views::WebDialogView` and removed
+  // in `SystemWebDialogDelegate::OnDialogClosed`.
+  CloudUploadDialog* dialog = new CloudUploadDialog();
+  dialog->ShowSystemDialog();
+  return true;
+}
+
+CloudUploadDialog::CloudUploadDialog()
+    : SystemWebDialogDelegate(GURL(chrome::kChromeUICloudUploadURL),
+                              std::u16string() /* title */) {}
+
+CloudUploadDialog::~CloudUploadDialog() = default;
+
+bool CloudUploadDialog::ShouldShowCloseButton() const {
+  return false;
+}
+
+CloudUploadDialogUI::CloudUploadDialogUI(content::WebUI* web_ui)
+    : ui::WebDialogUI(web_ui) {
+  content::WebUIDataSource::CreateAndAdd(Profile::FromWebUI(web_ui),
+                                         chrome::kChromeUICloudUploadHost);
+}
+
+CloudUploadDialogUI::~CloudUploadDialogUI() = default;
+
+}  // namespace chromeos::cloud_upload
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h b/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h
new file mode 100644
index 0000000..34166cd
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_DIALOG_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_DIALOG_H_
+
+#include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+namespace chromeos::cloud_upload {
+
+// Defines the web dialog used to help users upload Office files to the cloud.
+class CloudUploadDialog : public SystemWebDialogDelegate {
+ public:
+  CloudUploadDialog(const CloudUploadDialog&) = delete;
+  CloudUploadDialog& operator=(const CloudUploadDialog&) = delete;
+
+  // Creates and shows a new dialog for the cloud upload workflow. Returns true
+  // if a new dialog has been effectively created.
+  static bool Show();
+
+ protected:
+  CloudUploadDialog();
+  ~CloudUploadDialog() override;
+  bool ShouldShowCloseButton() const override;
+};
+
+// The WebUI for chrome://cloud-upload-dialog, used for uploading files to the
+// cloud.
+class CloudUploadDialogUI : public ui::WebDialogUI {
+ public:
+  explicit CloudUploadDialogUI(content::WebUI* web_ui);
+  CloudUploadDialogUI(const CloudUploadDialogUI&) = delete;
+  CloudUploadDialogUI& operator=(const CloudUploadDialogUI&) = delete;
+  ~CloudUploadDialogUI() override;
+};
+
+}  // namespace chromeos::cloud_upload
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_DIALOG_H_
diff --git a/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_handler.cc b/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_handler.cc
index 18be5fa..9ae1c8d 100644
--- a/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_handler.cc
@@ -197,10 +197,6 @@
   DCHECK(pinned);
   optional_fields.pinned = pinned.value();
 
-  absl::optional<bool> renotify = notifObj->FindBool("richDataRenotify");
-  DCHECK(renotify);
-  optional_fields.renotify = renotify.value();
-
   absl::optional<bool> show_snooze = notifObj->FindBool("richDataShowSnooze");
   DCHECK(show_snooze);
   optional_fields.should_show_snooze_button = show_snooze.value();
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index 60dd829..b0d15d1 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -877,7 +877,7 @@
         disposition == WindowOpenDisposition::NEW_WINDOW
             ? apps::LaunchContainer::kLaunchContainerWindow
             : apps::LaunchContainer::kLaunchContainerTab,
-        disposition, apps::mojom::LaunchSource::kFromNewTabPage);
+        disposition, apps::LaunchSource::kFromNewTabPage);
     params.override_url = override_url;
     apps::AppServiceProxyFactory::GetForProfile(profile)
         ->BrowserAppLauncher()
@@ -895,7 +895,7 @@
         extension_id, launch_container,
         old_contents ? WindowOpenDisposition::CURRENT_TAB
                      : WindowOpenDisposition::NEW_FOREGROUND_TAB,
-        apps::mojom::LaunchSource::kFromNewTabPage);
+        apps::LaunchSource::kFromNewTabPage);
     params.override_url = override_url;
     WebContents* new_contents =
         apps::AppServiceProxyFactory::GetForProfile(profile)
diff --git a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.cc b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.cc
index 4f2d0d0..c5f6b5e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.cc
@@ -101,6 +101,7 @@
     return;
 
   loading_saved_device_page_ = true;
+  loading_start_time_ = base::TimeTicks::Now();
   ash::quick_pair::FastPairRepository::Get()->GetSavedDevices(
       base::BindOnce(&FastPairSavedDevicesHandler::OnGetSavedDevices,
                      weak_ptr_factory_.GetWeakPtr()));
@@ -122,9 +123,14 @@
   if (devices.empty()) {
     QP_LOG(VERBOSE) << __func__ << ": No devices saved to the user's account";
     base::Value::List saved_devices_list;
+    ash::quick_pair::RecordSavedDevicesCount(
+        /*num_devices=*/saved_devices_list.size());
     FireWebUIListener(kSavedDevicesListMessage,
                       base::Value(std::move(saved_devices_list)));
     loading_saved_device_page_ = false;
+    base::TimeDelta total_load_time =
+        base::TimeTicks::Now() - loading_start_time_;
+    ash::quick_pair::RecordSavedDevicesTotalUxLoadTime(total_load_time);
     return;
   }
 
@@ -223,9 +229,14 @@
         /*account_key=*/account_key));
   }
 
+  ash::quick_pair::RecordSavedDevicesCount(
+      /*num_devices=*/saved_devices_list.size());
   FireWebUIListener(kSavedDevicesListMessage,
                     base::Value(std::move(saved_devices_list)));
   QP_LOG(VERBOSE) << __func__ << ": Sending device list";
+  base::TimeDelta total_load_time =
+      base::TimeTicks::Now() - loading_start_time_;
+  ash::quick_pair::RecordSavedDevicesTotalUxLoadTime(total_load_time);
 
   // We reset the state here for another page load that may happened while
   // chrome://os-settings is open, since our decoding tasks are completed.
diff --git a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.h b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.h
index 51a9ec0..cf0f698 100644
--- a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.h
@@ -10,6 +10,7 @@
 #include "base/containers/flat_set.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "ui/gfx/image/image.h"
 
@@ -47,6 +48,7 @@
   void OnSavedDeviceDeleted(bool success);
 
   bool loading_saved_device_page_ = false;
+  base::TimeTicks loading_start_time_;
 
   std::unique_ptr<base::AtomicRefCount> pending_decoding_tasks_count_;
   std::vector<nearby::fastpair::FastPairDevice> devices_;
diff --git a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler_unittest.cc
index 8b81f61c..27740ac 100644
--- a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler_unittest.cc
@@ -87,6 +87,10 @@
 
 const char kSavedDeviceRemoveResultMetricName[] =
     "Bluetooth.ChromeOS.FastPair.SavedDevices.Remove.Result";
+const char kSavedDevicesTotalUxLoadTimeMetricName[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.TotalUxLoadTime";
+const char kSavedDevicesCountMetricName[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.DeviceCount";
 
 nearby::fastpair::FastPairDevice CreateFastPairDevice(
     const std::string device_name,
@@ -283,6 +287,8 @@
 };
 
 TEST_F(FastPairSavedDevicesHandlerTest, GetSavedDevices) {
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      0);
   InitializeSavedDevicesList(
       /*device_name1=*/kDeviceName1, /*device_image_bytes1=*/kImageBytes1,
       /*account_key1=*/kAccountKey1, /*device_name2=*/kDeviceName2,
@@ -306,9 +312,13 @@
       /*account_key2=*/kAccountKey2, /*device_name3=*/kDeviceName3,
       /*expected_device_url3=*/kDisplayUrlBase64,
       /*account_key3=*/kAccountKey3);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      1);
 }
 
 TEST_F(FastPairSavedDevicesHandlerTest, ReloadBeforePageLoadsIgnored) {
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      0);
   InitializeSavedDevicesList(
       /*device_name1=*/kDeviceName1, /*device_image_bytes1=*/kImageBytes1,
       /*account_key1=*/kAccountKey1, /*device_name2=*/kDeviceName2,
@@ -338,9 +348,13 @@
       /*account_key2=*/kAccountKey2, /*device_name3=*/kDeviceName3,
       /*expected_device_url3=*/kDisplayUrlBase64,
       /*account_key3=*/kAccountKey3);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      1);
 }
 
 TEST_F(FastPairSavedDevicesHandlerTest, ReloadAfterPageLoads) {
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      0);
   InitializeSavedDevicesList(
       /*device_name1=*/kDeviceName1, /*device_image_bytes1=*/kImageBytes1,
       /*account_key1=*/kAccountKey1, /*device_name2=*/kDeviceName2,
@@ -363,6 +377,8 @@
       /*account_key2=*/kAccountKey2, /*device_name3=*/kDeviceName3,
       /*expected_device_url3=*/kDisplayUrlBase64,
       /*account_key3=*/kAccountKey3);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      1);
 
   LoadPage();
   base::RunLoop().RunUntilIdle();
@@ -388,9 +404,15 @@
       /*account_key2=*/kAccountKey2, /*device_name3=*/kDeviceName3,
       /*expected_device_url3=*/kDisplayUrlBase64,
       /*account_key3=*/kAccountKey3);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      2);
 }
 
 TEST_F(FastPairSavedDevicesHandlerTest, EmptyListSentToWebUi) {
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      0);
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/0, 0);
   fast_pair_repository_.SetSavedDevices(
       /*status=*/nearby::fastpair::OptInStatus::STATUS_OPTED_OUT,
       /*devices=*/{});
@@ -401,9 +423,17 @@
   VerifyOptInStatus(*test_web_ui()->call_data()[0],
                     nearby::fastpair::OptInStatus::STATUS_OPTED_OUT);
   VerifyEmptySavedDevicesList(*test_web_ui()->call_data()[1]);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      1);
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/0, 1);
 }
 
 TEST_F(FastPairSavedDevicesHandlerTest, SavedDevicesBecomesEmpty) {
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      0);
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/0, 0);
   InitializeSavedDevicesList(
       /*device_name1=*/kDeviceName1, /*device_image_bytes1=*/kImageBytes1,
       /*account_key1=*/kAccountKey1, /*device_name2=*/kDeviceName2,
@@ -424,6 +454,10 @@
       /*account_key2=*/kAccountKey2, /*device_name3=*/kDeviceName3,
       /*expected_device_url3=*/kDisplayUrlBase64,
       /*account_key3=*/kAccountKey3);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      1);
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/0, 0);
 
   fast_pair_repository_.SetSavedDevices(
       /*status=*/nearby::fastpair::OptInStatus::STATUS_OPTED_OUT,
@@ -437,9 +471,15 @@
   VerifyOptInStatus(*test_web_ui()->call_data()[2],
                     nearby::fastpair::OptInStatus::STATUS_OPTED_OUT);
   VerifyEmptySavedDevicesList(*test_web_ui()->call_data()[3]);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      2);
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/0, 1);
 }
 
 TEST_F(FastPairSavedDevicesHandlerTest, SavedDevicesChanges) {
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      0);
   InitializeSavedDevicesList(
       /*device_name1=*/kDeviceName1, /*device_image_bytes1=*/kImageBytes1,
       /*account_key1=*/kAccountKey1, /*device_name2=*/kDeviceName2,
@@ -460,6 +500,8 @@
       /*account_key2=*/kAccountKey2, /*device_name3=*/kDeviceName3,
       /*expected_device_url3=*/kDisplayUrlBase64,
       /*account_key3=*/kAccountKey3);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      1);
 
   InitializeSavedDevicesList(
       /*device_name1=*/kDeviceName4, /*device_image_bytes1=*/kImageBytes4,
@@ -481,9 +523,13 @@
       /*account_key2=*/kAccountKey5, /*device_name3=*/kDeviceName6,
       /*expected_device_url3=*/kDisplayUrlBase64,
       /*account_key3=*/kAccountKey6);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      2);
 }
 
 TEST_F(FastPairSavedDevicesHandlerTest, EmptyImageSentToWebUi) {
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      0);
   ON_CALL(*mock_decoder_, DecodeImage(testing::_, testing::_, testing::_))
       .WillByDefault(base::test::RunOnceCallback<2>(gfx::Image()));
   InitializeSavedDevicesList(
@@ -507,9 +553,13 @@
       /*device_name2=*/kDeviceName2, /*expected_device_url2=*/"",
       /*account_key2=*/kAccountKey2, /*device_name3=*/kDeviceName3,
       /*expected_device_url3=*/"", /*account_key3=*/kAccountKey3);
+  histogram_tester().ExpectTotalCount(kSavedDevicesTotalUxLoadTimeMetricName,
+                                      1);
 }
 
 TEST_F(FastPairSavedDevicesHandlerTest, RemoveSavedDevice) {
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/3, 0);
   InitializeSavedDevicesList(
       /*device_name1=*/kDeviceName1, /*device_image_bytes1=*/kImageBytes1,
       /*account_key1=*/kAccountKey1, /*device_name2=*/kDeviceName2,
@@ -531,6 +581,10 @@
                                        /*success=*/true, 0);
   histogram_tester().ExpectBucketCount(kSavedDeviceRemoveResultMetricName,
                                        /*success=*/false, 0);
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/2, 0);
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/3, 1);
 
   RemoveDevice(kAccountKey3);
   base::RunLoop().RunUntilIdle();
@@ -557,6 +611,10 @@
                                        /*success=*/true, 1);
   histogram_tester().ExpectBucketCount(kSavedDeviceRemoveResultMetricName,
                                        /*success=*/false, 0);
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/2, 1);
+  histogram_tester().ExpectBucketCount(kSavedDevicesCountMetricName,
+                                       /*num_devices=*/3, 1);
 }
 
 }  // namespace chromeos::settings
diff --git a/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc b/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc
index 59383c9a..44f7f118 100644
--- a/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc
+++ b/chrome/browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc
@@ -850,7 +850,7 @@
   {
     auto launch_params = apps::CreateCrosapiLaunchParamsWithEventFlags(
         apps::AppServiceProxyFactory::GetForProfile(profile()), app_id,
-        /*event_flags=*/0, apps::mojom::LaunchSource::kFromSharesheet,
+        /*event_flags=*/0, apps::LaunchSource::kFromSharesheet,
         display::kInvalidDisplayId);
     launch_params->intent = apps_util::ConvertAppServiceToCrosapiIntent(
         apps_util::CreateShareIntentFromText(shared_link, shared_title),
@@ -906,7 +906,7 @@
 
     auto launch_params = apps::CreateCrosapiLaunchParamsWithEventFlags(
         apps::AppServiceProxyFactory::GetForProfile(profile()), app_id,
-        /*event_flags=*/0, apps::mojom::LaunchSource::kFromSharesheet,
+        /*event_flags=*/0, apps::LaunchSource::kFromSharesheet,
         display::kInvalidDisplayId);
     launch_params->intent = std::move(crosapi_intent);
 
@@ -969,7 +969,7 @@
 
     auto launch_params = apps::CreateCrosapiLaunchParamsWithEventFlags(
         apps::AppServiceProxyFactory::GetForProfile(profile()), app_id,
-        /*event_flags=*/0, apps::mojom::LaunchSource::kFromSharesheet,
+        /*event_flags=*/0, apps::LaunchSource::kFromSharesheet,
         display::kInvalidDisplayId);
     launch_params->intent = std::move(crosapi_intent);
 
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
index dd85503..b08eac5 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -772,8 +772,8 @@
 content::WebContents* WebAppPublisherHelper::Launch(
     const std::string& app_id,
     int32_t event_flags,
-    apps::mojom::LaunchSource launch_source,
-    apps::mojom::WindowInfoPtr window_info) {
+    apps::LaunchSource launch_source,
+    apps::WindowInfoPtr window_info) {
   if (IsShuttingDown()) {
     return nullptr;
   }
@@ -784,48 +784,48 @@
   }
 
   switch (launch_source) {
-    case apps::mojom::LaunchSource::kUnknown:
-    case apps::mojom::LaunchSource::kFromParentalControls:
+    case apps::LaunchSource::kUnknown:
+    case apps::LaunchSource::kFromParentalControls:
       break;
-    case apps::mojom::LaunchSource::kFromAppListGrid:
-    case apps::mojom::LaunchSource::kFromAppListGridContextMenu:
+    case apps::LaunchSource::kFromAppListGrid:
+    case apps::LaunchSource::kFromAppListGridContextMenu:
       UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunch",
                                 extension_misc::APP_LAUNCH_APP_LIST_MAIN,
                                 extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
 
       break;
-    case apps::mojom::LaunchSource::kFromAppListQuery:
-    case apps::mojom::LaunchSource::kFromAppListQueryContextMenu:
+    case apps::LaunchSource::kFromAppListQuery:
+    case apps::LaunchSource::kFromAppListQueryContextMenu:
       UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunch",
                                 extension_misc::APP_LAUNCH_APP_LIST_SEARCH,
                                 extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
       break;
-    case apps::mojom::LaunchSource::kFromAppListRecommendation:
-    case apps::mojom::LaunchSource::kFromShelf:
-    case apps::mojom::LaunchSource::kFromFileManager:
-    case apps::mojom::LaunchSource::kFromLink:
-    case apps::mojom::LaunchSource::kFromOmnibox:
-    case apps::mojom::LaunchSource::kFromChromeInternal:
-    case apps::mojom::LaunchSource::kFromKeyboard:
-    case apps::mojom::LaunchSource::kFromOtherApp:
-    case apps::mojom::LaunchSource::kFromMenu:
-    case apps::mojom::LaunchSource::kFromInstalledNotification:
-    case apps::mojom::LaunchSource::kFromTest:
-    case apps::mojom::LaunchSource::kFromArc:
-    case apps::mojom::LaunchSource::kFromSharesheet:
-    case apps::mojom::LaunchSource::kFromReleaseNotesNotification:
-    case apps::mojom::LaunchSource::kFromFullRestore:
-    case apps::mojom::LaunchSource::kFromSmartTextContextMenu:
-    case apps::mojom::LaunchSource::kFromDiscoverTabNotification:
-    case apps::mojom::LaunchSource::kFromManagementApi:
-    case apps::mojom::LaunchSource::kFromKiosk:
-    case apps::mojom::LaunchSource::kFromCommandLine:
-    case apps::mojom::LaunchSource::kFromBackgroundMode:
-    case apps::mojom::LaunchSource::kFromNewTabPage:
-    case apps::mojom::LaunchSource::kFromIntentUrl:
-    case apps::mojom::LaunchSource::kFromOsLogin:
-    case apps::mojom::LaunchSource::kFromProtocolHandler:
-    case apps::mojom::LaunchSource::kFromUrlHandler:
+    case apps::LaunchSource::kFromAppListRecommendation:
+    case apps::LaunchSource::kFromShelf:
+    case apps::LaunchSource::kFromFileManager:
+    case apps::LaunchSource::kFromLink:
+    case apps::LaunchSource::kFromOmnibox:
+    case apps::LaunchSource::kFromChromeInternal:
+    case apps::LaunchSource::kFromKeyboard:
+    case apps::LaunchSource::kFromOtherApp:
+    case apps::LaunchSource::kFromMenu:
+    case apps::LaunchSource::kFromInstalledNotification:
+    case apps::LaunchSource::kFromTest:
+    case apps::LaunchSource::kFromArc:
+    case apps::LaunchSource::kFromSharesheet:
+    case apps::LaunchSource::kFromReleaseNotesNotification:
+    case apps::LaunchSource::kFromFullRestore:
+    case apps::LaunchSource::kFromSmartTextContextMenu:
+    case apps::LaunchSource::kFromDiscoverTabNotification:
+    case apps::LaunchSource::kFromManagementApi:
+    case apps::LaunchSource::kFromKiosk:
+    case apps::LaunchSource::kFromCommandLine:
+    case apps::LaunchSource::kFromBackgroundMode:
+    case apps::LaunchSource::kFromNewTabPage:
+    case apps::LaunchSource::kFromIntentUrl:
+    case apps::LaunchSource::kFromOsLogin:
+    case apps::LaunchSource::kFromProtocolHandler:
+    case apps::LaunchSource::kFromUrlHandler:
       break;
   }
 
@@ -852,7 +852,9 @@
 
   DisplayMode display_mode = registrar().GetAppEffectiveDisplayMode(app_id);
   apps::AppLaunchParams params = apps::CreateAppIdLaunchParamsWithEventFlags(
-      app_id, event_flags, launch_source, display::kInvalidDisplayId,
+      app_id, event_flags,
+      apps::ConvertMojomLaunchSourceToLaunchSource(launch_source),
+      display::kInvalidDisplayId,
       /*fallback_container=*/
       ConvertDisplayModeToAppLaunchContainer(display_mode));
   if (file_paths) {
@@ -1181,7 +1183,7 @@
 
   apps::AppLaunchParams params(
       app_id, ConvertDisplayModeToAppLaunchContainer(display_mode),
-      WindowOpenDisposition::CURRENT_TAB, apps::mojom::LaunchSource::kFromMenu,
+      WindowOpenDisposition::CURRENT_TAB, apps::LaunchSource::kFromMenu,
       display_id);
 
   auto menu_item = shortcut_id_map_.find(shortcut_id);
@@ -1546,7 +1548,8 @@
   bool is_file_handling_launch = intent->files && !intent->files->empty() &&
                                  !apps_util::IsShareIntent(intent);
   auto params = apps::CreateAppLaunchParamsForIntent(
-      app_id, event_flags, launch_source, display_id,
+      app_id, event_flags,
+      apps::ConvertMojomLaunchSourceToLaunchSource(launch_source), display_id,
       ConvertDisplayModeToAppLaunchContainer(
           registrar().GetAppEffectiveDisplayMode(app_id)),
       std::move(intent), profile_);
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
index f52cf71..e90fb96b 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
@@ -33,6 +33,7 @@
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
 #include "components/services/app_service/public/cpp/permission.h"
@@ -205,8 +206,8 @@
 
   content::WebContents* Launch(const std::string& app_id,
                                int32_t event_flags,
-                               apps::mojom::LaunchSource launch_source,
-                               apps::mojom::WindowInfoPtr window_info);
+                               apps::LaunchSource launch_source,
+                               apps::WindowInfoPtr window_info);
 
   void LaunchAppWithFiles(const std::string& app_id,
                           int32_t event_flags,
diff --git a/chrome/browser/web_applications/app_service/web_apps.cc b/chrome/browser/web_applications/app_service/web_apps.cc
index 8dd2cd3b..451aa52 100644
--- a/chrome/browser/web_applications/app_service/web_apps.cc
+++ b/chrome/browser/web_applications/app_service/web_apps.cc
@@ -161,7 +161,8 @@
                      int32_t event_flags,
                      apps::LaunchSource launch_source,
                      apps::WindowInfoPtr window_info) {
-  // TODO(crbug.com/1253250): Add the implementation.
+  publisher_helper().Launch(app_id, event_flags, launch_source,
+                            std::move(window_info));
 }
 
 void WebApps::LaunchAppWithParams(apps::AppLaunchParams&& params,
@@ -191,8 +192,10 @@
                      int32_t event_flags,
                      apps::mojom::LaunchSource launch_source,
                      apps::mojom::WindowInfoPtr window_info) {
-  publisher_helper().Launch(app_id, event_flags, launch_source,
-                            std::move(window_info));
+  publisher_helper().Launch(
+      app_id, event_flags,
+      apps::ConvertMojomLaunchSourceToLaunchSource(launch_source),
+      apps::ConvertMojomWindowInfoToWindowInfo(window_info));
 }
 
 void WebApps::LaunchAppWithFiles(const std::string& app_id,
diff --git a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
index 178b4763..6504236 100644
--- a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
@@ -116,10 +116,9 @@
 
   WebAppBrowserController* controller;
   {
-    apps::AppLaunchParams params(app_id,
-                                 apps::LaunchContainer::kLaunchContainerWindow,
-                                 WindowOpenDisposition::NEW_WINDOW,
-                                 apps::mojom::LaunchSource::kFromTest);
+    apps::AppLaunchParams params(
+        app_id, apps::LaunchContainer::kLaunchContainerWindow,
+        WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
     content::WebContents* contents =
         apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
             ->BrowserAppLauncher()
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index d85c25c..718888d9 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1657799781-3a3555f6870839761b74bf6194a5dab4d7413c5d.profdata
+chrome-mac-arm-main-1657821506-486d4d23d0f789a7f8fadb304293f5a4528c03a4.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index f808f7b3..b9300a5b 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1657809902-2584e57de6db21c3a9d49b665847507731b82758.profdata
+chrome-win32-main-1657832321-39029d6baff073aaef53a4f25c5894bbe1f13030.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 4a8cafe..676b0f81 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1657809902-d9943c8ff2ee9aa21b84a79c4cadeb564eb3ecc9.profdata
+chrome-win64-main-1657832321-e4079ba91a4837aa4079c1cdf5084d7b38061a9e.profdata
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index eeeb1f4..2bed480 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -79,7 +79,7 @@
 #include "components/nacl/common/nacl_process_type.h"
 #endif
 
-#if BUILDFLAG(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PPAPI)
 #include "content/public/common/pepper_plugin_info.h"
 #include "ppapi/shared_impl/ppapi_permissions.h"  // nogncheck
 #endif
@@ -98,7 +98,7 @@
 
 namespace {
 
-#if BUILDFLAG(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PPAPI)
 #if BUILDFLAG(ENABLE_PDF)
 const char kPDFPluginExtension[] = "pdf";
 const char kPDFPluginDescription[] = "Portable Document Format";
@@ -156,7 +156,7 @@
   plugins->push_back(nacl);
 #endif  // BUILDFLAG(ENABLE_NACL)
 }
-#endif  //  BUILDFLAG(ENABLE_PLUGINS)
+#endif  // BUILDFLAG(ENABLE_PPAPI)
 
 }  // namespace
 
@@ -191,7 +191,7 @@
   gpu::SetKeysForCrashLogging(gpu_info);
 }
 
-#if BUILDFLAG(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PPAPI)
 // static
 content::PepperPluginInfo* ChromeContentClient::FindMostRecentPlugin(
     const std::vector<std::unique_ptr<content::PepperPluginInfo>>& plugins) {
@@ -210,13 +210,13 @@
 
   return plugin_map.rbegin()->second;
 }
-#endif  // BUILDFLAG(ENABLE_PLUGINS)
+#endif  // BUILDFLAG(ENABLE_PPAPI)
 
 void ChromeContentClient::AddPepperPlugins(
     std::vector<content::PepperPluginInfo>* plugins) {
-#if BUILDFLAG(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PPAPI)
   ComputeBuiltInPlugins(plugins);
-#endif  // BUILDFLAG(ENABLE_PLUGINS)
+#endif  // BUILDFLAG(ENABLE_PPAPI)
 }
 
 void ChromeContentClient::AddContentDecryptionModules(
diff --git a/chrome/common/chrome_content_client.h b/chrome/common/chrome_content_client.h
index e27016c..d50547a9 100644
--- a/chrome/common/chrome_content_client.h
+++ b/chrome/common/chrome_content_client.h
@@ -19,9 +19,9 @@
 #include "content/public/common/content_client.h"
 #include "ppapi/buildflags/buildflags.h"
 
-#if BUILDFLAG(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PPAPI)
 #include "content/public/common/pepper_plugin_info.h"
-#endif
+#endif  // BUILDFLAG(ENABLE_PPAPI)
 
 namespace embedder_support {
 class OriginTrialPolicyImpl;
@@ -58,7 +58,7 @@
       content::PepperPluginInfo::PPP_ShutdownModuleFunc shutdown_module);
 #endif
 
-#if BUILDFLAG(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PPAPI)
   // This returns the most recent plugin based on the plugin versions. In the
   // event of a tie, a debug plugin will be considered more recent than a
   // non-debug plugin.
@@ -67,7 +67,7 @@
   // The method is only visible for testing purposes.
   static content::PepperPluginInfo* FindMostRecentPlugin(
       const std::vector<std::unique_ptr<content::PepperPluginInfo>>& plugins);
-#endif
+#endif  // BUILDFLAG(ENABLE_PPAPI)
 
   void SetActiveURL(const GURL& url, std::string top_origin) override;
   void SetGpuInfo(const gpu::GPUInfo& gpu_info) override;
diff --git a/chrome/common/chrome_content_client_unittest.cc b/chrome/common/chrome_content_client_unittest.cc
index 3623991..7df450d 100644
--- a/chrome/common/chrome_content_client_unittest.cc
+++ b/chrome/common/chrome_content_client_unittest.cc
@@ -30,7 +30,7 @@
 
 namespace chrome_common {
 
-#if BUILDFLAG(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PPAPI)
 TEST(ChromeContentClientTest, FindMostRecent) {
   std::vector<std::unique_ptr<content::PepperPluginInfo>> version_vector;
   // Test an empty vector.
@@ -96,7 +96,7 @@
   most_recent = ChromeContentClient::FindMostRecentPlugin(version_vector);
   EXPECT_STREQ("system_flash", most_recent->name.c_str());
 }
-#endif  // BUILDFLAG(ENABLE_PLUGINS)
+#endif  // BUILDFLAG(ENABLE_PPAPI)
 
 TEST(ChromeContentClientTest, AdditionalSchemes) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index d7374923..ff1b2a4b 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -41,11 +41,6 @@
     "AllowTouchpadHapticClickSettings", base::FEATURE_ENABLED_BY_DEFAULT};
 #endif  // defined(IS_CHROMEOS_ASH)
 
-// Always reinstall system web apps, instead of only doing so after version
-// upgrade or locale changes.
-const base::Feature kAlwaysReinstallSystemWebApps{
-    "ReinstallSystemWebApps", base::FEATURE_DISABLED_BY_DEFAULT};
-
 #if BUILDFLAG(IS_ANDROID)
 const base::Feature kAnonymousUpdateChecks{"AnonymousUpdateChecks",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
@@ -364,11 +359,6 @@
     base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
-// Enables all registered system web apps, regardless of their respective
-// feature flags.
-const base::Feature kEnableAllSystemWebApps{"EnableAllSystemWebApps",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enable the restricted web APIs for high-trusted apps.
 const base::Feature kEnableRestrictedWebApis{"EnableRestrictedWebApis",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 66e4c64..94ab6f0 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -46,9 +46,6 @@
 extern const base::Feature kAllowTouchpadHapticClickSettings;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kAlwaysReinstallSystemWebApps;
-
 #if BUILDFLAG(IS_ANDROID)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kAnonymousUpdateChecks;
@@ -246,9 +243,6 @@
 #endif
 
 COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kEnableAllSystemWebApps;
-
-COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kEnableAmbientAuthenticationInGuestSession;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/extensions/api/identity.idl b/chrome/common/extensions/api/identity.idl
index 7d40c3a..aec69f94 100644
--- a/chrome/common/extensions/api/identity.idl
+++ b/chrome/common/extensions/api/identity.idl
@@ -89,8 +89,15 @@
     boolean? interactive;
   };
 
-  callback GetAuthTokenCallback = void (optional DOMString token,
-                                        optional DOMString[] grantedScopes);
+  dictionary GetAuthTokenResult {
+    // The specific token associated with the request.
+    DOMString? token;
+
+    // A list of OAuth2 scopes granted to the extension.
+    DOMString[]? grantedScopes;
+  };
+
+  callback GetAuthTokenCallback = void (GetAuthTokenResult result);
   callback GetAccountsCallback = void (AccountInfo[] accounts);
   callback GetProfileUserInfoCallback = void (ProfileUserInfo userInfo);
   callback InvalidateAuthTokenCallback = void ();
@@ -120,14 +127,19 @@
     // context. In particular, do not use <code>getAuthToken</code>
     // interactively when your app is first launched.
     //
+    // Note: When called with a callback, instead of returning an object this
+    // function will return the two properties as separate arguments passed to
+    // the callback.
+    //
     // |details| : Token options.
     // |callback| : Called with an OAuth2 access token as specified by the
     // manifest, or undefined if there was an error. The
     // <code>grantedScopes</code> parameter is populated since Chrome 87. When
     // available, this parameter contains the list of granted scopes
     // corresponding with the returned token.
-    static void getAuthToken(optional TokenDetails details,
-                             optional GetAuthTokenCallback callback);
+    [supportsPromises] static void getAuthToken(
+        optional TokenDetails details,
+        optional GetAuthTokenCallback callback);
 
     // Retrieves email address and obfuscated gaia id of the user
     // signed into a profile.
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index dc6e56b..bfd1813 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -300,6 +300,8 @@
 const char kChromeUICertificateManagerDialogURL[] =
     "chrome://certificate-manager/";
 const char kChromeUICertificateManagerHost[] = "certificate-manager";
+const char kChromeUICloudUploadHost[] = "cloud-upload-dialog";
+const char kChromeUICloudUploadURL[] = "chrome://cloud-upload-dialog/";
 const char kChromeUIConfirmPasswordChangeHost[] = "confirm-password-change";
 const char kChromeUIConfirmPasswordChangeUrl[] =
     "chrome://confirm-password-change";
@@ -420,6 +422,7 @@
     kChromeUIBluetoothPairingHost,
     kChromeUIBorealisCreditsHost,
     kChromeUICertificateManagerHost,
+    kChromeUICloudUploadHost,
     kChromeUICrostiniCreditsHost,
     kChromeUICrostiniInstallerHost,
     kChromeUICryptohomeHost,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 3fc2ab5..575e849 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -279,6 +279,8 @@
 extern const char kChromeUIBluetoothPairingURL[];
 extern const char kChromeUICertificateManagerDialogURL[];
 extern const char kChromeUICertificateManagerHost[];
+extern const char kChromeUICloudUploadHost[];
+extern const char kChromeUICloudUploadURL[];
 extern const char kChromeUIConfirmPasswordChangeHost[];
 extern const char kChromeUIConfirmPasswordChangeUrl[];
 extern const char kChromeUICrostiniInstallerHost[];
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 431e7f5..0ad8a99 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -302,6 +302,8 @@
       "extensions/chrome_extensions_renderer_client.h",
       "extensions/extension_hooks_delegate.cc",
       "extensions/extension_hooks_delegate.h",
+      "extensions/identity_hooks_delegate.cc",
+      "extensions/identity_hooks_delegate.h",
       "extensions/media_galleries_custom_bindings.cc",
       "extensions/media_galleries_custom_bindings.h",
       "extensions/notifications_native_handler.cc",
diff --git a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
index 8fa5894..5f82ebe 100644
--- a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
+++ b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
@@ -15,6 +15,7 @@
 #include "chrome/grit/renderer_resources.h"
 #include "chrome/renderer/extensions/app_hooks_delegate.h"
 #include "chrome/renderer/extensions/extension_hooks_delegate.h"
+#include "chrome/renderer/extensions/identity_hooks_delegate.h"
 #include "chrome/renderer/extensions/media_galleries_custom_bindings.h"
 #include "chrome/renderer/extensions/notifications_native_handler.h"
 #include "chrome/renderer/extensions/page_capture_custom_bindings.h"
@@ -243,6 +244,8 @@
   bindings->GetHooksForAPI("tabs")->SetDelegate(
       std::make_unique<extensions::TabsHooksDelegate>(
           bindings_system->messaging_service()));
+  bindings->GetHooksForAPI("identity")
+      ->SetDelegate(std::make_unique<extensions::IdentityHooksDelegate>());
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   bindings->GetHooksForAPI("accessibilityPrivate")
       ->SetDelegate(
diff --git a/chrome/renderer/extensions/identity_hooks_delegate.cc b/chrome/renderer/extensions/identity_hooks_delegate.cc
new file mode 100644
index 0000000..b572dfa
--- /dev/null
+++ b/chrome/renderer/extensions/identity_hooks_delegate.cc
@@ -0,0 +1,72 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/extensions/identity_hooks_delegate.h"
+
+#include "base/bind.h"
+#include "base/check.h"
+#include "extensions/renderer/bindings/api_binding_types.h"
+#include "extensions/renderer/v8_helpers.h"
+
+namespace extensions {
+
+namespace {
+constexpr char kGetAuthToken[] = "identity.getAuthToken";
+
+// Function that, for callback based API calls, will make the values associated
+// with each property on the return object a separate argument in a new result
+// vector it returns instead.
+// Note: This is to allow the promise version of the API to return a
+// single object, while still supporting the previous callback version which
+// expects multiple parameters to be passed to the callback.
+std::vector<v8::Local<v8::Value>> MassageGetAuthTokenResults(
+    const std::vector<v8::Local<v8::Value>>& result_args,
+    v8::Local<v8::Context> context,
+    binding::AsyncResponseType async_type) {
+  // If this is not a callback based API call, we don't need to modify anything.
+  if (async_type != binding::AsyncResponseType::kCallback) {
+    return result_args;
+  }
+
+  DCHECK_EQ(1u, result_args.size());
+  DCHECK(result_args[0]->IsObject());
+  v8::Local<v8::Object> result_obj = result_args[0].As<v8::Object>();
+
+  // The object sent back has two properties on it which we need to split into
+  // two separate arguments"
+  v8::Local<v8::Value> token;
+  bool success = v8_helpers::GetProperty(context, result_obj, "token", &token);
+  DCHECK(success);
+  v8::Local<v8::Value> granted_scopes;
+  success = v8_helpers::GetProperty(context, result_obj, "grantedScopes",
+                                    &granted_scopes);
+  DCHECK(success);
+  std::vector<v8::Local<v8::Value>> new_args{token, granted_scopes};
+
+  return new_args;
+}
+
+}  // namespace
+
+using RequestResult = APIBindingHooks::RequestResult;
+
+IdentityHooksDelegate::IdentityHooksDelegate() = default;
+IdentityHooksDelegate::~IdentityHooksDelegate() = default;
+
+RequestResult IdentityHooksDelegate::HandleRequest(
+    const std::string& method_name,
+    const APISignature* signature,
+    v8::Local<v8::Context> context,
+    std::vector<v8::Local<v8::Value>>* arguments,
+    const APITypeReferenceMap& refs) {
+  // Only add the result handler for the getAuthToken function.
+  if (method_name != kGetAuthToken)
+    return RequestResult(RequestResult::NOT_HANDLED);
+
+  return RequestResult(RequestResult::NOT_HANDLED,
+                       v8::Local<v8::Function>() /*custom_callback*/,
+                       base::BindOnce(MassageGetAuthTokenResults));
+}
+
+}  // namespace extensions
diff --git a/chrome/renderer/extensions/identity_hooks_delegate.h b/chrome/renderer/extensions/identity_hooks_delegate.h
new file mode 100644
index 0000000..c04ccb2
--- /dev/null
+++ b/chrome/renderer/extensions/identity_hooks_delegate.h
@@ -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.
+
+#ifndef CHROME_RENDERER_EXTENSIONS_IDENTITY_HOOKS_DELEGATE_H_
+#define CHROME_RENDERER_EXTENSIONS_IDENTITY_HOOKS_DELEGATE_H_
+
+#include <vector>
+
+#include "extensions/renderer/bindings/api_binding_hooks_delegate.h"
+#include "extensions/renderer/bindings/api_signature.h"
+#include "v8/include/v8-forward.h"
+
+namespace extensions {
+
+// Custom native hooks for the identity API.
+class IdentityHooksDelegate : public APIBindingHooksDelegate {
+ public:
+  IdentityHooksDelegate();
+
+  IdentityHooksDelegate(const IdentityHooksDelegate&) = delete;
+  IdentityHooksDelegate& operator=(const IdentityHooksDelegate&) = delete;
+
+  ~IdentityHooksDelegate() override;
+
+  // APIBindingHooksDelegate:
+  APIBindingHooks::RequestResult HandleRequest(
+      const std::string& method_name,
+      const APISignature* signature,
+      v8::Local<v8::Context> context,
+      std::vector<v8::Local<v8::Value>>* arguments,
+      const APITypeReferenceMap& refs) override;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_RENDERER_EXTENSIONS_IDENTITY_HOOKS_DELEGATE_H_
diff --git a/chrome/renderer/extensions/identity_hooks_delegate_unittest.cc b/chrome/renderer/extensions/identity_hooks_delegate_unittest.cc
new file mode 100644
index 0000000..544c677
--- /dev/null
+++ b/chrome/renderer/extensions/identity_hooks_delegate_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/extensions/identity_hooks_delegate.h"
+
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/mojom/frame.mojom.h"
+#include "extensions/renderer/bindings/api_binding_test_util.h"
+#include "extensions/renderer/native_extension_bindings_system.h"
+#include "extensions/renderer/native_extension_bindings_system_test_base.h"
+#include "extensions/renderer/script_context.h"
+
+namespace extensions {
+
+using IdentityHooksDelegateTest = NativeExtensionBindingsSystemUnittest;
+
+// Tests that the result modifier used in the getAuthToken handle request hook
+// results in callback-based calls getting a response with multiple arguments
+// and promise-based calls getting a response with a single object.
+TEST_F(IdentityHooksDelegateTest, GetAuthToken) {
+  // Initialize bindings system.
+  bindings_system()
+      ->api_system()
+      ->GetHooksForAPI("identity")
+      ->SetDelegate(std::make_unique<IdentityHooksDelegate>());
+  // Register extension.
+  scoped_refptr<const Extension> extension = ExtensionBuilder("testExtension")
+                                                 .SetManifestVersion(3)
+                                                 .AddPermission("identity")
+                                                 .Build();
+  RegisterExtension(extension);
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = MainContext();
+  ScriptContext* script_context = CreateScriptContext(
+      context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
+  script_context->set_url(extension->url());
+  bindings_system()->UpdateBindingsForContext(script_context);
+
+  constexpr char kFakeAPIResponse[] =
+      R"([{"token": "foo", "grantedScopes": ["bar"]}])";
+
+  // Calling getAuthToken without a callback should return a promise that gets
+  // fulfilled with an object with the results as properties on it.
+  {
+    v8::Local<v8::Function> func = FunctionFromString(
+        context, "(function() { return chrome.identity.getAuthToken(); })");
+    v8::Local<v8::Value> result = RunFunction(func, context, 0, nullptr);
+    v8::Local<v8::Promise> promise;
+    ASSERT_TRUE(GetValueAs(result, &promise));
+    EXPECT_EQ(v8::Promise::kPending, promise->State());
+
+    bindings_system()->HandleResponse(last_params().request_id,
+                                      /*success=*/true,
+                                      ListValueFromString(kFakeAPIResponse),
+                                      /*error=*/std::string());
+
+    EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
+    // Note that the object here differs slightly from the response, in that it
+    // is not array wrapped and the keys become alphabetized.
+    EXPECT_EQ(R"({"grantedScopes":["bar"],"token":"foo"})",
+              V8ToString(promise->Result(), context));
+  }
+
+  // Calling getAuthToken with a callback should end up with the callback being
+  // called with multiple parameters rather than a single object.
+  {
+    constexpr char kFunctionCall[] =
+        R"((function(api) {
+             chrome.identity.getAuthToken((token, grantedScopes) => {
+               this.argument1 = token;
+               this.argument2 = grantedScopes;
+             });
+           }))";
+    v8::Local<v8::Function> func = FunctionFromString(context, kFunctionCall);
+    RunFunctionOnGlobal(func, context, 0, nullptr);
+
+    bindings_system()->HandleResponse(last_params().request_id,
+                                      /*success=*/true,
+                                      ListValueFromString(kFakeAPIResponse),
+                                      /*error=*/std::string());
+
+    EXPECT_EQ(R"("foo")", GetStringPropertyFromObject(context->Global(),
+                                                      context, "argument1"));
+    EXPECT_EQ(R"(["bar"])", GetStringPropertyFromObject(context->Global(),
+                                                        context, "argument2"));
+  }
+}
+
+}  // namespace extensions
diff --git a/chrome/renderer/sandbox_status_extension_android.cc b/chrome/renderer/sandbox_status_extension_android.cc
index 39b4431f..1b23af4 100644
--- a/chrome/renderer/sandbox_status_extension_android.cc
+++ b/chrome/renderer/sandbox_status_extension_android.cc
@@ -123,7 +123,7 @@
                      std::move(global_callback)));
 }
 
-std::unique_ptr<base::Value> SandboxStatusExtension::ReadSandboxStatus() {
+base::Value::Dict SandboxStatusExtension::ReadSandboxStatus() {
   std::string secontext;
   base::FilePath path(FILE_PATH_LITERAL("/proc/self/attr/current"));
   base::ReadFileToString(path, &secontext);
@@ -132,28 +132,24 @@
   path = base::FilePath(FILE_PATH_LITERAL("/proc/self/status"));
   base::ReadFileToString(path, &proc_status);
 
-  auto status = std::make_unique<base::DictionaryValue>();
-  status->SetIntKey("uid", getuid());
-  status->SetIntKey("pid", getpid());
-  status->SetStringKey("secontext", secontext);
-  status->SetIntKey("seccompStatus",
-                    static_cast<int>(content::GetSeccompSandboxStatus()));
-  status->SetStringKey("procStatus", proc_status);
-  status->SetStringKey(
-      "androidBuildId",
-      base::android::BuildInfo::GetInstance()->android_build_id());
-
-  return std::move(status);
+  base::Value::Dict status;
+  status.Set("uid", static_cast<int>(getuid()));
+  status.Set("pid", getpid());
+  status.Set("secontext", secontext);
+  status.Set("seccompStatus",
+             static_cast<int>(content::GetSeccompSandboxStatus()));
+  status.Set("procStatus", proc_status);
+  status.Set("androidBuildId",
+             base::android::BuildInfo::GetInstance()->android_build_id());
+  return status;
 }
 
 void SandboxStatusExtension::RunCallback(
     std::unique_ptr<v8::Global<v8::Function>> callback,
-    std::unique_ptr<base::Value> status) {
+    base::Value::Dict status) {
   if (!render_frame())
     return;
 
-  CHECK(status);
-
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context =
@@ -163,7 +159,7 @@
       v8::Local<v8::Function>::New(isolate, *callback);
 
   v8::Local<v8::Value> argv[] = {
-      content::V8ValueConverter::Create()->ToV8Value(*status, context)};
+      content::V8ValueConverter::Create()->ToV8Value(status, context)};
   render_frame()->GetWebFrame()->CallFunctionEvenIfScriptDisabled(
       callback_local, v8::Object::New(isolate), 1, argv);
 }
diff --git a/chrome/renderer/sandbox_status_extension_android.h b/chrome/renderer/sandbox_status_extension_android.h
index f613a92..927a25d 100644
--- a/chrome/renderer/sandbox_status_extension_android.h
+++ b/chrome/renderer/sandbox_status_extension_android.h
@@ -59,14 +59,14 @@
   void GetSandboxStatus(gin::Arguments* args);
 
   // Called on the blocking pool, this gets the sandbox status of the current
-  // renderer process and returns a status object as a base::Value.
-  std::unique_ptr<base::Value> ReadSandboxStatus();
+  // renderer process and returns a status object as a base::Value::Dict.
+  base::Value::Dict ReadSandboxStatus();
 
   // Runs the callback argument provided to GetSandboxStatus() with the status
   // object computed by ReadSandboxStatus(). This is called back on the thread
   // on which GetSandboxStatus() was called originally.
   void RunCallback(std::unique_ptr<v8::Global<v8::Function>> callback,
-                   std::unique_ptr<base::Value> status);
+                   base::Value::Dict status);
 
   // Set to true by AddSandboxStatusExtension().
   bool should_install_ = false;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 719b51e..2f59811 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7821,6 +7821,7 @@
       "../renderer/extensions/chrome_native_extension_bindings_system_unittest.cc",
       "../renderer/extensions/custom_types_unittest.cc",
       "../renderer/extensions/extension_hooks_delegate_unittest.cc",
+      "../renderer/extensions/identity_hooks_delegate_unittest.cc",
       "../renderer/extensions/renderer_permissions_policy_delegate_unittest.cc",
       "../renderer/extensions/tabs_hooks_delegate_unittest.cc",
     ]
diff --git a/chrome/test/data/android/url_overriding/redirect_to_other_browser.html b/chrome/test/data/android/url_overriding/redirect_to_other_browser.html
index b99eefd..f3d9df22 100644
--- a/chrome/test/data/android/url_overriding/redirect_to_other_browser.html
+++ b/chrome/test/data/android/url_overriding/redirect_to_other_browser.html
@@ -1,6 +1,6 @@
 <html>
   <head>
-    <meta http-equiv="refresh" content="0;URL='intent:text/plain;base64,SGVsbG8sIHdvcmxk#Intent;action=android.intent.action.VIEW;scheme=data;package=com.other.browser;end'" />
+    <meta http-equiv="refresh" content="0;URL='intent:PARAM_URL#Intent;action=android.intent.action.VIEW;scheme=https;package=com.other.browser;end'" />
     <script></script>
   </head>
   <body>
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js
index 5a044ae..a5ebd796 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js
@@ -46,6 +46,7 @@
           '/emoji_test_ordering_remaining.json',
         ],
         'emoticon': ['/emoticon_test_ordering.json'],
+        'symbol': ['/symbol_test_ordering.json'],
       },
     });
 
@@ -268,7 +269,7 @@
             'emoji-search', 'emoji-category-button:last-of-type',
             'cr-icon-button');
 
-        const emoticonTestGroupId = '10';
+        const emoticonTestGroupId = '20';
         emojiPicker.scrollToGroup(emoticonTestGroupId);
 
         await waitForCondition(
@@ -279,7 +280,7 @@
       'Scrolling to an emoticon group should activate the corresponding ' +
           'subcategory tab.',
       async () => {
-        const emoticonTestGroupId = '10';
+        const emoticonTestGroupId = '20';
         emojiPicker.scrollToGroup(emoticonTestGroupId);
         const emoticonTabButton = await waitForCondition(
             () => findInEmojiPicker(
@@ -293,7 +294,7 @@
   test('Scrolling to an emoticon group should update chevrons.', async () => {
     const leftChevron = findInEmojiPicker('#left-chevron');
     const rightChevron = findInEmojiPicker('#right-chevron');
-    const emoticonTestGroupId = '15';
+    const emoticonTestGroupId = '25';
 
     emojiPicker.scrollToGroup(emoticonTestGroupId);
     // when scrolling to the next page, the chevron display needs to be updated.
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_search_test.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_search_test.js
index 93db507..528542e 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_search_test.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_search_test.js
@@ -36,6 +36,7 @@
           '/emoji_test_ordering_remaining.json',
         ],
         'emoticon': ['/emoticon_test_ordering.json'],
+        'symbol': ['/symbol_test_ordering.json'],
       },
     });
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
index 0158d03f..fa26485 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.ts
@@ -512,7 +512,6 @@
     wallpaperProvider = new TestWallpaperProvider();
     personalizationStore = new TestPersonalizationStore({});
     personalizationStore.setReducersEnabled(true);
-    loadTimeData.resetForTesting({[fullscreenPreviewFeature]: true});
   });
 
   test(
@@ -715,8 +714,6 @@
   });
 
   test('error displays when fetch collections failed', async () => {
-    loadTimeData.overrideValues({['networkError']: 'someError'});
-
     // Set collections to null to simulate collections failure.
     wallpaperProvider.setCollectionsToFail();
 
@@ -745,7 +742,7 @@
           // Set collections.
           // Collections are completed loading with null value. Error displays.
           {
-            'error': {message: loadTimeData.getString('networkError')},
+            'error': {message: loadTimeData.getString('wallpaperNetworkError')},
           },
         ],
         personalizationStore.states.map(filterAndFlattenState(['error'])));
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index 19301c1..6933546 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -2095,10 +2095,9 @@
     const extensions::Extension* extension = LoadExtension(app_dir);
     ASSERT_TRUE(extension);
 
-    apps::AppLaunchParams params(extension->id(),
-                                 apps::LaunchContainer::kLaunchContainerNone,
-                                 WindowOpenDisposition::NEW_WINDOW,
-                                 apps::mojom::LaunchSource::kFromTest);
+    apps::AppLaunchParams params(
+        extension->id(), apps::LaunchContainer::kLaunchContainerNone,
+        WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromTest);
     params.command_line = *base::CommandLine::ForCurrentProcess();
     apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
         ->BrowserAppLauncher()
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index ca27751..2c93b6f 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -228,6 +228,11 @@
 }  // namespace
 
 int UpdaterMain(int argc, const char* const* argv) {
+#if BUILDFLAG(IS_WIN)
+  CHECK(EnableSecureDllLoading());
+  EnableProcessHeapMetadataProtection();
+#endif
+
   base::PlatformThread::SetName("UpdaterMain");
   base::AtExitManager exit_manager;
 
diff --git a/chrome/updater/win/BUILD.gn b/chrome/updater/win/BUILD.gn
index cf6a84a9..8396c0b5 100644
--- a/chrome/updater/win/BUILD.gn
+++ b/chrome/updater/win/BUILD.gn
@@ -33,17 +33,18 @@
   ]
 
   deps = [
-    "//build/win:default_exe_manifest",
     "//chrome/updater:base",
     "//chrome/updater/win/ui/resources",
   ]
 
+  public_configs = [ "//build/config/win:windowed" ]
+  public_deps = [ "//build/win:default_exe_manifest" ]
+
   data_deps = [ ":uninstall.cmd" ]
 }
 
 executable("updater") {
   configs -= [ "//build/config/compiler:cet_shadow_stack" ]
-  configs += [ "//build/config/win:windowed" ]
 
   deps = [
     ":updater_executable",
@@ -54,7 +55,6 @@
 
 executable("updater_test") {
   configs -= [ "//build/config/compiler:cet_shadow_stack" ]
-  configs += [ "//build/config/win:windowed" ]
 
   deps = [
     ":updater_executable",
diff --git a/chrome/updater/win/app_install_controller.cc b/chrome/updater/win/app_install_controller.cc
index 71bbb44..a5aa465 100644
--- a/chrome/updater/win/app_install_controller.cc
+++ b/chrome/updater/win/app_install_controller.cc
@@ -464,7 +464,7 @@
   bool DoRestartBrowser(bool restart_all_browsers,
                         const std::vector<std::u16string>& urls) override;
   bool DoReboot() override;
-  void DoCancel() override {}
+  void DoCancel() override;
 
   // Overrides for WTL::CMessageFilter.
   BOOL PreTranslateMessage(MSG* msg) override;
@@ -872,6 +872,13 @@
   return false;
 }
 
+void AppInstallControllerImpl::DoCancel() {
+  DCHECK_EQ(GetUIThreadID(), GetCurrentThreadId());
+  main_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&UpdateService::CancelInstalls, update_service_, app_id_));
+}
+
 }  // namespace
 
 scoped_refptr<App> MakeAppInstall() {
diff --git a/chrome/updater/win/installer/installer.cc b/chrome/updater/win/installer/installer.cc
index aaf76e9..56228e9 100644
--- a/chrome/updater/win/installer/installer.cc
+++ b/chrome/updater/win/installer/installer.cc
@@ -511,6 +511,9 @@
 }
 
 ProcessExitResult WMain(HMODULE module) {
+  CHECK(EnableSecureDllLoading());
+  EnableProcessHeapMetadataProtection();
+
   CommandString cmd_line_args;
   ProcessExitResult args_result = BuildCommandLineArguments(
       ::GetCommandLineW(), cmd_line_args.get(), cmd_line_args.capacity());
diff --git a/chrome/updater/win/ui/progress_wnd.cc b/chrome/updater/win/ui/progress_wnd.cc
index cc9a35f..7e6d56f0b 100644
--- a/chrome/updater/win/ui/progress_wnd.cc
+++ b/chrome/updater/win/ui/progress_wnd.cc
@@ -368,7 +368,9 @@
 
   // TODO(sorin): should use base::TimeDelta, https://crbug.com/1016921
   int time_remaining_sec = CeilingDivide(time_remaining_ms, kMsPerSec);
-  if (time_remaining_ms < 0) {
+  if (is_canceled_) {
+    s = GetLocalizedString(IDS_CANCELING_BASE);
+  } else if (time_remaining_ms < 0) {
     s = GetLocalizedString(IDS_DOWNLOADING_BASE);
   } else if (time_remaining_ms == 0) {
     s = GetLocalizedString(IDS_DOWNLOADING_COMPLETED_BASE);
diff --git a/chrome/updater/win/ui/splash_screen.cc b/chrome/updater/win/ui/splash_screen.cc
index 04ec1e23..5b27d9c 100644
--- a/chrome/updater/win/ui/splash_screen.cc
+++ b/chrome/updater/win/ui/splash_screen.cc
@@ -64,6 +64,24 @@
 
 void SplashScreen::Dismiss(base::OnceClosure on_close_closure) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // After the splash screen is dismissed, but before the progress UI is shown,
+  // there is a brief period of time when there are no windows for the current
+  // process.
+  //
+  // By default, Windows gives the previous foreground process (say the command
+  // line window) the foreground window at this point.
+  //
+  // To allow the subsequent progress UI to get foreground, the following call
+  // to `::LockSetForegroundWindow(LSFW_LOCK)` is made before closing the splash
+  // screen to prevent other applications from making a foreground change in
+  // between.
+  //
+  // To complete the cycle, the progress UI calls
+  // `::LockSetForegroundWindow(LSFW_UNLOCK)` before it calls
+  // `::SetForegroundWindow`.
+  ::LockSetForegroundWindow(LSFW_LOCK);
+
   on_close_closure_ = std::move(on_close_closure);
   switch (state_) {
     case WindowState::STATE_CREATED:
diff --git a/chrome/updater/win/ui/ui.cc b/chrome/updater/win/ui/ui.cc
index e3c1e4c7..a0b1508c 100644
--- a/chrome/updater/win/ui/ui.cc
+++ b/chrome/updater/win/ui/ui.cc
@@ -172,7 +172,16 @@
   CenterWindow(nullptr);
   SetVisible(true);
 
-  ::SetForegroundWindow(*this);
+  // To allow the progress UI to get foreground, the splash screen calls
+  // `::LockSetForegroundWindow(LSFW_LOCK)` before closing the splash screen to
+  // prevent other applications from making a foreground change in between. The
+  // following call completes the cycle with LSFW_UNLOCK.
+  ::LockSetForegroundWindow(LSFW_UNLOCK);
+
+  if (!::SetForegroundWindow(*this)) {
+    LOG(WARNING) << __func__
+                 << ": ::SetForegroundWindow failed: " << ::GetLastError();
+  }
 }
 
 bool OmahaWnd::OnComplete() {
diff --git a/chrome/updater/win/win_util.cc b/chrome/updater/win/win_util.cc
index 942fdf6..743457e 100644
--- a/chrome/updater/win/win_util.cc
+++ b/chrome/updater/win/win_util.cc
@@ -848,4 +848,33 @@
              : CompareOSVersionsInternal(os_version, kOSTypeMask, oper);
 }
 
+bool EnableSecureDllLoading() {
+  static const auto set_default_dll_directories =
+      reinterpret_cast<decltype(&::SetDefaultDllDirectories)>(::GetProcAddress(
+          ::GetModuleHandle(L"kernel32.dll"), "SetDefaultDllDirectories"));
+
+  if (!set_default_dll_directories)
+    return true;
+
+#if defined(COMPONENT_BUILD)
+  const DWORD directory_flags = LOAD_LIBRARY_SEARCH_DEFAULT_DIRS;
+#else
+  const DWORD directory_flags = LOAD_LIBRARY_SEARCH_SYSTEM32;
+#endif
+
+  return set_default_dll_directories(directory_flags);
+}
+
+bool EnableProcessHeapMetadataProtection() {
+  if (!::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, nullptr,
+                            0)) {
+    LOG(ERROR) << __func__
+               << ": Failed to enable heap metadata protection: " << std::hex
+               << HRESULTFromLastError();
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace updater
diff --git a/chrome/updater/win/win_util.h b/chrome/updater/win/win_util.h
index 4678295..a12693f2 100644
--- a/chrome/updater/win/win_util.h
+++ b/chrome/updater/win/win_util.h
@@ -266,6 +266,18 @@
 // call to `::GetVersionEx` or `::RtlGetVersion`.
 bool CompareOSVersions(const OSVERSIONINFOEX& os, BYTE oper);
 
+// This function calls ::SetDefaultDllDirectories to restrict DLL loads to
+// either full paths or %SYSTEM32%. ::SetDefaultDllDirectories is available on
+// Windows 8.1 and above, and on Windows Vista and above when KB2533623 is
+// applied.
+[[nodiscard]] bool EnableSecureDllLoading();
+
+// Enables metadata protection in the heap manager. This allows for the process
+// to be terminated immediately when a buffer overflow or illegal heap
+// operations are detected. This call enables protection for the entire process
+// and cannot be reversed.
+bool EnableProcessHeapMetadataProtection();
+
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_WIN_WIN_UTIL_H_
diff --git a/chrome/updater/win/win_util_unittest.cc b/chrome/updater/win/win_util_unittest.cc
index 944ba20..6a2a503 100644
--- a/chrome/updater/win/win_util_unittest.cc
+++ b/chrome/updater/win/win_util_unittest.cc
@@ -268,4 +268,12 @@
   EXPECT_EQ(is_com_caller_admin, ::IsUserAnAdmin());
 }
 
+TEST(WinUtil, EnableSecureDllLoading) {
+  EXPECT_TRUE(EnableSecureDllLoading());
+}
+
+TEST(WinUtil, EnableProcessHeapMetadataProtection) {
+  EXPECT_TRUE(EnableProcessHeapMetadataProtection());
+}
+
 }  // namespace updater
diff --git a/chromecast/cast_core/runtime/browser/BUILD.gn b/chromecast/cast_core/runtime/browser/BUILD.gn
index 14d0f70a..5fda6994 100644
--- a/chromecast/cast_core/runtime/browser/BUILD.gn
+++ b/chromecast/cast_core/runtime/browser/BUILD.gn
@@ -194,6 +194,7 @@
     "//components/cast_streaming/public/mojom",
     "//components/url_rewrite/browser",
     "//third_party/abseil-cpp:absl",
+    "//third_party/cast_core/public/src/proto/common:application_state_proto",
     "//third_party/cast_core/public/src/proto/common:value_proto",
     "//third_party/cast_core/public/src/proto/core:cast_core_service_proto_castcore",
     "//third_party/cast_core/public/src/proto/metrics:metrics_recorder_proto_castcore",
diff --git a/chromecast/cast_core/runtime/browser/runtime_application_base.cc b/chromecast/cast_core/runtime/browser/runtime_application_base.cc
index 2a223cc..734d3593 100644
--- a/chromecast/cast_core/runtime/browser/runtime_application_base.cc
+++ b/chromecast/cast_core/runtime/browser/runtime_application_base.cc
@@ -6,10 +6,12 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/notreached.h"
 #include "base/ranges/algorithm.h"
 #include "base/task/bind_post_task.h"
 #include "chromecast/browser/cast_web_service.h"
 #include "chromecast/browser/cast_web_view_factory.h"
+#include "chromecast/browser/visibility_types.h"
 #include "chromecast/cast_core/grpc/grpc_status_or.h"
 #include "chromecast/cast_core/runtime/browser/url_rewrite/url_request_rewrite_type_converters.h"
 #include "chromecast/common/feature_constants.h"
@@ -94,6 +96,18 @@
           task_runner_,
           base::BindRepeating(&RuntimeApplicationBase::HandleSetUrlRewriteRules,
                               weak_factory_.GetWeakPtr())));
+  grpc_server_
+      ->SetHandler<cast::v2::RuntimeApplicationServiceHandler::SetMediaState>(
+          base::BindPostTask(
+              task_runner_,
+              base::BindRepeating(&RuntimeApplicationBase::HandleSetMediaState,
+                                  weak_factory_.GetWeakPtr())));
+  grpc_server_
+      ->SetHandler<cast::v2::RuntimeApplicationServiceHandler::SetVisibility>(
+          base::BindPostTask(
+              task_runner_,
+              base::BindRepeating(&RuntimeApplicationBase::HandleSetVisibility,
+                                  weak_factory_.GetWeakPtr())));
   grpc_server_->SetHandler<
       cast::v2::RuntimeMessagePortApplicationServiceHandler::PostMessage>(
       base::BindPostTask(
@@ -147,11 +161,18 @@
   cast_media_service_grpc_endpoint_.emplace(
       request.cast_media_service_info().grpc_endpoint());
 
+  // Fallback to UNBLOCK if media_state is not defined.
+  SetMediaState(request.media_state() == cast::common::MediaState::UNDEFINED
+                    ? cast::common::MediaState::UNBLOCKED
+                    : request.media_state());
+  // Fallback to FULL_SCREEN if visibility is not defined.
+  SetVisibility(request.visibility() == cast::common::Visibility::UNDEFINED
+                    ? cast::common::Visibility::FULL_SCREEN
+                    : request.visibility());
+
   // Report that Cast application launch is initiated.
   std::move(callback).Run(grpc::Status::OK);
 
-  // Initiate application initialization flow where bindings and any extra setup
-  // happens.
   LaunchApplication();
 }
 
@@ -192,53 +213,77 @@
 }
 
 bool RuntimeApplicationBase::GetIsAudioOnly() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   const auto* entry =
       FindEntry(feature::kCastCoreIsAudioOnly, GetAppConfig().extra_features());
-  if (entry && entry->value().value_case() == cast::common::Value::kFlag) {
-    return entry->value().flag();
+  if (!entry) {
+    return false;
   }
-  return false;
+
+  DCHECK(entry->value().value_case() == cast::common::Value::kFlag);
+  return entry->value().flag();
+}
+
+bool RuntimeApplicationBase::GetIsRemoteControlMode() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  const auto* entry = FindEntry(feature::kCastCoreIsRemoteControlMode,
+                                GetAppConfig().extra_features());
+  if (!entry) {
+    return false;
+  }
+
+  DCHECK(entry->value().value_case() == cast::common::Value::kFlag);
+  return entry->value().flag();
 }
 
 bool RuntimeApplicationBase::GetEnforceFeaturePermissions() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   const auto* entry = FindEntry(feature::kCastCoreEnforceFeaturePermissions,
                                 GetAppConfig().extra_features());
-  if (entry && entry->value().value_case() == cast::common::Value::kFlag) {
-    return entry->value().flag();
+  if (!entry) {
+    return false;
   }
-  return false;
+
+  DCHECK(entry->value().value_case() == cast::common::Value::kFlag);
+  return entry->value().flag();
 }
 
 std::vector<int> RuntimeApplicationBase::GetFeaturePermissions() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::vector<int> feature_permissions;
   const auto* entry = FindEntry(feature::kCastCoreFeaturePermissions,
                                 GetAppConfig().extra_features());
-  if (entry) {
-    DCHECK(entry->value().value_case() == cast::common::Value::kArray);
-    base::ranges::for_each(
-        entry->value().array().values(),
-        [&feature_permissions](const cast::common::Value& value) {
-          DCHECK(value.value_case() == cast::common::Value::kNumber);
-          feature_permissions.push_back(value.number());
-        });
+  if (!entry) {
+    return feature_permissions;
   }
+
+  DCHECK(entry->value().value_case() == cast::common::Value::kArray);
+  base::ranges::for_each(
+      entry->value().array().values(),
+      [&feature_permissions](const cast::common::Value& value) {
+        DCHECK(value.value_case() == cast::common::Value::kNumber);
+        feature_permissions.push_back(value.number());
+      });
   return feature_permissions;
 }
 
 std::vector<std::string>
 RuntimeApplicationBase::GetAdditionalFeaturePermissionOrigins() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::vector<std::string> feature_permission_origins;
   const auto* entry = FindEntry(feature::kCastCoreFeaturePermissionOrigins,
                                 GetAppConfig().extra_features());
-  if (entry) {
-    DCHECK(entry->value().value_case() == cast::common::Value::kArray);
-    base::ranges::for_each(
-        entry->value().array().values(),
-        [&feature_permission_origins](const cast::common::Value& value) {
-          DCHECK(value.value_case() == cast::common::Value::kText);
-          feature_permission_origins.push_back(value.text());
-        });
+  if (!entry) {
+    return feature_permission_origins;
   }
+
+  DCHECK(entry->value().value_case() == cast::common::Value::kArray);
+  base::ranges::for_each(
+      entry->value().array().values(),
+      [&feature_permission_origins](const cast::common::Value& value) {
+        DCHECK(value.value_case() == cast::common::Value::kText);
+        feature_permission_origins.push_back(value.text());
+      });
   return feature_permission_origins;
 }
 
@@ -301,16 +346,40 @@
   reactor->Write(cast::v2::SetUrlRewriteRulesResponse());
 }
 
+void RuntimeApplicationBase::HandleSetMediaState(
+    cast::v2::SetMediaStateRequest request,
+    cast::v2::RuntimeApplicationServiceHandler::SetMediaState::Reactor*
+        reactor) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  SetMediaState(request.media_state());
+  reactor->Write(cast::v2::SetMediaStateResponse());
+}
+
+void RuntimeApplicationBase::HandleSetVisibility(
+    cast::v2::SetVisibilityRequest request,
+    cast::v2::RuntimeApplicationServiceHandler::SetVisibility::Reactor*
+        reactor) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  SetVisibility(request.visibility());
+  reactor->Write(cast::v2::SetVisibilityResponse());
+}
+
 void RuntimeApplicationBase::OnApplicationLaunched() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   LOG(INFO) << "Application is launched: " << *this;
 
   // Create the window and show the web view.
-  cast_web_view_->window()->GrantScreenAccess();
-  cast_web_view_->window()->CreateWindow(
-      ::chromecast::mojom::ZOrder::APP,
-      chromecast::VisibilityPriority::STICKY_ACTIVITY);
-  GetCastWebContents()->SetWebVisibilityAndPaint(true);
+  cast_web_view_->window()->AddObserver(this);
+  if (visibility_ == cast::common::Visibility::FULL_SCREEN) {
+    LOG(INFO) << "Loading application in full screen: " << *this;
+    cast_web_view_->window()->GrantScreenAccess();
+    cast_web_view_->window()->CreateWindow(mojom::ZOrder::APP,
+                                           VisibilityPriority::STICKY_ACTIVITY);
+  } else {
+    LOG(INFO) << "Loading application in background: " << *this;
+    cast_web_view_->window()->CreateWindow(mojom::ZOrder::APP,
+                                           VisibilityPriority::HIDDEN);
+  }
 
   // Notify Cast Core.
   auto call = core_app_stub_->CreateCall<
@@ -326,6 +395,10 @@
   params->renderer_type = renderer_type_;
   params->handle_inner_contents = true;
   params->session_id = GetCastSessionId();
+  params->is_remote_control_mode = GetIsRemoteControlMode();
+  params->activity_id = params->is_remote_control_mode
+                            ? params->session_id
+                            : GetAppConfig().app_id();
 #if DCHECK_IS_ON()
   params->enabled_for_dev = true;
 #endif
@@ -335,6 +408,76 @@
   return cast_web_view;
 }
 
+void RuntimeApplicationBase::SetMediaState(
+    cast::common::MediaState::Type media_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!cast_web_view_) {
+    return;
+  }
+
+  if (media_state == media_state_) {
+    // No actual update happened.
+    return;
+  }
+
+  media_state_ = media_state;
+  LOG(INFO) << "Media state updated: state="
+            << cast::common::MediaState::Type_Name(media_state_) << ", "
+            << *this;
+  switch (media_state_) {
+    case cast::common::MediaState::LOAD_BLOCKED:
+      GetCastWebContents()->BlockMediaLoading(true);
+      GetCastWebContents()->BlockMediaStarting(true);
+      break;
+
+    case cast::common::MediaState::START_BLOCKED:
+      GetCastWebContents()->BlockMediaLoading(false);
+      GetCastWebContents()->BlockMediaStarting(true);
+      break;
+
+    case cast::common::MediaState::UNBLOCKED:
+      GetCastWebContents()->BlockMediaLoading(false);
+      GetCastWebContents()->BlockMediaStarting(false);
+      break;
+
+    default:
+      NOTREACHED();
+  }
+}
+
+void RuntimeApplicationBase::SetVisibility(
+    cast::common::Visibility::Type visibility) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!cast_web_view_) {
+    return;
+  }
+
+  if (visibility == visibility_) {
+    // No actual update happened.
+    return;
+  }
+  visibility_ = visibility;
+
+  LOG(INFO) << "Visibility updated: state="
+            << cast::common::Visibility::Type_Name(visibility_) << ", "
+            << *this;
+  switch (visibility_) {
+    case cast::common::Visibility::FULL_SCREEN:
+      cast_web_view_->window()->RequestVisibility(
+          VisibilityPriority::STICKY_ACTIVITY);
+      cast_web_view_->window()->GrantScreenAccess();
+      break;
+
+    case cast::common::Visibility::HIDDEN:
+      cast_web_view_->window()->RequestVisibility(VisibilityPriority::HIDDEN);
+      cast_web_view_->window()->RevokeScreenAccess();
+      break;
+
+    default:
+      NOTREACHED();
+  }
+}
+
 void RuntimeApplicationBase::StopApplication(
     cast::v2::ApplicationStatusRequest::StopReason stop_reason) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -359,6 +502,10 @@
 
   if (cast_web_view_) {
     GetCastWebContents()->ClosePage();
+    // Check if window is still available as page might have been closed before.
+    if (cast_web_view_->window()) {
+      cast_web_view_->window()->RemoveObserver(this);
+    }
   }
 
   grpc_server_->Stop();
@@ -366,4 +513,23 @@
   LOG(INFO) << "Application is stopped: " << *this;
 }
 
+void RuntimeApplicationBase::OnVisibilityChange(
+    VisibilityType visibility_type) {
+  switch (visibility_type) {
+    case VisibilityType::FULL_SCREEN:
+    case VisibilityType::PARTIAL_OUT:
+    case VisibilityType::TRANSIENTLY_HIDDEN:
+      LOG(INFO) << "Application is visible now: " << *this;
+      GetCastWebContents()->SetWebVisibilityAndPaint(true);
+      break;
+    default:
+      LOG(INFO) << "Application is hidden now: " << *this;
+      GetCastWebContents()->SetWebVisibilityAndPaint(false);
+      break;
+  }
+
+  // TODO(vigeni): Record the metrics?
+  // RecordAppEvent(is_visible_ ? "AppShown" : "AppHidden");
+}
+
 }  // namespace chromecast
diff --git a/chromecast/cast_core/runtime/browser/runtime_application_base.h b/chromecast/cast_core/runtime/browser/runtime_application_base.h
index 4d0f62c..6ee9d9a 100644
--- a/chromecast/cast_core/runtime/browser/runtime_application_base.h
+++ b/chromecast/cast_core/runtime/browser/runtime_application_base.h
@@ -10,11 +10,13 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
+#include "chromecast/browser/cast_content_window.h"
 #include "chromecast/browser/cast_web_view.h"
 #include "chromecast/cast_core/grpc/grpc_server.h"
 #include "chromecast/cast_core/runtime/browser/runtime_application.h"
 #include "components/url_rewrite/browser/url_request_rewrite_rules_manager.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/cast_core/public/src/proto/common/application_state.pb.h"
 #include "third_party/cast_core/public/src/proto/common/value.pb.h"
 #include "third_party/cast_core/public/src/proto/v2/core_application_service.castcore.pb.h"
 #include "third_party/cast_core/public/src/proto/v2/core_message_port_application_service.castcore.pb.h"
@@ -27,7 +29,8 @@
 
 // This class is for sharing code between Web and streaming RuntimeApplication
 // implementations, including Load and Launch behavior.
-class RuntimeApplicationBase : public RuntimeApplication {
+class RuntimeApplicationBase : public RuntimeApplication,
+                               public CastContentWindow::Observer {
  public:
   ~RuntimeApplicationBase() override;
 
@@ -88,6 +91,8 @@
   base::Value GetRendererFeatures() const;
   // Returns if app is audio only.
   bool GetIsAudioOnly() const;
+  // Returns if remote control mode is enabled.
+  bool GetIsRemoteControlMode() const;
   // Returns if feature permissions are enforced.
   bool GetEnforceFeaturePermissions() const;
   // Returns feature permissions.
@@ -105,15 +110,30 @@
       cast::v2::SetUrlRewriteRulesRequest request,
       cast::v2::RuntimeApplicationServiceHandler::SetUrlRewriteRules::Reactor*
           reactor);
+  void HandleSetMediaState(
+      cast::v2::SetMediaStateRequest request,
+      cast::v2::RuntimeApplicationServiceHandler::SetMediaState::Reactor*
+          reactor);
+  void HandleSetVisibility(
+      cast::v2::SetVisibilityRequest request,
+      cast::v2::RuntimeApplicationServiceHandler::SetVisibility::Reactor*
+          reactor);
 
   // RuntimeMessagePortApplicationService handlers:
   void HandlePostMessage(cast::web::Message request,
                          cast::v2::RuntimeMessagePortApplicationServiceHandler::
                              PostMessage::Reactor* reactor);
 
+  // CastContentWindow::Observer implementation:
+  void OnVisibilityChange(VisibilityType visibility_type) override;
+
   // Creates the root CastWebView for this Cast session.
   CastWebView::Scoped CreateCastWebView();
 
+  // Sets content window state.
+  void SetMediaState(cast::common::MediaState::Type media_state);
+  void SetVisibility(cast::common::Visibility::Type visibility);
+
   const std::string cast_session_id_;
   const cast::common::ApplicationConfig app_config_;
 
@@ -137,6 +157,10 @@
   // Renderer type used by this application.
   mojom::RendererType renderer_type_;
 
+  cast::common::MediaState::Type media_state_ =
+      cast::common::MediaState::LOAD_BLOCKED;
+  cast::common::Visibility::Type visibility_ = cast::common::Visibility::HIDDEN;
+
   SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<RuntimeApplicationBase> weak_factory_{this};
 };
diff --git a/chromecast/common/feature_constants.cc b/chromecast/common/feature_constants.cc
index 437d9c3..3663b22 100644
--- a/chromecast/common/feature_constants.cc
+++ b/chromecast/common/feature_constants.cc
@@ -32,6 +32,7 @@
 const char kCastCoreFeaturePermissionOrigins[] =
     "cast_core_feature_permission_origins";
 const char kCastCoreIsAudioOnly[] = "cast_core_is_audio_only";
+const char kCastCoreIsRemoteControlMode[] = "cast_core_is_remote_control_mode";
 
 }  // namespace feature
 }  // namespace chromecast
diff --git a/chromecast/common/feature_constants.h b/chromecast/common/feature_constants.h
index 58c3098d..1251f7b 100644
--- a/chromecast/common/feature_constants.h
+++ b/chromecast/common/feature_constants.h
@@ -57,6 +57,7 @@
 extern const char kCastCoreFeaturePermissions[];
 extern const char kCastCoreFeaturePermissionOrigins[];
 extern const char kCastCoreIsAudioOnly[];
+extern const char kCastCoreIsRemoteControlMode[];
 
 }  // namespace feature
 }  // namespace chromecast
diff --git a/chromeos/ash/components/dbus/BUILD.gn b/chromeos/ash/components/dbus/BUILD.gn
index 65c9f4a..537fc590 100644
--- a/chromeos/ash/components/dbus/BUILD.gn
+++ b/chromeos/ash/components/dbus/BUILD.gn
@@ -18,6 +18,7 @@
     "//chromeos/ash/components/dbus/authpolicy",
     "//chromeos/ash/components/dbus/authpolicy:authpolicy_proto",
     "//chromeos/ash/components/dbus/biod:test_support",
+    "//chromeos/ash/components/dbus/gnubby:unit_tests",
     "//chromeos/ash/components/dbus/hermes:test_support",
     "//chromeos/ash/components/dbus/hiberman",
     "//chromeos/ash/components/dbus/ip_peripheral:test_support",
diff --git a/chromeos/dbus/arc/BUILD.gn b/chromeos/ash/components/dbus/arc/BUILD.gn
similarity index 86%
rename from chromeos/dbus/arc/BUILD.gn
rename to chromeos/ash/components/dbus/arc/BUILD.gn
index 0feaa12..edccd31 100644
--- a/chromeos/dbus/arc/BUILD.gn
+++ b/chromeos/ash/components/dbus/arc/BUILD.gn
@@ -2,11 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash, "Non-ChromeOS builds cannot depend on //chromeos/ash")
 
 component("arc") {
-  output_name = "chromeos_dbus_arc"
-  defines = [ "IS_CHROMEOS_DBUS_ARC_IMPL" ]
+  output_name = "ash_dbus_arc"
+  defines = [ "IS_ASH_DBUS_ARC_IMPL" ]
 
   deps = [
     "//base",
diff --git a/chromeos/dbus/arc/DIR_METADATA b/chromeos/ash/components/dbus/arc/DIR_METADATA
similarity index 100%
rename from chromeos/dbus/arc/DIR_METADATA
rename to chromeos/ash/components/dbus/arc/DIR_METADATA
diff --git a/chromeos/dbus/arc/OWNERS b/chromeos/ash/components/dbus/arc/OWNERS
similarity index 100%
rename from chromeos/dbus/arc/OWNERS
rename to chromeos/ash/components/dbus/arc/OWNERS
diff --git a/chromeos/dbus/arc/arc_appfuse_provider_client.cc b/chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.cc
similarity index 95%
rename from chromeos/dbus/arc/arc_appfuse_provider_client.cc
rename to chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.cc
index 3f23f08b..117d553 100644
--- a/chromeos/dbus/arc/arc_appfuse_provider_client.cc
+++ b/chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/arc_appfuse_provider_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.h"
 
 #include <utility>
 
@@ -11,13 +11,13 @@
 #include "base/callback_helpers.h"
 #include "base/check_op.h"
 #include "base/logging.h"
-#include "chromeos/dbus/arc/fake_arc_appfuse_provider_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_appfuse_provider_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -149,4 +149,4 @@
   g_instance = nullptr;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/arc_appfuse_provider_client.h b/chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.h
similarity index 85%
rename from chromeos/dbus/arc/arc_appfuse_provider_client.h
rename to chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.h
index f1184aa..aa6e31f 100644
--- a/chromeos/dbus/arc/arc_appfuse_provider_client.h
+++ b/chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.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 CHROMEOS_DBUS_ARC_ARC_APPFUSE_PROVIDER_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_ARC_APPFUSE_PROVIDER_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_APPFUSE_PROVIDER_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_APPFUSE_PROVIDER_CLIENT_H_
 
 #include <stdint.h>
 
@@ -12,13 +12,13 @@
 #include "chromeos/dbus/common/dbus_client.h"
 #include "chromeos/dbus/common/dbus_method_call_status.h"
 
-namespace chromeos {
+namespace ash {
 
 // ArcAppfuseProviderClient is used to communicate with the ArcAppfuseProvider
 // service which provides ProxyFileDescriptor (aka appfuse) feature for ARC. All
 // methods should be called from the origin thread (UI thread) which initializes
 // the DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) ArcAppfuseProviderClient
+class COMPONENT_EXPORT(ASH_DBUS_ARC) ArcAppfuseProviderClient
     : public DBusClient {
  public:
   // Returns the global instance if initialized. May return null.
@@ -56,6 +56,6 @@
   ~ArcAppfuseProviderClient() override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_ARC_APPFUSE_PROVIDER_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_APPFUSE_PROVIDER_CLIENT_H_
diff --git a/chromeos/dbus/arc/arc_camera_client.cc b/chromeos/ash/components/dbus/arc/arc_camera_client.cc
similarity index 93%
rename from chromeos/dbus/arc/arc_camera_client.cc
rename to chromeos/ash/components/dbus/arc/arc_camera_client.cc
index 6c398c4..a1ba522 100644
--- a/chromeos/dbus/arc/arc_camera_client.cc
+++ b/chromeos/ash/components/dbus/arc/arc_camera_client.cc
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/arc_camera_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_camera_client.h"
 
 #include <utility>
 
 #include "base/bind.h"
 #include "base/check_op.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/dbus/arc/fake_arc_camera_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_camera_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -94,4 +94,4 @@
   return g_instance;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/arc_camera_client.h b/chromeos/ash/components/dbus/arc/arc_camera_client.h
similarity index 80%
rename from chromeos/dbus/arc/arc_camera_client.h
rename to chromeos/ash/components/dbus/arc/arc_camera_client.h
index f666e42..91ca2a7 100644
--- a/chromeos/dbus/arc/arc_camera_client.h
+++ b/chromeos/ash/components/dbus/arc/arc_camera_client.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 CHROMEOS_DBUS_ARC_ARC_CAMERA_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_ARC_CAMERA_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_CAMERA_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_CAMERA_CLIENT_H_
 
 #include <string>
 
@@ -13,11 +13,11 @@
 
 #include "base/files/scoped_file.h"
 
-namespace chromeos {
+namespace ash {
 
 // ArcCameraClient is used to communicate with the arc-camera service for ARC
 // Camera HAL v1.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) ArcCameraClient {
+class COMPONENT_EXPORT(ASH_DBUS_ARC) ArcCameraClient {
  public:
   // Creates and initializes the global instance. |bus| must not be null.
   static void Initialize(dbus::Bus* bus);
@@ -46,11 +46,6 @@
   virtual ~ArcCameraClient();
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove when moved to ash.
-namespace ash {
-using ::chromeos::ArcCameraClient;
 }  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_ARC_CAMERA_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_CAMERA_CLIENT_H_
diff --git a/chromeos/dbus/arc/arc_data_snapshotd_client.cc b/chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.cc
similarity index 97%
rename from chromeos/dbus/arc/arc_data_snapshotd_client.cc
rename to chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.cc
index fc4dca2..d81cad1 100644
--- a/chromeos/dbus/arc/arc_data_snapshotd_client.cc
+++ b/chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/arc_data_snapshotd_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.h"
 
 #include <memory>
 
@@ -11,12 +11,12 @@
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/dbus/arc/fake_arc_data_snapshotd_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "third_party/cros_system_api/dbus/arc-data-snapshotd/dbus-constants.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -218,4 +218,4 @@
   g_instance = nullptr;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/arc_data_snapshotd_client.h b/chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.h
similarity index 90%
rename from chromeos/dbus/arc/arc_data_snapshotd_client.h
rename to chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.h
index cf284be..724dff9 100644
--- a/chromeos/dbus/arc/arc_data_snapshotd_client.h
+++ b/chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.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 CHROMEOS_DBUS_ARC_ARC_DATA_SNAPSHOTD_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_ARC_DATA_SNAPSHOTD_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_DATA_SNAPSHOTD_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_DATA_SNAPSHOTD_CLIENT_H_
 
 #include <string>
 
@@ -13,11 +13,11 @@
 #include "chromeos/dbus/common/dbus_method_call_status.h"
 #include "dbus/object_proxy.h"
 
-namespace chromeos {
+namespace ash {
 
 // ArcDataSnapshotdClient is used to delegate ARC data/ snapshot related tasks
 // to arc-data-snapshotd daemon in Chrome OS.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) ArcDataSnapshotdClient
+class COMPONENT_EXPORT(ASH_DBUS_ARC) ArcDataSnapshotdClient
     : public DBusClient {
  public:
   // Returns the global instance if initialized. May return null.
@@ -82,6 +82,6 @@
   ~ArcDataSnapshotdClient() override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_ARC_DATA_SNAPSHOTD_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_DATA_SNAPSHOTD_CLIENT_H_
diff --git a/chromeos/dbus/arc/arc_keymaster_client.cc b/chromeos/ash/components/dbus/arc/arc_keymaster_client.cc
similarity index 93%
rename from chromeos/dbus/arc/arc_keymaster_client.cc
rename to chromeos/ash/components/dbus/arc/arc_keymaster_client.cc
index f469af3a..04cc9e1 100644
--- a/chromeos/dbus/arc/arc_keymaster_client.cc
+++ b/chromeos/ash/components/dbus/arc/arc_keymaster_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/arc_keymaster_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_keymaster_client.h"
 
 #include <memory>
 #include <utility>
@@ -10,13 +10,13 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
-#include "chromeos/dbus/arc/fake_arc_keymaster_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_keymaster_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -96,4 +96,4 @@
   g_instance = nullptr;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/arc_keymaster_client.h b/chromeos/ash/components/dbus/arc/arc_keymaster_client.h
similarity index 80%
rename from chromeos/dbus/arc/arc_keymaster_client.h
rename to chromeos/ash/components/dbus/arc/arc_keymaster_client.h
index ea91a1b..ce35f16 100644
--- a/chromeos/dbus/arc/arc_keymaster_client.h
+++ b/chromeos/ash/components/dbus/arc/arc_keymaster_client.h
@@ -2,19 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_ARC_ARC_KEYMASTER_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_ARC_KEYMASTER_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_KEYMASTER_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_KEYMASTER_CLIENT_H_
 
 #include "base/files/scoped_file.h"
 #include "chromeos/dbus/common/dbus_client.h"
 #include "chromeos/dbus/common/dbus_method_call_status.h"
 
-namespace chromeos {
+namespace ash {
 
 // ArcKeymasterClient is used to bootstrap a Mojo connection with the
 // arc-keymasterd daemon in Chrome OS.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) ArcKeymasterClient
-    : public DBusClient {
+class COMPONENT_EXPORT(ASH_DBUS_ARC) ArcKeymasterClient : public DBusClient {
  public:
   // Returns the global instance if initialized. May return null.
   static ArcKeymasterClient* Get();
@@ -42,6 +41,6 @@
   ~ArcKeymasterClient() override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_ARC_KEYMASTER_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_KEYMASTER_CLIENT_H_
diff --git a/chromeos/dbus/arc/arc_midis_client.cc b/chromeos/ash/components/dbus/arc/arc_midis_client.cc
similarity index 94%
rename from chromeos/dbus/arc/arc_midis_client.cc
rename to chromeos/ash/components/dbus/arc/arc_midis_client.cc
index d4758a2..5411e6b 100644
--- a/chromeos/dbus/arc/arc_midis_client.cc
+++ b/chromeos/ash/components/dbus/arc/arc_midis_client.cc
@@ -1,7 +1,7 @@
 // Copyright 2017 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 "chromeos/dbus/arc/arc_midis_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_midis_client.h"
 
 #include <memory>
 #include <utility>
@@ -9,13 +9,13 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
-#include "chromeos/dbus/arc/fake_arc_midis_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_midis_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -102,4 +102,4 @@
   g_instance = nullptr;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/arc_midis_client.h b/chromeos/ash/components/dbus/arc/arc_midis_client.h
similarity index 82%
rename from chromeos/dbus/arc/arc_midis_client.h
rename to chromeos/ash/components/dbus/arc/arc_midis_client.h
index a1aeb66..8cb0a7d3 100644
--- a/chromeos/dbus/arc/arc_midis_client.h
+++ b/chromeos/ash/components/dbus/arc/arc_midis_client.h
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_ARC_ARC_MIDIS_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_ARC_MIDIS_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_MIDIS_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_MIDIS_CLIENT_H_
 
 #include "base/component_export.h"
 #include "base/files/scoped_file.h"
 #include "chromeos/dbus/common/dbus_client.h"
 #include "chromeos/dbus/common/dbus_method_call_status.h"
 
-namespace chromeos {
+namespace ash {
 
 // ArcMidisClient is used to pass an FD to the midis daemon for the purpose
 // of setting up a Mojo channel. It is expected to be called once during browser
 // initialization.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) ArcMidisClient : public DBusClient {
+class COMPONENT_EXPORT(ASH_DBUS_ARC) ArcMidisClient : public DBusClient {
  public:
   // Returns the global instance if initialized. May return null.
   static ArcMidisClient* Get();
@@ -43,6 +43,6 @@
   ~ArcMidisClient() override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_ARC_MIDIS_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_MIDIS_CLIENT_H_
diff --git a/chromeos/dbus/arc/arc_obb_mounter_client.cc b/chromeos/ash/components/dbus/arc/arc_obb_mounter_client.cc
similarity index 94%
rename from chromeos/dbus/arc/arc_obb_mounter_client.cc
rename to chromeos/ash/components/dbus/arc/arc_obb_mounter_client.cc
index 688492c..2ca76af5 100644
--- a/chromeos/dbus/arc/arc_obb_mounter_client.cc
+++ b/chromeos/ash/components/dbus/arc/arc_obb_mounter_client.cc
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/arc_obb_mounter_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_obb_mounter_client.h"
 
 #include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
-#include "chromeos/dbus/arc/fake_arc_obb_mounter_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_obb_mounter_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -112,4 +112,4 @@
   g_instance = nullptr;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/arc_obb_mounter_client.h b/chromeos/ash/components/dbus/arc/arc_obb_mounter_client.h
similarity index 83%
rename from chromeos/dbus/arc/arc_obb_mounter_client.h
rename to chromeos/ash/components/dbus/arc/arc_obb_mounter_client.h
index 2a9290c..01ad4e0f 100644
--- a/chromeos/dbus/arc/arc_obb_mounter_client.h
+++ b/chromeos/ash/components/dbus/arc/arc_obb_mounter_client.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 CHROMEOS_DBUS_ARC_ARC_OBB_MOUNTER_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_ARC_OBB_MOUNTER_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_OBB_MOUNTER_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_OBB_MOUNTER_CLIENT_H_
 
 #include <stdint.h>
 
@@ -13,14 +13,13 @@
 #include "chromeos/dbus/common/dbus_client.h"
 #include "chromeos/dbus/common/dbus_method_call_status.h"
 
-namespace chromeos {
+namespace ash {
 
 // ArcObbMounterClient is used to communicate with the ArcObbMounter service
 // which mounts OBB (opaque binary blob - https://goo.gl/ja8aN1) files.
 // All method should be called from the origin thread (UI thread) which
 // initializes the DBusThreadManager instance.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) ArcObbMounterClient
-    : public DBusClient {
+class COMPONENT_EXPORT(ASH_DBUS_ARC) ArcObbMounterClient : public DBusClient {
  public:
   // Returns the global instance if initialized. May return null.
   static ArcObbMounterClient* Get();
@@ -51,6 +50,6 @@
   ~ArcObbMounterClient() override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_ARC_OBB_MOUNTER_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_OBB_MOUNTER_CLIENT_H_
diff --git a/chromeos/dbus/arc/arc_sensor_service_client.cc b/chromeos/ash/components/dbus/arc/arc_sensor_service_client.cc
similarity index 93%
rename from chromeos/dbus/arc/arc_sensor_service_client.cc
rename to chromeos/ash/components/dbus/arc/arc_sensor_service_client.cc
index 8073599..f403d905 100644
--- a/chromeos/dbus/arc/arc_sensor_service_client.cc
+++ b/chromeos/ash/components/dbus/arc/arc_sensor_service_client.cc
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/arc_sensor_service_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_sensor_service_client.h"
 
 #include <utility>
 
 #include "base/bind.h"
 #include "base/check_op.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/dbus/arc/fake_arc_sensor_service_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_sensor_service_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -95,4 +95,4 @@
   return g_instance;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/arc_sensor_service_client.h b/chromeos/ash/components/dbus/arc/arc_sensor_service_client.h
similarity index 78%
rename from chromeos/dbus/arc/arc_sensor_service_client.h
rename to chromeos/ash/components/dbus/arc/arc_sensor_service_client.h
index 5c74147..03b1c85 100644
--- a/chromeos/dbus/arc/arc_sensor_service_client.h
+++ b/chromeos/ash/components/dbus/arc/arc_sensor_service_client.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 CHROMEOS_DBUS_ARC_ARC_SENSOR_SERVICE_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_ARC_SENSOR_SERVICE_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_SENSOR_SERVICE_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_SENSOR_SERVICE_CLIENT_H_
 
 #include <string>
 
@@ -12,10 +12,10 @@
 #include "dbus/bus.h"
 #include "dbus/message.h"
 
-namespace chromeos {
+namespace ash {
 
 // ArcSensorServiceClient is used to communicate with arc-sensor-service.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) ArcSensorServiceClient {
+class COMPONENT_EXPORT(ASH_DBUS_ARC) ArcSensorServiceClient {
  public:
   // Creates and initializes the global instance. |bus| must not be null.
   static void Initialize(dbus::Bus* bus);
@@ -42,11 +42,6 @@
   ArcSensorServiceClient& operator=(const ArcSensorServiceClient&) = delete;
 };
 
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove when moved to ash.
-namespace ash {
-using ::chromeos::ArcSensorServiceClient;
 }  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_ARC_SENSOR_SERVICE_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_ARC_SENSOR_SERVICE_CLIENT_H_
diff --git a/chromeos/dbus/arc/fake_arc_appfuse_provider_client.cc b/chromeos/ash/components/dbus/arc/fake_arc_appfuse_provider_client.cc
similarity index 91%
rename from chromeos/dbus/arc/fake_arc_appfuse_provider_client.cc
rename to chromeos/ash/components/dbus/arc/fake_arc_appfuse_provider_client.cc
index 12bb2a3..14a7fe0e 100644
--- a/chromeos/dbus/arc/fake_arc_appfuse_provider_client.cc
+++ b/chromeos/ash/components/dbus/arc/fake_arc_appfuse_provider_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/fake_arc_appfuse_provider_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_appfuse_provider_client.h"
 
 #include <utility>
 
@@ -10,7 +10,7 @@
 #include "base/callback.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-namespace chromeos {
+namespace ash {
 
 FakeArcAppfuseProviderClient::FakeArcAppfuseProviderClient() = default;
 
@@ -43,4 +43,4 @@
       FROM_HERE, base::BindOnce(std::move(callback), base::ScopedFD()));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/fake_arc_appfuse_provider_client.h b/chromeos/ash/components/dbus/arc/fake_arc_appfuse_provider_client.h
similarity index 72%
rename from chromeos/dbus/arc/fake_arc_appfuse_provider_client.h
rename to chromeos/ash/components/dbus/arc/fake_arc_appfuse_provider_client.h
index 9c04252..b4548e2 100644
--- a/chromeos/dbus/arc/fake_arc_appfuse_provider_client.h
+++ b/chromeos/ash/components/dbus/arc/fake_arc_appfuse_provider_client.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_ARC_FAKE_ARC_APPFUSE_PROVIDER_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_FAKE_ARC_APPFUSE_PROVIDER_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_APPFUSE_PROVIDER_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_APPFUSE_PROVIDER_CLIENT_H_
 
-#include "chromeos/dbus/arc/arc_appfuse_provider_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_appfuse_provider_client.h"
 
-namespace chromeos {
+namespace ash {
 
 // A fake implementation of ArcAppfuseProviderClient.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) FakeArcAppfuseProviderClient
+class COMPONENT_EXPORT(ASH_DBUS_ARC) FakeArcAppfuseProviderClient
     : public ArcAppfuseProviderClient {
  public:
   FakeArcAppfuseProviderClient();
@@ -38,6 +38,6 @@
                 DBusMethodCallback<base::ScopedFD> callback) override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_FAKE_ARC_APPFUSE_PROVIDER_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_APPFUSE_PROVIDER_CLIENT_H_
diff --git a/chromeos/dbus/arc/fake_arc_camera_client.cc b/chromeos/ash/components/dbus/arc/fake_arc_camera_client.cc
similarity index 90%
rename from chromeos/dbus/arc/fake_arc_camera_client.cc
rename to chromeos/ash/components/dbus/arc/fake_arc_camera_client.cc
index fd59c65..7c21cc10 100644
--- a/chromeos/dbus/arc/fake_arc_camera_client.cc
+++ b/chromeos/ash/components/dbus/arc/fake_arc_camera_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/fake_arc_camera_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_camera_client.h"
 
 #include <utility>
 
@@ -10,7 +10,7 @@
 #include "base/check_op.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -41,4 +41,4 @@
       FROM_HERE, base::BindOnce(std::move(callback), true));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/fake_arc_camera_client.h b/chromeos/ash/components/dbus/arc/fake_arc_camera_client.h
similarity index 68%
rename from chromeos/dbus/arc/fake_arc_camera_client.h
rename to chromeos/ash/components/dbus/arc/fake_arc_camera_client.h
index 77417591..ffc2c48 100644
--- a/chromeos/dbus/arc/fake_arc_camera_client.h
+++ b/chromeos/ash/components/dbus/arc/fake_arc_camera_client.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_ARC_FAKE_ARC_CAMERA_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_FAKE_ARC_CAMERA_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_CAMERA_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_CAMERA_CLIENT_H_
 
-#include "chromeos/dbus/arc/arc_camera_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_camera_client.h"
 
-namespace chromeos {
+namespace ash {
 
 // Fake implementation of ArcCameraClient.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) FakeArcCameraClient
+class COMPONENT_EXPORT(ASH_DBUS_ARC) FakeArcCameraClient
     : public ArcCameraClient {
  public:
   // Returns the fake global instance if initialized. May return null.
@@ -31,6 +31,6 @@
   ~FakeArcCameraClient() override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_FAKE_ARC_CAMERA_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_CAMERA_CLIENT_H_
diff --git a/chromeos/dbus/arc/fake_arc_data_snapshotd_client.cc b/chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.cc
similarity index 95%
rename from chromeos/dbus/arc/fake_arc_data_snapshotd_client.cc
rename to chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.cc
index 4b0eae2..c8e7a64 100644
--- a/chromeos/dbus/arc/fake_arc_data_snapshotd_client.cc
+++ b/chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/fake_arc_data_snapshotd_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.h"
 
 #include <utility>
 
@@ -11,7 +11,7 @@
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-namespace chromeos {
+namespace ash {
 
 FakeArcDataSnapshotdClient::FakeArcDataSnapshotdClient() = default;
 FakeArcDataSnapshotdClient::~FakeArcDataSnapshotdClient() = default;
@@ -65,4 +65,4 @@
       FROM_HERE, base::BindOnce(std::move(callback), is_available_));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/fake_arc_data_snapshotd_client.h b/chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.h
similarity index 80%
rename from chromeos/dbus/arc/fake_arc_data_snapshotd_client.h
rename to chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.h
index 98a7eb81..f8ede92 100644
--- a/chromeos/dbus/arc/fake_arc_data_snapshotd_client.h
+++ b/chromeos/ash/components/dbus/arc/fake_arc_data_snapshotd_client.h
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_ARC_FAKE_ARC_DATA_SNAPSHOTD_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_FAKE_ARC_DATA_SNAPSHOTD_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_DATA_SNAPSHOTD_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_DATA_SNAPSHOTD_CLIENT_H_
 
 #include <string>
 
-#include "chromeos/dbus/arc/arc_data_snapshotd_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_data_snapshotd_client.h"
 
-namespace chromeos {
+namespace ash {
 
 // A fake implementation of ArcDataSnapshotdClient.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) FakeArcDataSnapshotdClient
+class COMPONENT_EXPORT(ASH_DBUS_ARC) FakeArcDataSnapshotdClient
     : public ArcDataSnapshotdClient {
  public:
   FakeArcDataSnapshotdClient();
@@ -56,6 +56,6 @@
   base::RepeatingClosure signal_callback_;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_FAKE_ARC_DATA_SNAPSHOTD_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_DATA_SNAPSHOTD_CLIENT_H_
diff --git a/chromeos/dbus/arc/fake_arc_keymaster_client.cc b/chromeos/ash/components/dbus/arc/fake_arc_keymaster_client.cc
similarity index 84%
rename from chromeos/dbus/arc/fake_arc_keymaster_client.cc
rename to chromeos/ash/components/dbus/arc/fake_arc_keymaster_client.cc
index 6e058496..f9bb8a25 100644
--- a/chromeos/dbus/arc/fake_arc_keymaster_client.cc
+++ b/chromeos/ash/components/dbus/arc/fake_arc_keymaster_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/fake_arc_keymaster_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_keymaster_client.h"
 
 #include <utility>
 
@@ -10,7 +10,7 @@
 #include "base/callback.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-namespace chromeos {
+namespace ash {
 
 void FakeArcKeymasterClient::Init(dbus::Bus* bus) {}
 
@@ -21,4 +21,4 @@
       FROM_HERE, base::BindOnce(std::move(callback), false));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/fake_arc_keymaster_client.h b/chromeos/ash/components/dbus/arc/fake_arc_keymaster_client.h
similarity index 65%
rename from chromeos/dbus/arc/fake_arc_keymaster_client.h
rename to chromeos/ash/components/dbus/arc/fake_arc_keymaster_client.h
index e02a5c3..8b46744 100644
--- a/chromeos/dbus/arc/fake_arc_keymaster_client.h
+++ b/chromeos/ash/components/dbus/arc/fake_arc_keymaster_client.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_ARC_FAKE_ARC_KEYMASTER_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_FAKE_ARC_KEYMASTER_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_KEYMASTER_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_KEYMASTER_CLIENT_H_
 
-#include "chromeos/dbus/arc/arc_keymaster_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_keymaster_client.h"
 
-namespace chromeos {
+namespace ash {
 
 // A fake implementation of ArcKeymasterClient.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) FakeArcKeymasterClient
+class COMPONENT_EXPORT(ASH_DBUS_ARC) FakeArcKeymasterClient
     : public ArcKeymasterClient {
  public:
   FakeArcKeymasterClient() = default;
@@ -28,6 +28,6 @@
                                VoidDBusMethodCallback callback) override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_FAKE_ARC_KEYMASTER_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_KEYMASTER_CLIENT_H_
diff --git a/chromeos/dbus/arc/fake_arc_midis_client.cc b/chromeos/ash/components/dbus/arc/fake_arc_midis_client.cc
similarity index 84%
rename from chromeos/dbus/arc/fake_arc_midis_client.cc
rename to chromeos/ash/components/dbus/arc/fake_arc_midis_client.cc
index eac3dd18..3c5b9ac 100644
--- a/chromeos/dbus/arc/fake_arc_midis_client.cc
+++ b/chromeos/ash/components/dbus/arc/fake_arc_midis_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/fake_arc_midis_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_midis_client.h"
 
 #include <utility>
 
@@ -10,7 +10,7 @@
 #include "base/callback.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-namespace chromeos {
+namespace ash {
 
 void FakeArcMidisClient::Init(dbus::Bus* bus) {}
 
@@ -21,4 +21,4 @@
       FROM_HERE, base::BindOnce(std::move(callback), false));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/fake_arc_midis_client.h b/chromeos/ash/components/dbus/arc/fake_arc_midis_client.h
similarity index 65%
rename from chromeos/dbus/arc/fake_arc_midis_client.h
rename to chromeos/ash/components/dbus/arc/fake_arc_midis_client.h
index 4d582c0..caef4a17 100644
--- a/chromeos/dbus/arc/fake_arc_midis_client.h
+++ b/chromeos/ash/components/dbus/arc/fake_arc_midis_client.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_ARC_FAKE_ARC_MIDIS_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_FAKE_ARC_MIDIS_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_MIDIS_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_MIDIS_CLIENT_H_
 
-#include "chromeos/dbus/arc/arc_midis_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_midis_client.h"
 
-namespace chromeos {
+namespace ash {
 
 // A fake implementation of ArcMidisClient.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) FakeArcMidisClient
+class COMPONENT_EXPORT(ASH_DBUS_ARC) FakeArcMidisClient
     : public ArcMidisClient {
  public:
   FakeArcMidisClient() = default;
@@ -28,6 +28,6 @@
                                VoidDBusMethodCallback callback) override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_FAKE_ARC_MIDIS_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_MIDIS_CLIENT_H_
diff --git a/chromeos/dbus/arc/fake_arc_obb_mounter_client.cc b/chromeos/ash/components/dbus/arc/fake_arc_obb_mounter_client.cc
similarity index 90%
rename from chromeos/dbus/arc/fake_arc_obb_mounter_client.cc
rename to chromeos/ash/components/dbus/arc/fake_arc_obb_mounter_client.cc
index 66b67ae..d2aa782 100644
--- a/chromeos/dbus/arc/fake_arc_obb_mounter_client.cc
+++ b/chromeos/ash/components/dbus/arc/fake_arc_obb_mounter_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/fake_arc_obb_mounter_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_obb_mounter_client.h"
 
 #include <utility>
 
@@ -10,7 +10,7 @@
 #include "base/callback.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-namespace chromeos {
+namespace ash {
 
 FakeArcObbMounterClient::FakeArcObbMounterClient() = default;
 
@@ -32,4 +32,4 @@
       FROM_HERE, base::BindOnce(std::move(callback), false));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/fake_arc_obb_mounter_client.h b/chromeos/ash/components/dbus/arc/fake_arc_obb_mounter_client.h
similarity index 69%
rename from chromeos/dbus/arc/fake_arc_obb_mounter_client.h
rename to chromeos/ash/components/dbus/arc/fake_arc_obb_mounter_client.h
index 01ec49d..0a1ee97 100644
--- a/chromeos/dbus/arc/fake_arc_obb_mounter_client.h
+++ b/chromeos/ash/components/dbus/arc/fake_arc_obb_mounter_client.h
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_ARC_FAKE_ARC_OBB_MOUNTER_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_FAKE_ARC_OBB_MOUNTER_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_OBB_MOUNTER_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_OBB_MOUNTER_CLIENT_H_
 
 #include <string>
 
-#include "chromeos/dbus/arc/arc_obb_mounter_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_obb_mounter_client.h"
 
-namespace chromeos {
+namespace ash {
 
 // A fake implementation of ArcObbMounterClient.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) FakeArcObbMounterClient
+class COMPONENT_EXPORT(ASH_DBUS_ARC) FakeArcObbMounterClient
     : public ArcObbMounterClient {
  public:
   FakeArcObbMounterClient();
@@ -34,6 +34,6 @@
                   VoidDBusMethodCallback callback) override;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_FAKE_ARC_OBB_MOUNTER_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_OBB_MOUNTER_CLIENT_H_
diff --git a/chromeos/dbus/arc/fake_arc_sensor_service_client.cc b/chromeos/ash/components/dbus/arc/fake_arc_sensor_service_client.cc
similarity index 89%
rename from chromeos/dbus/arc/fake_arc_sensor_service_client.cc
rename to chromeos/ash/components/dbus/arc/fake_arc_sensor_service_client.cc
index 9abe8b63..f0e17b07 100644
--- a/chromeos/dbus/arc/fake_arc_sensor_service_client.cc
+++ b/chromeos/ash/components/dbus/arc/fake_arc_sensor_service_client.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/arc/fake_arc_sensor_service_client.h"
+#include "chromeos/ash/components/dbus/arc/fake_arc_sensor_service_client.h"
 
 #include <utility>
 
@@ -10,7 +10,7 @@
 #include "base/check_op.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -42,4 +42,4 @@
       FROM_HERE, base::BindOnce(std::move(callback), true));
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/arc/fake_arc_sensor_service_client.h b/chromeos/ash/components/dbus/arc/fake_arc_sensor_service_client.h
similarity index 68%
rename from chromeos/dbus/arc/fake_arc_sensor_service_client.h
rename to chromeos/ash/components/dbus/arc/fake_arc_sensor_service_client.h
index 32b952c..464894f 100644
--- a/chromeos/dbus/arc/fake_arc_sensor_service_client.h
+++ b/chromeos/ash/components/dbus/arc/fake_arc_sensor_service_client.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_ARC_FAKE_ARC_SENSOR_SERVICE_CLIENT_H_
-#define CHROMEOS_DBUS_ARC_FAKE_ARC_SENSOR_SERVICE_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_SENSOR_SERVICE_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_SENSOR_SERVICE_CLIENT_H_
 
-#include "chromeos/dbus/arc/arc_sensor_service_client.h"
+#include "chromeos/ash/components/dbus/arc/arc_sensor_service_client.h"
 
-namespace chromeos {
+namespace ash {
 
 // Fake implementation of ArcSensorServiceClient.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_ARC) FakeArcSensorServiceClient
+class COMPONENT_EXPORT(ASH_DBUS_ARC) FakeArcSensorServiceClient
     : public ArcSensorServiceClient {
  public:
   // Returns the fake global instance if initialized. May return null.
@@ -31,6 +31,6 @@
       delete;
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_ARC_FAKE_ARC_SENSOR_SERVICE_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_ARC_FAKE_ARC_SENSOR_SERVICE_CLIENT_H_
diff --git a/chromeos/dbus/gnubby/BUILD.gn b/chromeos/ash/components/dbus/gnubby/BUILD.gn
similarity index 90%
rename from chromeos/dbus/gnubby/BUILD.gn
rename to chromeos/ash/components/dbus/gnubby/BUILD.gn
index 28be6eff..b9d9c74 100644
--- a/chromeos/dbus/gnubby/BUILD.gn
+++ b/chromeos/ash/components/dbus/gnubby/BUILD.gn
@@ -7,8 +7,8 @@
 assert(is_chromeos_ash, "Non-Chrome-OS builds must not depend on //chromeos")
 
 component("gnubby") {
-  output_name = "chromeos_gnubby"
-  defines = [ "IS_CHROMEOS_DBUS_GNUBBY_IMPL" ]
+  output_name = "ash_gnubby"
+  defines = [ "IS_ASH_DBUS_GNUBBY_IMPL" ]
   deps = [
     "//base",
     "//chromeos/dbus/common",
diff --git a/chromeos/dbus/gnubby/fake_gnubby_client.cc b/chromeos/ash/components/dbus/gnubby/fake_gnubby_client.cc
similarity index 86%
rename from chromeos/dbus/gnubby/fake_gnubby_client.cc
rename to chromeos/ash/components/dbus/gnubby/fake_gnubby_client.cc
index d59733b..bbd4c02b 100644
--- a/chromeos/dbus/gnubby/fake_gnubby_client.cc
+++ b/chromeos/ash/components/dbus/gnubby/fake_gnubby_client.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/gnubby/fake_gnubby_client.h"
+#include "chromeos/ash/components/dbus/gnubby/fake_gnubby_client.h"
 
-namespace chromeos {
+namespace ash {
 
 FakeGnubbyClient::FakeGnubbyClient() {}
 
@@ -26,4 +26,4 @@
     observer.PromptUserAuth();
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/gnubby/fake_gnubby_client.h b/chromeos/ash/components/dbus/gnubby/fake_gnubby_client.h
similarity index 69%
rename from chromeos/dbus/gnubby/fake_gnubby_client.h
rename to chromeos/ash/components/dbus/gnubby/fake_gnubby_client.h
index aa27465..a9d3c541 100644
--- a/chromeos/dbus/gnubby/fake_gnubby_client.h
+++ b/chromeos/ash/components/dbus/gnubby/fake_gnubby_client.h
@@ -2,18 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_GNUBBY_FAKE_GNUBBY_CLIENT_H_
-#define CHROMEOS_DBUS_GNUBBY_FAKE_GNUBBY_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_GNUBBY_FAKE_GNUBBY_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_GNUBBY_FAKE_GNUBBY_CLIENT_H_
 
 #include "base/component_export.h"
 #include "base/observer_list.h"
-#include "chromeos/dbus/gnubby/gnubby_client.h"
+#include "chromeos/ash/components/dbus/gnubby/gnubby_client.h"
 
-namespace chromeos {
+namespace ash {
 
 // A fake implementation of GnubbyClient used for tests.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_GNUBBY) FakeGnubbyClient
-    : public GnubbyClient {
+class COMPONENT_EXPORT(ASH_DBUS_GNUBBY) FakeGnubbyClient : public GnubbyClient {
  public:
   FakeGnubbyClient();
 
@@ -37,6 +36,6 @@
   base::WeakPtrFactory<FakeGnubbyClient> weak_ptr_factory_{this};
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_GNUBBY_FAKE_GNUBBY_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_GNUBBY_FAKE_GNUBBY_CLIENT_H_
diff --git a/chromeos/dbus/gnubby/fake_gnubby_client_unittest.cc b/chromeos/ash/components/dbus/gnubby/fake_gnubby_client_unittest.cc
similarity index 89%
rename from chromeos/dbus/gnubby/fake_gnubby_client_unittest.cc
rename to chromeos/ash/components/dbus/gnubby/fake_gnubby_client_unittest.cc
index 4382cf5..6b935d0 100644
--- a/chromeos/dbus/gnubby/fake_gnubby_client_unittest.cc
+++ b/chromeos/ash/components/dbus/gnubby/fake_gnubby_client_unittest.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/gnubby/fake_gnubby_client.h"
+#include "chromeos/ash/components/dbus/gnubby/fake_gnubby_client.h"
 
 #include "base/scoped_observation.h"
 #include "base/test/task_environment.h"
+#include "chromeos/ash/components/dbus/gnubby/gnubby_client.h"
 #include "chromeos/dbus/attestation/attestation.pb.h"
-#include "chromeos/dbus/gnubby/gnubby_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -56,4 +56,4 @@
   EXPECT_EQ(observer.calls(), 1);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/gnubby/gnubby_client.cc b/chromeos/ash/components/dbus/gnubby/gnubby_client.cc
similarity index 93%
rename from chromeos/dbus/gnubby/gnubby_client.cc
rename to chromeos/ash/components/dbus/gnubby/gnubby_client.cc
index 3b36cd0..2de2fb4a 100644
--- a/chromeos/dbus/gnubby/gnubby_client.cc
+++ b/chromeos/ash/components/dbus/gnubby/gnubby_client.cc
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/gnubby/gnubby_client.h"
+#include "chromeos/ash/components/dbus/gnubby/gnubby_client.h"
 
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
-#include "chromeos/dbus/gnubby/fake_gnubby_client.h"
+#include "chromeos/ash/components/dbus/gnubby/fake_gnubby_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_path.h"
 #include "dbus/object_proxy.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -103,4 +103,4 @@
   g_instance = nullptr;
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chromeos/dbus/gnubby/gnubby_client.h b/chromeos/ash/components/dbus/gnubby/gnubby_client.h
similarity index 82%
rename from chromeos/dbus/gnubby/gnubby_client.h
rename to chromeos/ash/components/dbus/gnubby/gnubby_client.h
index a2ae976..d8c52f2e 100644
--- a/chromeos/dbus/gnubby/gnubby_client.h
+++ b/chromeos/ash/components/dbus/gnubby/gnubby_client.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 CHROMEOS_DBUS_GNUBBY_GNUBBY_CLIENT_H_
-#define CHROMEOS_DBUS_GNUBBY_GNUBBY_CLIENT_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_GNUBBY_GNUBBY_CLIENT_H_
+#define CHROMEOS_ASH_COMPONENTS_DBUS_GNUBBY_GNUBBY_CLIENT_H_
 
 #include "base/component_export.h"
 #include "chromeos/dbus/common/dbus_client.h"
@@ -11,9 +11,9 @@
 #include "dbus/message.h"
 #include "third_party/cros_system_api/dbus/u2f/dbus-constants.h"
 
-namespace chromeos {
+namespace ash {
 // GnubbyClient is used to communicate with the Gnubby service.
-class COMPONENT_EXPORT(CHROMEOS_DBUS_GNUBBY) GnubbyClient : public DBusClient {
+class COMPONENT_EXPORT(ASH_DBUS_GNUBBY) GnubbyClient : public DBusClient {
  public:
   // Interface for observing changes in Gnubby Client
   class Observer {
@@ -51,6 +51,6 @@
   GnubbyClient();
   ~GnubbyClient() override;
 };
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROMEOS_DBUS_GNUBBY_GNUBBY_CLIENT_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_DBUS_GNUBBY_GNUBBY_CLIENT_H_
diff --git a/chromeos/components/quick_answers/understanding/intent_generator.cc b/chromeos/components/quick_answers/understanding/intent_generator.cc
index df50080..d5acb8d 100644
--- a/chromeos/components/quick_answers/understanding/intent_generator.cc
+++ b/chromeos/components/quick_answers/understanding/intent_generator.cc
@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h"
 #include "chromeos/components/quick_answers/quick_answers_model.h"
+#include "chromeos/components/quick_answers/utils/quick_answers_metrics.h"
 #include "chromeos/components/quick_answers/utils/quick_answers_utils.h"
 #include "chromeos/components/quick_answers/utils/spell_checker.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -210,6 +211,9 @@
     std::move(complete_callback_)
         .Run(IntentInfo(request.selected_text, IntentType::kDictionary,
                         QuickAnswersState::Get()->application_locale()));
+
+    // Record intent source type for dictionary intent.
+    RecordDictionaryIntentSource(DictionaryIntentSource::kHunspell);
     return;
   }
 
@@ -276,6 +280,10 @@
               entity_str,
               RewriteIntent(request.selected_text, entity_str, it->second),
               QuickAnswersState::Get()->application_locale()));
+
+      // Record intent source type for dictionary intent.
+      if (it->second == IntentType::kDictionary)
+        RecordDictionaryIntentSource(DictionaryIntentSource::kTextClassifier);
       return;
     }
   }
diff --git a/chromeos/components/quick_answers/utils/quick_answers_metrics.cc b/chromeos/components/quick_answers/utils/quick_answers_metrics.cc
index 90eec66..5233b76 100644
--- a/chromeos/components/quick_answers/utils/quick_answers_metrics.cc
+++ b/chromeos/components/quick_answers/utils/quick_answers_metrics.cc
@@ -23,6 +23,8 @@
 const char kQuickAnswersRequestTextLength[] = "QuickAnswers.RequestTextLength";
 const char kQuickAnswersTtsEngineEvent[] =
     "QuickAnswers.TextToSpeech.EngineEvent";
+const char kQuickAnswersDictionaryIntentSource[] =
+    "QuickAnswers.DictionaryIntent.Source";
 
 const char kDurationSuffix[] = ".Duration";
 const char kDefinitionSuffix[] = ".Definition";
@@ -127,4 +129,8 @@
   base::UmaHistogramEnumeration(kQuickAnswersTtsEngineEvent, event);
 }
 
+void RecordDictionaryIntentSource(DictionaryIntentSource source) {
+  base::UmaHistogramEnumeration(kQuickAnswersDictionaryIntentSource, source);
+}
+
 }  // namespace quick_answers
diff --git a/chromeos/components/quick_answers/utils/quick_answers_metrics.h b/chromeos/components/quick_answers/utils/quick_answers_metrics.h
index 9f6239b..50c89b8 100644
--- a/chromeos/components/quick_answers/utils/quick_answers_metrics.h
+++ b/chromeos/components/quick_answers/utils/quick_answers_metrics.h
@@ -29,6 +29,19 @@
   kMaxValue = TTS_EVENT_OTHER
 };
 
+// Enumeration of dictionary intent source type.
+//
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// Also remember to update the QuickAnswersDictionaryIntentSource enum
+// listing in tools/metrics/histograms/enums.xml.
+enum class DictionaryIntentSource {
+  kTextClassifier = 0,
+  kHunspell = 1,
+
+  kMaxValue = kHunspell,
+};
+
 // Record the status of loading quick answers with status and duration.
 void RecordLoadingStatus(LoadStatus status, const base::TimeDelta duration);
 
@@ -59,6 +72,9 @@
 // Record the TTS engine event types as they occur in quick answers.
 void RecordTtsEngineEvent(TtsEngineEvent event);
 
+// Record the source type of dictionary intent.
+void RecordDictionaryIntentSource(DictionaryIntentSource source);
+
 }  // namespace quick_answers
 
 #endif  // CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_QUICK_ANSWERS_METRICS_H_
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn
index cafb63ad..c35776d 100644
--- a/chromeos/dbus/BUILD.gn
+++ b/chromeos/dbus/BUILD.gn
@@ -11,7 +11,6 @@
   output_name = "chromeos_dbus"  # Avoid conflict with //dbus
   defines = [ "IS_CHROMEOS_DBUS_IMPL" ]
   public_deps = [
-    "//chromeos/dbus/arc",
     "//chromeos/dbus/common",
     "//chromeos/dbus/constants",
     "//chromeos/dbus/debug_daemon",
@@ -32,7 +31,6 @@
     "//chromeos/dbus/cros_disks",
     "//chromeos/dbus/easy_unlock",
     "//chromeos/dbus/fwupd",
-    "//chromeos/dbus/gnubby",
     "//chromeos/dbus/util",
     "//components/account_id",
     "//components/device_event_log",
@@ -79,7 +77,6 @@
     "//chromeos/dbus/dlcservice:unit_tests",
     "//chromeos/dbus/easy_unlock:unit_tests",
     "//chromeos/dbus/fwupd:test_support",
-    "//chromeos/dbus/gnubby:unit_tests",
     "//chromeos/dbus/power:power_manager_proto",
     "//chromeos/dbus/power:test_support",
     "//chromeos/dbus/shill:test_support",
diff --git a/chromeos/dbus/cros_disks/cros_disks_client.cc b/chromeos/dbus/cros_disks/cros_disks_client.cc
index 5147a2a..c5d1f67 100644
--- a/chromeos/dbus/cros_disks/cros_disks_client.cc
+++ b/chromeos/dbus/cros_disks/cros_disks_client.cc
@@ -46,6 +46,8 @@
 constexpr char kRemountOption[] = "remount";
 constexpr char kMountLabelOption[] = "mountlabel";
 
+CrosDisksClient* g_instance = nullptr;
+
 // Checks if retrieved media type is in boundaries of DeviceMediaType.
 bool IsValidMediaType(uint32_t type) {
   return type < static_cast<uint32_t>(cros_disks::DEVICE_MEDIA_NUM_VALUES);
@@ -301,7 +303,6 @@
                        std::move(callback), std::move(error_callback)));
   }
 
- protected:
   void Init(dbus::Bus* bus) override {
     proxy_ = bus->GetObjectProxy(
         cros_disks::kCrosDisksServiceName,
@@ -814,18 +815,42 @@
 ////////////////////////////////////////////////////////////////////////////////
 // CrosDisksClient
 
-CrosDisksClient::CrosDisksClient() = default;
-
-CrosDisksClient::~CrosDisksClient() = default;
+// static
+CrosDisksClient* CrosDisksClient::Get() {
+  return g_instance;
+}
 
 // static
-std::unique_ptr<CrosDisksClient> CrosDisksClient::Create() {
+void CrosDisksClient::Initialize(dbus::Bus* bus) {
+  // See ArcDataSnapshotdManager for code that sets this flag.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kCrosDisksFake)) {
-    return std::make_unique<FakeCrosDisksClient>();
-  } else {
-    return std::make_unique<CrosDisksClientImpl>();
+    InitializeFake();
+    return;
   }
+  CHECK(bus);
+  (new CrosDisksClientImpl())->Init(bus);
+}
+
+// static
+void CrosDisksClient::InitializeFake() {
+  (new FakeCrosDisksClient())->Init(nullptr);
+}
+
+// static
+void CrosDisksClient::Shutdown() {
+  CHECK(g_instance);
+  delete g_instance;
+}
+
+CrosDisksClient::CrosDisksClient() {
+  CHECK(!g_instance);
+  g_instance = this;
+}
+
+CrosDisksClient::~CrosDisksClient() {
+  CHECK_EQ(g_instance, this);
+  g_instance = nullptr;
 }
 
 // static
diff --git a/chromeos/dbus/cros_disks/cros_disks_client.h b/chromeos/dbus/cros_disks/cros_disks_client.h
index 9da68ef..0238727 100644
--- a/chromeos/dbus/cros_disks/cros_disks_client.h
+++ b/chromeos/dbus/cros_disks/cros_disks_client.h
@@ -341,11 +341,21 @@
                                    const std::string& device_path) = 0;
   };
 
+  // Returns the global instance if initialized. May return null.
+  static CrosDisksClient* Get();
+
+  // Creates and initializes the global instance. |bus| must not be null.
+  static void Initialize(dbus::Bus* bus);
+
+  // Creates and initializes a fake global instance.
+  static void InitializeFake();
+
+  // Destroys the global instance if it has been initialized.
+  static void Shutdown();
+
   CrosDisksClient(const CrosDisksClient&) = delete;
   CrosDisksClient& operator=(const CrosDisksClient&) = delete;
 
-  ~CrosDisksClient() override;
-
   // Registers the given |observer| to listen D-Bus signals.
   virtual void AddObserver(Observer* observer) = 0;
 
@@ -408,10 +418,6 @@
                                    GetDevicePropertiesCallback callback,
                                    base::OnceClosure error_callback) = 0;
 
-  // Factory function, creates a new instance and returns ownership.
-  // For normal usage, access the singleton via DBusThreadManager::Get().
-  static std::unique_ptr<CrosDisksClient> Create();
-
   // Returns the path of the mount point for archive files.
   static base::FilePath GetArchiveMountPoint();
 
@@ -426,8 +432,9 @@
       RemountOption remount);
 
  protected:
-  // Create() should be used instead.
+  // Initialize() should be used instead.
   CrosDisksClient();
+  ~CrosDisksClient() override;
 };
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/dbus_clients_browser.cc b/chromeos/dbus/dbus_clients_browser.cc
index 1e201a2..d7272233 100644
--- a/chromeos/dbus/dbus_clients_browser.cc
+++ b/chromeos/dbus/dbus_clients_browser.cc
@@ -5,8 +5,6 @@
 #include "chromeos/dbus/dbus_clients_browser.h"
 
 #include "base/check.h"
-#include "chromeos/dbus/cros_disks/cros_disks_client.h"
-#include "chromeos/dbus/cros_disks/fake_cros_disks_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
 #include "chromeos/dbus/debug_daemon/fake_debug_daemon_client.h"
@@ -30,7 +28,6 @@
 #endif  // USE_REAL_DBUS_CLIENTS
 
 DBusClientsBrowser::DBusClientsBrowser(bool use_real_clients) {
-  cros_disks_client_ = CREATE_DBUS_CLIENT(CrosDisksClient, use_real_clients);
   debug_daemon_client_ =
       CREATE_DBUS_CLIENT(DebugDaemonClient, use_real_clients);
   easy_unlock_client_ = CREATE_DBUS_CLIENT(EasyUnlockClient, use_real_clients);
@@ -42,7 +39,6 @@
 void DBusClientsBrowser::Initialize(dbus::Bus* system_bus) {
   DCHECK(DBusThreadManager::IsInitialized());
 
-  cros_disks_client_->Init(system_bus);
   debug_daemon_client_->Init(system_bus);
   easy_unlock_client_->Init(system_bus);
   fwupd_client_->Init(system_bus);
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h
index d1e1b5e..232695d 100644
--- a/chromeos/dbus/dbus_clients_browser.h
+++ b/chromeos/dbus/dbus_clients_browser.h
@@ -15,7 +15,6 @@
 
 namespace chromeos {
 
-class CrosDisksClient;
 class DebugDaemonClient;
 class EasyUnlockClient;
 class FwupdClient;
@@ -41,7 +40,6 @@
   friend class DBusThreadManager;
   friend class DBusThreadManagerSetter;
 
-  std::unique_ptr<CrosDisksClient> cros_disks_client_;
   std::unique_ptr<DebugDaemonClient> debug_daemon_client_;
   std::unique_ptr<EasyUnlockClient> easy_unlock_client_;
   std::unique_ptr<FwupdClient> fwupd_client_;
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc
index d6a789b7..44a0fc6 100644
--- a/chromeos/dbus/dbus_thread_manager.cc
+++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -11,7 +11,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_pump_type.h"
 #include "chromeos/dbus/common/dbus_client.h"
-#include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/dbus/dbus_clients_browser.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
 #include "chromeos/dbus/easy_unlock/easy_unlock_client.h"
@@ -37,10 +36,6 @@
              ? g_setter->name.get()   \
              : (clients_browser_ ? clients_browser_->name.get() : nullptr)
 
-CrosDisksClient* DBusThreadManager::GetCrosDisksClient() {
-  RETURN_DBUS_CLIENT(cros_disks_client_);
-}
-
 DebugDaemonClient* DBusThreadManager::GetDebugDaemonClient() {
   RETURN_DBUS_CLIENT(debug_daemon_client_);
 }
@@ -117,11 +112,6 @@
 
 DBusThreadManagerSetter::~DBusThreadManagerSetter() = default;
 
-void DBusThreadManagerSetter::SetCrosDisksClient(
-    std::unique_ptr<CrosDisksClient> client) {
-  cros_disks_client_ = std::move(client);
-}
-
 void DBusThreadManagerSetter::SetDebugDaemonClient(
     std::unique_ptr<DebugDaemonClient> client) {
   debug_daemon_client_ = std::move(client);
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h
index 10a82663..a752e72 100644
--- a/chromeos/dbus/dbus_thread_manager.h
+++ b/chromeos/dbus/dbus_thread_manager.h
@@ -15,7 +15,6 @@
 namespace chromeos {
 
 // Style Note: Clients are sorted by names.
-class CrosDisksClient;
 class DBusClientsBrowser;
 class DBusThreadManagerSetter;
 class DebugDaemonClient;
@@ -56,7 +55,6 @@
   // pointers after DBusThreadManager has been shut down.
   // TODO(jamescook): Replace this with calls to FooClient::Get().
   // http://crbug.com/647367
-  CrosDisksClient* GetCrosDisksClient();
   DebugDaemonClient* GetDebugDaemonClient();
   EasyUnlockClient* GetEasyUnlockClient();
 
@@ -77,7 +75,6 @@
 // TODO(jamescook): Replace these with FooClient::InitializeForTesting().
 class COMPONENT_EXPORT(CHROMEOS_DBUS) DBusThreadManagerSetter {
  public:
-  void SetCrosDisksClient(std::unique_ptr<CrosDisksClient> client);
   void SetDebugDaemonClient(std::unique_ptr<DebugDaemonClient> client);
 
  private:
@@ -89,7 +86,6 @@
       delete;
   ~DBusThreadManagerSetter();
 
-  std::unique_ptr<CrosDisksClient> cros_disks_client_;
   std::unique_ptr<DebugDaemonClient> debug_daemon_client_;
 };
 
diff --git a/chromeos/dbus/dbus_thread_manager_unittest.cc b/chromeos/dbus/dbus_thread_manager_unittest.cc
index a83fed72..e5bcae48 100644
--- a/chromeos/dbus/dbus_thread_manager_unittest.cc
+++ b/chromeos/dbus/dbus_thread_manager_unittest.cc
@@ -20,7 +20,6 @@
   EXPECT_TRUE(manager->IsUsingFakes());
 
   // Clients were created.
-  EXPECT_TRUE(manager->GetCrosDisksClient());
   EXPECT_TRUE(manager->GetDebugDaemonClient());
   EXPECT_TRUE(manager->GetEasyUnlockClient());
 
diff --git a/components/crash/core/app/crashpad.cc b/components/crash/core/app/crashpad.cc
index b8f49d1..34abee3 100644
--- a/components/crash/core/app/crashpad.cc
+++ b/components/crash/core/app/crashpad.cc
@@ -49,24 +49,15 @@
 
 namespace {
 
-void SetLogFatalCrashKey(const char* file,
-                         int line,
-                         const char* message_without_prefix) {
-  static crashpad::StringAnnotation<512> crash_key("LOG_FATAL");
-  crash_key.Set(logging::LogMessage::BuildCrashString(file, line,
-                                                      message_without_prefix));
-}
-
 void AbslAbortHook(const char* file,
                    int line,
                    const char* buf_start,
                    const char* prefix_end,
                    const char* buf_end) {
-  SetLogFatalCrashKey(file, line, prefix_end);
-
-  // IMMEDIATE_CRASH() generates better stack dumps than the abort() that absl::
-  // would trigger if this returns.
-  IMMEDIATE_CRASH();
+  // This simulates that a CHECK(false) was done at file:line instead of here.
+  // This is used instead of IMMEDIATE_CRASH() to give better error messages
+  // locally (printed stack for one).
+  logging::CheckError::Check(file, line, "false").stream() << prefix_end;
 }
 
 base::FilePath* g_database_path;
@@ -94,7 +85,9 @@
   base::AutoReset<bool> guard(&guarded, true);
 
   CHECK_LE(message_start, string.size());
-  SetLogFatalCrashKey(file, line, string.c_str() + message_start);
+  static crashpad::StringAnnotation<512> crash_key("LOG_FATAL");
+  crash_key.Set(logging::LogMessage::BuildCrashString(
+      file, line, string.c_str() + message_start));
 
   // Rather than including the code to force the crash here, allow the caller to
   // do it.
diff --git a/components/external_intents/android/BUILD.gn b/components/external_intents/android/BUILD.gn
index 204cafa..2eee9282 100644
--- a/components/external_intents/android/BUILD.gn
+++ b/components/external_intents/android/BUILD.gn
@@ -7,6 +7,7 @@
 android_library("java") {
   sources = [
     "java/src/org/chromium/components/external_intents/AuthenticatorNavigationInterceptor.java",
+    "java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java",
     "java/src/org/chromium/components/external_intents/ExternalIntentsSwitches.java",
     "java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java",
     "java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java",
@@ -48,11 +49,18 @@
 }
 
 generate_jni("jni_headers") {
-  sources = [ "java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java" ]
+  sources = [
+    "java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java",
+    "java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java",
+  ]
 }
 
 static_library("android") {
-  sources = [ "intercept_navigation_delegate_impl.cc" ]
+  sources = [
+    "external_intents_features.cc",
+    "external_intents_features.h",
+    "intercept_navigation_delegate_impl.cc",
+  ]
 
   deps = [
     ":jni_headers",
diff --git a/components/external_intents/android/external_intents_features.cc b/components/external_intents/android/external_intents_features.cc
new file mode 100644
index 0000000..84c8f32
--- /dev/null
+++ b/components/external_intents/android/external_intents_features.cc
@@ -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.
+
+#include "components/external_intents/android/external_intents_features.h"
+
+#include <jni.h>
+#include <stddef.h>
+#include <string>
+
+#include "base/android/jni_string.h"
+#include "base/notreached.h"
+#include "components/external_intents/android/jni_headers/ExternalIntentsFeatures_jni.h"
+
+namespace external_intents {
+
+namespace {
+
+// Array of features exposed through the Java ExternalIntentsFeatures API.
+const base::Feature* kFeaturesExposedToJava[] = {
+    &kScaryExternalNavigationRefactoring,
+};
+
+}  // namespace
+
+// Alphabetical:
+const base::Feature kScaryExternalNavigationRefactoring{
+    "ScaryExternalNavigationRefactoring", base::FEATURE_ENABLED_BY_DEFAULT};
+
+static jlong JNI_ExternalIntentsFeatures_GetFeature(JNIEnv* env, jint ordinal) {
+  return reinterpret_cast<jlong>(kFeaturesExposedToJava[ordinal]);
+}
+
+}  // namespace external_intents
diff --git a/components/external_intents/android/external_intents_features.h b/components/external_intents/android/external_intents_features.h
new file mode 100644
index 0000000..93d30123
--- /dev/null
+++ b/components/external_intents/android/external_intents_features.h
@@ -0,0 +1,17 @@
+// 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_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURES_H_
+#define COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace external_intents {
+
+// Alphabetical:
+extern const base::Feature kScaryExternalNavigationRefactoring;
+
+}  // namespace external_intents
+
+#endif  // COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURES_H_
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java
new file mode 100644
index 0000000..7d01dd8
--- /dev/null
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java
@@ -0,0 +1,56 @@
+// 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.
+
+package org.chromium.components.external_intents;
+
+import android.os.Build;
+
+import org.chromium.base.Features;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.build.BuildConfig;
+
+/**
+ * Java accessor for base/feature_list.h state.
+ *
+ * This class provides methods to access values of feature flags registered in
+ * |kFeaturesExposedToJava| in components/external_intents/android/external_intents_features.cc.
+ *
+ */
+@JNINamespace("external_intents")
+public class ExternalIntentsFeatures extends Features {
+    public static final String SCARY_EXTERNAL_NAVIGATION_REFACTORING_NAME =
+            "ScaryExternalNavigationRefactoring";
+
+    public static final ExternalIntentsFeatures SCARY_EXTERNAL_NAVIGATION_REFACTORING =
+            new ExternalIntentsFeatures(0, SCARY_EXTERNAL_NAVIGATION_REFACTORING_NAME);
+
+    private final int mOrdinal;
+
+    private ExternalIntentsFeatures(int ordinal, String name) {
+        super(name);
+        mOrdinal = ordinal;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        // Test-only hack to make sure we get coverage of the feature both enabled and disabled
+        // across all test suites. CQ will run with the feature enabled on M, waterfall will run
+        // with the feature disabled on various API levels.
+        if (BuildConfig.IS_FOR_TEST && mOrdinal == SCARY_EXTERNAL_NAVIGATION_REFACTORING.mOrdinal) {
+            return Build.VERSION.SDK_INT <= Build.VERSION_CODES.N;
+        }
+        return super.isEnabled();
+    }
+
+    @Override
+    protected long getFeaturePointer() {
+        return ExternalIntentsFeaturesJni.get().getFeature(mOrdinal);
+    }
+
+    @NativeMethods
+    interface Natives {
+        long getFeature(int ordinal);
+    }
+}
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index 33ef01b0..81559e7 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -73,6 +73,7 @@
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.permissions.PermissionCallback;
 import org.chromium.url.GURL;
+import org.chromium.url.Origin;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -506,7 +507,8 @@
             params.getRedirectHandler().setShouldNotOverrideUrlLoadingOnCurrentRedirectChain();
         }
         if (DEBUG) Log.i(TAG, "clobberCurrentTab called");
-        return clobberCurrentTab(browserFallbackUrl, params.getReferrerUrl());
+        return clobberCurrentTab(browserFallbackUrl, params.getReferrerUrl(),
+                params.getInitiatorOrigin(), params.isRendererInitiated());
     }
 
     private void printDebugShouldOverrideUrlLoadingResultType(OverrideUrlLoadingResult result) {
@@ -651,7 +653,8 @@
                         params.getAsyncActionTakenInMainFrameCallback().onResult(
                                 new ExternalNavigationParams.AsyncActionTakenParams(false, true));
                     }
-                    clobberCurrentTab(params.getUrl(), params.getReferrerUrl());
+                    clobberCurrentTab(params.getUrl(), params.getReferrerUrl(),
+                            params.getInitiatorOrigin(), params.isRendererInitiated());
                 } else {
                     // TODO(tedchoc): Show an indication to the user that the navigation failed
                     //                instead of silently dropping it on the floor.
@@ -674,17 +677,26 @@
      *
      * @param url The new URL after clobbering the current tab.
      * @param referrerUrl The HTTP referrer URL.
+     * @param initiatorOrigin The Origin the navigation intiated from.
      * @return OverrideUrlLoadingResultType (if the tab has been clobbered, or we're launching an
      *         intent.)
      */
     @VisibleForTesting
-    protected OverrideUrlLoadingResult clobberCurrentTab(GURL url, GURL referrerUrl) {
+    protected OverrideUrlLoadingResult clobberCurrentTab(
+            GURL url, GURL referrerUrl, Origin initiatorOrigin, boolean isRendererInitiated) {
         int transitionType = PageTransition.LINK;
         final LoadUrlParams loadUrlParams = new LoadUrlParams(url, transitionType);
         if (!referrerUrl.isEmpty()) {
             Referrer referrer = new Referrer(referrerUrl.getSpec(), ReferrerPolicy.ALWAYS);
             loadUrlParams.setReferrer(referrer);
         }
+        if (RedirectHandler.isRefactoringEnabled()) {
+            // This URL came from the renderer, so it should be seen as part of the current redirect
+            // chain.
+            loadUrlParams.setIsRendererInitiated(isRendererInitiated);
+            loadUrlParams.setInitiatorOrigin(initiatorOrigin);
+        }
+
         assert mDelegate.hasValidTab() : "clobberCurrentTab was called with an empty tab.";
         // Loading URL will start a new navigation which cancels the current one
         // that this clobbering is being done for. It leads to UAF. To avoid that,
@@ -949,6 +961,19 @@
     }
 
     /**
+     * If a navigation chain has used the history API to go back/forward external navigation is
+     * probably not expected or desirable.
+     */
+    private boolean navigationChainUsedBackOrForward(ExternalNavigationParams params) {
+        if (params.getRedirectHandler() != null
+                && params.getRedirectHandler().navigationChainUsedBackOrForward()) {
+            if (DEBUG) Log.i(TAG, "Navigation chain used back or forward.");
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * If the intent can't be resolved, we should fall back to the browserFallbackUrl, or try to
      * find the app on the market if no fallback is provided.
      */
@@ -1464,6 +1489,12 @@
 
         if (isLinkFromChromeInternalPage(params)) return OverrideUrlLoadingResult.forNoOverride();
 
+        if (RedirectHandler.isRefactoringEnabled()) {
+            if (navigationChainUsedBackOrForward(params)) {
+                return OverrideUrlLoadingResult.forNoOverride();
+            }
+        }
+
         if (hasInternalScheme(params.getUrl(), targetIntent)
                 || hasContentScheme(params.getUrl(), targetIntent)
                 || hasFileSchemeInIntentURI(params.getUrl(), targetIntent)) {
@@ -1571,7 +1602,7 @@
 
         return startActivity(targetIntent, shouldProxyForInstantApps, requiresIntentChooser,
                 resolvingInfos, resolveActivity, browserFallbackUrl, intentDataUrl,
-                params.getReferrerUrl());
+                params.getReferrerUrl(), params.getInitiatorOrigin(), params.isRendererInitiated());
     }
 
     // https://crbug.com/1249964
@@ -1906,7 +1937,7 @@
      *              used by Instant Apps intents).
      */
     private void startActivity(Intent intent, boolean proxy) {
-        startActivity(intent, proxy, false, null, null, null, null, null);
+        startActivity(intent, proxy, false, null, null, null, null, null, null, false);
     }
 
     /**
@@ -1925,12 +1956,14 @@
      * @param browserFallbackUrl The fallback URL if the user chooses not to leave this app.
      * @param intentDataUrl The URL |intent| is targeting.
      * @param referrerUrl The referrer for the navigation.
+     * @param initiatorOrigin The Origin that initiated the navigation, if any.
+     * @param isRendererInitiated True if the navigation was renderer initiated.
      * @returns The OverrideUrlLoadingResult for starting (or not starting) the Activity.
      */
     protected OverrideUrlLoadingResult startActivity(Intent intent, boolean proxy,
             boolean requiresIntentChooser, QueryIntentActivitiesSupplier resolvingInfos,
             ResolveActivitySupplier resolveActivity, GURL browserFallbackUrl, GURL intentDataUrl,
-            GURL referrerUrl) {
+            GURL referrerUrl, Origin initiatorOrigin, boolean isRendererInitiated) {
         // Only touches disk on Kitkat. See http://crbug.com/617725 for more context.
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
         try {
@@ -1947,7 +1980,8 @@
                 }
                 if (requiresIntentChooser) {
                     return startActivityWithChooser(intent, resolvingInfos, resolveActivity,
-                            browserFallbackUrl, intentDataUrl, referrerUrl, context);
+                            browserFallbackUrl, intentDataUrl, referrerUrl, context,
+                            initiatorOrigin, isRendererInitiated);
                 }
                 return doStartActivity(intent, context);
             }
@@ -1981,7 +2015,8 @@
     @SuppressWarnings("UseCompatLoadingForDrawables")
     private OverrideUrlLoadingResult startActivityWithChooser(final Intent intent,
             QueryIntentActivitiesSupplier resolvingInfos, ResolveActivitySupplier resolveActivity,
-            GURL browserFallbackUrl, GURL intentDataUrl, GURL referrerUrl, Context context) {
+            GURL browserFallbackUrl, GURL intentDataUrl, GURL referrerUrl, Context context,
+            Origin initiatorOrigin, boolean isRendererInitiated) {
         ResolveInfo intentResolveInfo = resolveActivity.get();
         // If this is null, then the intent was only previously matching
         // non-default filters, so just drop it.
@@ -2049,9 +2084,11 @@
                             // matches what would have happened had the regular chooser dialog shown
                             // up and the user selected this app.
                             if (UrlUtilities.isAcceptedScheme(intentDataUrl)) {
-                                clobberCurrentTab(intentDataUrl, referrerUrl);
+                                clobberCurrentTab(intentDataUrl, referrerUrl, initiatorOrigin,
+                                        isRendererInitiated);
                             } else if (!browserFallbackUrl.isEmpty()) {
-                                clobberCurrentTab(browserFallbackUrl, referrerUrl);
+                                clobberCurrentTab(browserFallbackUrl, referrerUrl, initiatorOrigin,
+                                        isRendererInitiated);
                             }
                             return;
                         }
@@ -2307,7 +2344,7 @@
     private static boolean isIncomingIntentRedirect(ExternalNavigationParams params) {
         boolean isOnEffectiveIntentRedirect = params.getRedirectHandler() == null
                 ? false
-                : params.getRedirectHandler().isOnEffectiveIntentRedirectChain();
+                : params.getRedirectHandler().isOnNoninitialLoadForIntentNavigationChain();
         return (params.isFromIntent() && params.isRedirect()) || isOnEffectiveIntentRedirect;
     }
 
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
index 536d0a0..1a1247bf 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
@@ -137,7 +137,8 @@
 
         redirectHandler.updateNewUrlLoading(navigationHandle.pageTransition(),
                 navigationHandle.isRedirect(), navigationHandle.hasUserGesture(),
-                lastUserInteractionTime, getLastCommittedEntryIndex(), isInitialNavigation());
+                lastUserInteractionTime, getLastCommittedEntryIndex(), isInitialNavigation(),
+                navigationHandle.isRendererInitiated());
 
         ExternalNavigationParams params =
                 buildExternalNavigationParams(navigationHandle, redirectHandler, escapedUrl)
@@ -183,7 +184,7 @@
         @PageTransition
         int transition = PageTransition.LINK;
         mClient.getOrCreateRedirectHandler().updateNewUrlLoading(transition, false, true,
-                mClient.getLastUserInteractionTime(), getLastCommittedEntryIndex(), false);
+                mClient.getLastUserInteractionTime(), getLastCommittedEntryIndex(), false, true);
     }
 
     /**
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/RedirectHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/RedirectHandler.java
index 80b526af..1440998 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/RedirectHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/RedirectHandler.java
@@ -70,11 +70,12 @@
     private class NavigationState {
         final int mInitialNavigationType;
         final boolean mHasUserStartedNonInitialNavigation;
-        boolean mIsOnEffectiveRedirectChain;
-        boolean mShouldNotOverrideUrlLoadingOnCurrentRedirectChain;
-        boolean mShouldNotBlockOverrideUrlLoadingOnCurrentRedirectionChain;
+        boolean mIsOnFirstLoadInChain = true;
+        boolean mShouldNotOverrideUrlLoadingOnCurrentNavigationChain;
+        boolean mShouldNotBlockOverrideUrlLoadingOnCurrentNavigationChain;
         // TODO(https://crbug.com/1286053): Plumb through the user activation time from blink.
         final long mNavigationChainStartTime = currentRealtime();
+        boolean mUsedBackOrForward;
 
         NavigationState(int initialNavigationType, boolean hasUserStartedNonInitialNavigation) {
             mInitialNavigationType = initialNavigationType;
@@ -99,6 +100,14 @@
     protected RedirectHandler() {}
 
     /**
+     * Temporarily gate ExternalNavigationHandler/RedirectHandler behind a finch kill switch in case
+     * things unexpectedly break.
+     */
+    public static boolean isRefactoringEnabled() {
+        return ExternalIntentsFeatures.SCARY_EXTERNAL_NAVIGATION_REFACTORING.isEnabled();
+    }
+
+    /**
      * Resets |mIntentState| for the newly received Intent.
      */
     public void updateIntent(Intent intent, boolean isCustomTabIntent, boolean sendToExternalApps,
@@ -142,7 +151,7 @@
      * occurs.
      */
     public void setShouldNotOverrideUrlLoadingOnCurrentRedirectChain() {
-        mNavigationState.mShouldNotOverrideUrlLoadingOnCurrentRedirectChain = true;
+        mNavigationState.mShouldNotOverrideUrlLoadingOnCurrentNavigationChain = true;
     }
 
     /**
@@ -150,7 +159,7 @@
      * a new user-initiated navigation occurs.
      */
     public void setShouldNotBlockUrlLoadingOverrideOnCurrentRedirectionChain() {
-        mNavigationState.mShouldNotBlockOverrideUrlLoadingOnCurrentRedirectionChain = true;
+        mNavigationState.mShouldNotBlockOverrideUrlLoadingOnCurrentNavigationChain = true;
     }
 
     /**
@@ -179,14 +188,37 @@
      * @param hasUserGesture whether this loading is started by a user gesture.
      * @param lastUserInteractionTime time when the last user interaction was made.
      * @param lastCommittedEntryIndex the last committed entry index right before this loading.
-     * @param isInitialNavigation whether this loading is for the initial navigation.
+     * @param isInitialNavigation whether this loading is for the initial navigation in a Tab.
+     * @param isRendererInitiated whether the navigation was initiated by a Renderer.
      */
     public void updateNewUrlLoading(int pageTransType, boolean isRedirect, boolean hasUserGesture,
-            long lastUserInteractionTime, int lastCommittedEntryIndex,
-            boolean isInitialNavigation) {
+            long lastUserInteractionTime, int lastCommittedEntryIndex, boolean isInitialNavigation,
+            boolean isRendererInitiated) {
+        mLastUserInteractionTimeMillis = lastUserInteractionTime;
+
+        if (isRefactoringEnabled()) {
+            // Treat anything renderer-initiated without a gesture as part of the same navigation
+            // chain. Server redirects are also part of the same navigation chain.
+            boolean isSameNavigationChain = isRedirect || (isRendererInitiated && !hasUserGesture);
+
+            if (mNavigationState != null && isSameNavigationChain) {
+                updateNavigationState(pageTransType);
+            } else {
+                resetNavigationState(pageTransType, hasUserGesture, lastCommittedEntryIndex,
+                        isInitialNavigation);
+            }
+        } else {
+            updateNewUrlLoadingOldVersion(pageTransType, isRedirect, hasUserGesture,
+                    lastUserInteractionTime, lastCommittedEntryIndex, isInitialNavigation,
+                    isRendererInitiated);
+        }
+    }
+
+    private void updateNewUrlLoadingOldVersion(int pageTransType, boolean isRedirect,
+            boolean hasUserGesture, long lastUserInteractionTime, int lastCommittedEntryIndex,
+            boolean isInitialNavigation, boolean isRendererInitiated) {
         long prevNewUrlLoadingTime = mLastNewUrlLoadingTime;
         mLastNewUrlLoadingTime = SystemClock.elapsedRealtime();
-        mLastUserInteractionTimeMillis = lastUserInteractionTime;
 
         int pageTransitionCore = pageTransType & PageTransition.CORE_MASK;
 
@@ -204,8 +236,7 @@
             }
         }
         if (!isNewLoadingStartedByUser) {
-            // Redirect chain starts from the second url loading.
-            mNavigationState.mIsOnEffectiveRedirectChain = true;
+            mNavigationState.mIsOnFirstLoadInChain = false;
             return;
         }
 
@@ -230,12 +261,45 @@
         mLastCommittedEntryIndexBeforeStartingNavigation = lastCommittedEntryIndex;
     }
 
+    private void updateNavigationState(int pageTransType) {
+        mNavigationState.mIsOnFirstLoadInChain = false;
+
+        boolean isBackOrForward = (pageTransType & PageTransition.FORWARD_BACK) != 0;
+        if (isBackOrForward) mNavigationState.mUsedBackOrForward = true;
+    }
+
+    private void resetNavigationState(int pageTransType, boolean hasUserGesture,
+            int lastCommittedEntryIndex, boolean isInitialNavigation) {
+        // Create the NavigationState for a new Navigation chain.
+        int pageTransitionCore = pageTransType & PageTransition.CORE_MASK;
+        boolean isFromIntent = (pageTransType & PageTransition.FROM_API) != 0;
+        boolean isBackOrForward = (pageTransType & PageTransition.FORWARD_BACK) != 0;
+        int initialNavigationType;
+        if (isFromIntent && mIntentState != null) {
+            initialNavigationType = NAVIGATION_TYPE_FROM_INTENT;
+        } else {
+            mIntentState = null;
+            if (pageTransitionCore == PageTransition.TYPED) {
+                initialNavigationType = NAVIGATION_TYPE_FROM_USER_TYPING;
+            } else if (pageTransitionCore == PageTransition.RELOAD || isBackOrForward) {
+                initialNavigationType = NAVIGATION_TYPE_FROM_RELOAD;
+            } else if (pageTransitionCore == PageTransition.LINK && !hasUserGesture) {
+                initialNavigationType = NAVIGATION_TYPE_FROM_LINK_WITHOUT_USER_GESTURE;
+            } else {
+                initialNavigationType = NAVIGATION_TYPE_OTHER;
+            }
+        }
+        mNavigationState = new NavigationState(initialNavigationType, !isInitialNavigation);
+        mLastCommittedEntryIndexBeforeStartingNavigation = lastCommittedEntryIndex;
+    }
+
     /**
-     * @return whether on effective intent redirect chain or not.
+     * @return whether this is a navigation chain initiated by an intent that is on a noninitial
+     *         navigation (eg. has followed a client or server redirect).
      */
-    public boolean isOnEffectiveIntentRedirectChain() {
+    public boolean isOnNoninitialLoadForIntentNavigationChain() {
         return mNavigationState.mInitialNavigationType == NAVIGATION_TYPE_FROM_INTENT
-                && mNavigationState.mIsOnEffectiveRedirectChain;
+                && !mNavigationState.mIsOnFirstLoadInChain;
     }
 
     /**
@@ -297,7 +361,7 @@
      * @return whether we should stay in Chrome or not.
      */
     public boolean shouldNotOverrideUrlLoading() {
-        return mNavigationState.mShouldNotOverrideUrlLoadingOnCurrentRedirectChain;
+        return mNavigationState.mShouldNotOverrideUrlLoadingOnCurrentNavigationChain;
     }
 
     /**
@@ -305,8 +369,8 @@
      * chain.
      */
     public boolean getAndClearShouldNotBlockOverrideUrlLoadingOnCurrentRedirectionChain() {
-        boolean value = mNavigationState.mShouldNotBlockOverrideUrlLoadingOnCurrentRedirectionChain;
-        mNavigationState.mShouldNotBlockOverrideUrlLoadingOnCurrentRedirectionChain = false;
+        boolean value = mNavigationState.mShouldNotBlockOverrideUrlLoadingOnCurrentNavigationChain;
+        mNavigationState.mShouldNotBlockOverrideUrlLoadingOnCurrentNavigationChain = false;
         return value;
     }
 
@@ -356,7 +420,7 @@
     }
 
     /**
-     * @return The initial intent of a redirect chain, if available.
+     * @return The initial intent of the navigation chain, if available.
      */
     public Intent getInitialIntent() {
         return mIntentState != null ? mIntentState.mInitialIntent : null;
@@ -372,6 +436,10 @@
                 > NAVIGATION_CHAIN_TIMEOUT_MILLIS;
     }
 
+    public boolean navigationChainUsedBackOrForward() {
+        return mNavigationState.mUsedBackOrForward;
+    }
+
     public void maybeLogExternalRedirectBlockedWithMissingGesture() {
         if (mNavigationState.mInitialNavigationType
                 == NAVIGATION_TYPE_FROM_LINK_WITHOUT_USER_GESTURE) {
diff --git a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
index cafdf33..17c88aa9 100644
--- a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
+++ b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
@@ -221,7 +221,8 @@
         mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME));
 
         RedirectHandler handler = RedirectHandler.create();
-        handler.updateNewUrlLoading(PageTransition.CLIENT_REDIRECT, false, false, 0, 0, false);
+        handler.updateNewUrlLoading(
+                PageTransition.CLIENT_REDIRECT, false, false, 0, 0, false, true);
 
         checkUrl(YOUTUBE_URL)
                 .withRedirectHandler(handler)
@@ -351,8 +352,9 @@
                 Intent.parseUri("http://example.test", Intent.URI_INTENT_SCHEME),
                 !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
         redirectHandler.updateNewUrlLoading(
-                PageTransition.LINK | PageTransition.FROM_API, false, false, 0, 0, true);
-        redirectHandler.updateNewUrlLoading(PageTransition.FORM_SUBMIT, false, false, 0, 0, false);
+                PageTransition.LINK | PageTransition.FROM_API, false, false, 0, 0, true, false);
+        redirectHandler.updateNewUrlLoading(
+                PageTransition.FORM_SUBMIT, false, false, 0, 0, false, true);
 
         // If the redirect is not associated with a user gesture but came from an incoming intent,
         // then allow those to launch external intents.
@@ -637,8 +639,10 @@
         // have any new resolver.
         redirectHandler.updateIntent(
                 ytIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withPageTransition(transTypeLinkFromIntent)
                 .withIsRendererInitiated(false)
@@ -649,8 +653,10 @@
         // Do not ignore if a new intent has any new resolver.
         redirectHandler.updateIntent(
                 fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withPageTransition(transTypeLinkFromIntent)
                 .withIsRendererInitiated(false)
@@ -662,8 +668,10 @@
         // Do not ignore if a new intent cannot be handled by Chrome.
         redirectHandler.updateIntent(
                 fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
         checkUrl("intent://myownurl")
                 .withPageTransition(transTypeLinkFromIntent)
                 .withIsRendererInitiated(false)
@@ -686,8 +694,10 @@
         // Ignore if an initial Intent was heading to Chrome.
         redirectHandler.updateIntent(
                 fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withPageTransition(transTypeLinkFromIntent)
                 .withIsRendererInitiated(false)
@@ -698,8 +708,10 @@
         // Do not ignore if the URI has an external protocol.
         redirectHandler.updateIntent(
                 fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
         checkUrl("market://1234")
                 .withPageTransition(transTypeLinkFromIntent)
                 .withIsRendererInitiated(false)
@@ -722,7 +734,8 @@
         barIntent.setPackage(mContext.getPackageName());
         redirectHandler.updateIntent(
                 barIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
         checkUrl(YOUTUBE_URL)
                 .withPageTransition(transTypeLinkFromIntent)
                 .withIsRendererInitiated(false)
@@ -734,8 +747,10 @@
         fooIntent.setPackage(mContext.getPackageName());
         redirectHandler.updateIntent(
                 fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
         checkUrl(YOUTUBE_URL)
                 .withPageTransition(transTypeLinkFromIntent)
                 .withIsRendererInitiated(false)
@@ -749,8 +764,10 @@
         extraIntent2.setPackage(mContext.getPackageName());
         redirectHandler.updateIntent(
                 extraIntent2, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
         checkUrl(YOUTUBE_URL)
                 .withPageTransition(transTypeLinkFromIntent)
                 .withIsRendererInitiated(false)
@@ -763,8 +780,10 @@
         extraIntent3.setPackage(mContext.getPackageName());
         redirectHandler.updateIntent(
                 extraIntent3, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
         checkUrl(YOUTUBE_URL)
                 .withPageTransition(transTypeLinkFromIntent)
                 .withIsRendererInitiated(false)
@@ -776,10 +795,11 @@
         // External intent for a user-initiated navigation should always be allowed.
         redirectHandler.updateIntent(
                 fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        // Simulate a real user navigation.
         redirectHandler.updateNewUrlLoading(
-                PageTransition.LINK, false, true, SystemClock.elapsedRealtime() + 1, 0, false);
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        // Simulate a real user navigation.
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true,
+                SystemClock.elapsedRealtime() + 1, 0, false, true);
         checkUrl(YOUTUBE_URL)
                 .withPageTransition(PageTransition.LINK)
                 .withRedirectHandler(redirectHandler)
@@ -799,8 +819,10 @@
         Intent intent = Intent.parseUri(IMDB_APP_INTENT_FOR_TOM_HANKS, Intent.URI_INTENT_SCHEME);
         redirectHandler.updateIntent(
                 intent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeTopLevelFromIntent, false, false, 0, 0, true);
-        redirectHandler.updateNewUrlLoading(transTypeTopLevelFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeTopLevelFromIntent, false, false, 0, 0, true, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeTopLevelFromIntent, true, false, 0, 0, false, false);
         checkUrl(IMDB_APP_INTENT_FOR_TOM_HANKS)
                 .withPageTransition(transTypeTopLevelFromIntent)
                 .withIsRendererInitiated(false)
@@ -823,8 +845,10 @@
         fooIntent.setPackage(mContext.getPackageName());
         redirectHandler.updateIntent(
                 fooIntent, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
 
         mDelegate.setCanHandleWithInstantApp(true);
         checkUrl("http://instantappenabled.com")
@@ -885,8 +909,10 @@
                 Intent.parseUri("http://instantappenabled.com", Intent.URI_INTENT_SCHEME);
         redirectHandler.updateIntent(
                 fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, !INTENT_STARTED_TASK);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0, false);
-        redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, false, false, 0, 0, false, false);
+        redirectHandler.updateNewUrlLoading(
+                transTypeLinkFromIntent, true, false, 0, 0, false, false);
 
         mDelegate.setCanHandleWithInstantApp(true);
         checkUrl("http://goo.gl/1234")
@@ -1165,7 +1191,8 @@
             mUrlHandler.mCanShowIncognitoDialog = true;
             ThreadUtils.runOnUiThreadBlocking(() -> {
                 RedirectHandler redirectHandler = RedirectHandler.create();
-                redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
+                redirectHandler.updateNewUrlLoading(
+                        PageTransition.LINK, false, true, 0, 0, false, true);
                 checkUrl(intent)
                         .withIsIncognito(true)
                         .withHasUserGesture(true)
@@ -1185,7 +1212,8 @@
                 mUrlHandler.mResolveInfoContainsSelf = false;
 
                 RedirectHandler redirectHandler = RedirectHandler.create();
-                redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
+                redirectHandler.updateNewUrlLoading(
+                        PageTransition.LINK, false, true, 0, 0, false, true);
                 checkUrl(intent)
                         .withIsIncognito(true)
                         .withHasUserGesture(true)
@@ -1360,7 +1388,7 @@
         mDelegate.setCanResolveActivityForExternalSchemes(false);
 
         RedirectHandler redirectHandler = RedirectHandler.create();
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false, true);
         String intent = "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;"
                 + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "="
                 + "https://play.google.com/store/apps/details?id=com.imdb.mobile"
@@ -1376,7 +1404,7 @@
                 mUrlHandler.mStartActivityIntent.getDataString());
 
         redirectHandler = RedirectHandler.create();
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false, true);
         String intentBadUrl = "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;"
                 + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "="
                 + "https://play.google.com/store/search?q=pub:imdb;end";
@@ -1395,13 +1423,13 @@
 
         RedirectHandler redirectHandler = RedirectHandler.create();
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false, true);
         checkUrl("http://goo.gl/abcdefg")
                 .withPageTransition(PageTransition.LINK)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, true, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, true, 0, 0, false, true);
         String realIntent = "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;"
                 + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "="
                 + "https://play.google.com/store/apps/details?id=com.imdb.mobile"
@@ -1427,7 +1455,8 @@
         mDelegate.add(new IntentActivity(IMDB_WEBPAGE_FOR_TOM_HANKS, WEBAPK_PACKAGE_NAME));
 
         RedirectHandler redirectHandler = RedirectHandler.create();
-        redirectHandler.updateNewUrlLoading(PageTransition.AUTO_SUBFRAME, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                PageTransition.AUTO_SUBFRAME, true, false, 0, 0, false, true);
 
         checkUrl(INTENT_URL_WITH_FALLBACK_URL)
                 .withIsMainFrame(false)
@@ -1444,7 +1473,7 @@
         mDelegate.add(new IntentActivity(IMDB_WEBPAGE_FOR_TOM_HANKS, WEBAPK_PACKAGE_NAME));
 
         RedirectHandler redirectHandler = RedirectHandler.create();
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0, false, true);
 
         checkUrl(INTENT_URL_WITH_FALLBACK_URL)
                 .withHasUserGesture(false)
@@ -1513,23 +1542,30 @@
 
         RedirectHandler redirectHandler = RedirectHandler.create();
 
-        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false, false);
         checkUrl("http://goo.gl/abcdefg")
                 .withPageTransition(PageTransition.TYPED)
                 .withIsRendererInitiated(false)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0, false, true);
         checkUrl(INTENT_URL_WITH_FALLBACK_URL_WITHOUT_PACKAGE_NAME)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE);
 
         // Now the user opens a link.
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 1, false);
-        checkUrl(YOUTUBE_MOBILE_URL)
-                .withRedirectHandler(redirectHandler)
-                .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 1, false, true);
+        if (RedirectHandler.isRefactoringEnabled()) {
+            checkUrl(YOUTUBE_MOBILE_URL)
+                    .withRedirectHandler(redirectHandler)
+                    .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+                            START_OTHER_ACTIVITY);
+        } else {
+            checkUrl(YOUTUBE_MOBILE_URL)
+                    .withRedirectHandler(redirectHandler)
+                    .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+        }
     }
 
     @Test
@@ -1540,7 +1576,7 @@
 
         RedirectHandler redirectHandler = RedirectHandler.create();
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false, true);
         checkUrl(INTENT_URL_WITH_CHAIN_FALLBACK_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE);
@@ -1549,7 +1585,7 @@
         // The fall-back URL was HTTP-schemed, but it was effectively redirected to a new intent
         // URL using javascript. However, we do not allow chained fallback intent, so we do NOT
         // override URL loading here.
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0, false, true);
         checkUrl(INTENT_URL_WITH_FALLBACK_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
@@ -1561,7 +1597,7 @@
         // systemclock or pass the new time as parameter.
         long lastUserInteractionTimeInMillis = SystemClock.elapsedRealtime() + 2 * 1000L;
         redirectHandler.updateNewUrlLoading(
-                PageTransition.LINK, false, true, lastUserInteractionTimeInMillis, 1, false);
+                PageTransition.LINK, false, true, lastUserInteractionTimeInMillis, 1, false, true);
         checkUrl(INTENT_URL_WITH_FALLBACK_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE);
@@ -1574,14 +1610,14 @@
 
         RedirectHandler redirectHandler = RedirectHandler.create();
 
-        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false, false);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withPageTransition(PageTransition.TYPED)
                 .withIsRendererInitiated(false)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, true, false, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, true, false, 0, 0, false, false);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withPageTransition(PageTransition.TYPED)
                 .withIsRendererInitiated(false)
@@ -1589,7 +1625,7 @@
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false, true);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
@@ -1602,18 +1638,18 @@
 
         RedirectHandler redirectHandler = RedirectHandler.create();
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 0, false, true);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 1, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 1, 0, false, true);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withIsRedirect(true)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 1, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 1, false, true);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
@@ -1830,18 +1866,19 @@
 
         RedirectHandler redirectHandler = RedirectHandler.create();
 
-        redirectHandler.updateNewUrlLoading(PageTransition.RELOAD, false, false, 1, 0, false);
+        redirectHandler.updateNewUrlLoading(
+                PageTransition.RELOAD, false, false, 1, 0, false, false);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 1, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 1, 0, false, true);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withIsRedirect(true)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 1, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 1, false, true);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
@@ -1855,19 +1892,19 @@
         RedirectHandler redirectHandler = RedirectHandler.create();
 
         redirectHandler.updateNewUrlLoading(
-                PageTransition.FORM_SUBMIT | PageTransition.FORWARD_BACK, false, false, 1, 0,
+                PageTransition.FORM_SUBMIT | PageTransition.FORWARD_BACK, false, false, 1, 0, false,
                 false);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 1, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 1, 0, false, true);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withIsRedirect(true)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
 
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 1, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 1, false, true);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
@@ -2183,7 +2220,7 @@
     @SmallTest
     public void testAutofillAssistantIntentWithFallback_InRegular() {
         RedirectHandler redirectHandler = RedirectHandler.create();
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, true);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, true, true);
 
         mDelegate.setIsIntentToAutofillAssistant(true);
         checkUrl(AUTOFILL_ASSISTANT_INTENT_URL_WITH_FALLBACK)
@@ -2248,7 +2285,7 @@
         mDelegate.add(new IntentActivity("https://someapp.com", "someapp"));
 
         RedirectHandler redirectHandler = RedirectHandler.create();
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, true);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, true, true);
 
         mUrlHandler.mIsGoogleReferrer = true;
         mDelegate.setIsIntentToAutofillAssistant(true);
@@ -2558,10 +2595,10 @@
         };
 
         // User clicks a link.
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false, true);
 
         // Redirects to youtube with javascript simulated link click.
-        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false);
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false, true);
         checkUrl(YOUTUBE_MOBILE_URL)
                 .withRedirectHandler(redirectHandler)
                 .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
@@ -2574,6 +2611,93 @@
                 .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
     }
 
+    @Test
+    @SmallTest
+    public void testRedirectMethods() {
+        mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME));
+        RedirectHandler redirectHandler = RedirectHandler.create();
+
+        // User clicks a link.
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false, true);
+
+        // Redirects to youtube with javascript simulated link click.
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false, true);
+        checkUrl(YOUTUBE_MOBILE_URL)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+                        START_OTHER_ACTIVITY);
+
+        // Redirects to youtube with client redirect ('window.location =' or meta refresh).
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK | PageTransition.CLIENT_REDIRECT,
+                false, false, 0, 1, false, true);
+        checkUrl(YOUTUBE_MOBILE_URL)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+                        START_OTHER_ACTIVITY);
+
+        // Redirects to youtube with server redirect.
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 0, 1, false, true);
+        checkUrl(YOUTUBE_MOBILE_URL)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+                        START_OTHER_ACTIVITY);
+
+        // Redirects to youtube with form submission.
+        redirectHandler.updateNewUrlLoading(
+                PageTransition.FORM_SUBMIT, false, false, 0, 1, false, true);
+        checkUrl(YOUTUBE_MOBILE_URL)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+                        START_OTHER_ACTIVITY);
+
+        // Redirects to youtube through history API.
+        redirectHandler.updateNewUrlLoading(
+                PageTransition.LINK | PageTransition.FORWARD_BACK, false, false, 0, 1, false, true);
+        checkUrl(YOUTUBE_MOBILE_URL)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+    }
+
+    @Test
+    @SmallTest
+    public void testExcludeBackAndForward() {
+        mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME));
+        RedirectHandler redirectHandler = RedirectHandler.create();
+
+        // User clicks a link.
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false, true);
+
+        // User clicks back button.
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK | PageTransition.FORWARD_BACK,
+                false, false, 1, 1, false, false);
+
+        // Site redirects to youtube.
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK | PageTransition.CLIENT_REDIRECT,
+                false, false, 1, 1, false, true);
+        checkUrl(YOUTUBE_MOBILE_URL)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+    }
+
+    @Test
+    @SmallTest
+    public void testLocationReplace() {
+        mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME));
+        RedirectHandler redirectHandler = RedirectHandler.create();
+
+        // User types a URL.
+        redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, true, 0, 0, false, false);
+
+        // User clicks a link using location.replace().
+        redirectHandler.updateNewUrlLoading(PageTransition.LINK | PageTransition.CLIENT_REDIRECT,
+                false, true, SystemClock.elapsedRealtime() + 1, 1, false, true);
+
+        checkUrl(YOUTUBE_MOBILE_URL)
+                .withRedirectHandler(redirectHandler)
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+                        START_OTHER_ACTIVITY);
+    }
+
     private static List<ResolveInfo> makeResolveInfos(ResolveInfo... infos) {
         return Arrays.asList(infos);
     }
@@ -2706,7 +2830,8 @@
         }
 
         @Override
-        protected OverrideUrlLoadingResult clobberCurrentTab(GURL url, GURL referrerUrl) {
+        protected OverrideUrlLoadingResult clobberCurrentTab(
+                GURL url, GURL referrerUrl, Origin initiatorOrigin, boolean isRendererInitiated) {
             mNewUrlAfterClobbering = url.getSpec();
             mReferrerUrlForClobbering = referrerUrl.getSpec();
             return OverrideUrlLoadingResult.forClobberingTab();
@@ -2726,12 +2851,14 @@
         protected OverrideUrlLoadingResult startActivity(Intent intent, boolean proxy,
                 boolean requiresIntentChooser, QueryIntentActivitiesSupplier resolvingInfos,
                 ResolveActivitySupplier resolveActivity, GURL browserFallbackUrl,
-                GURL intentDataUrl, GURL referrerUrl) {
+                GURL intentDataUrl, GURL referrerUrl, Origin initiatorOrigin,
+                boolean isRendererInitiated) {
             mStartActivityIntent = intent;
             mCalledWithProxy = proxy;
             if (mSendIntentsForReal) {
                 return super.startActivity(intent, proxy, requiresIntentChooser, resolvingInfos,
-                        resolveActivity, browserFallbackUrl, intentDataUrl, referrerUrl);
+                        resolveActivity, browserFallbackUrl, intentDataUrl, referrerUrl,
+                        initiatorOrigin, isRendererInitiated);
             }
             return OverrideUrlLoadingResult.forExternalIntent();
         }
diff --git a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java
index fe8d741..b0c05b6 100644
--- a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java
+++ b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java
@@ -33,6 +33,7 @@
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Unittests for tab redirect handler.
@@ -85,10 +86,12 @@
         handler.updateIntent(sYtIntent, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, true, false, 0, 0, false);
-        Assert.assertTrue(handler.isOnEffectiveIntentRedirectChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false, false);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, true, false, 0, 0, false, false);
+        Assert.assertTrue(handler.isOnNoninitialLoadForIntentNavigationChain());
         Assert.assertFalse(handler.hasNewResolver(
                 queryIntentActivities(sMoblieYtIntent), mQueryIntentFunction));
         Assert.assertTrue(
@@ -108,10 +111,11 @@
         handler.updateIntent(sYtIntent, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
-        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false);
-        Assert.assertTrue(handler.isOnEffectiveIntentRedirectChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false, false);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
+        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false, true);
+        Assert.assertTrue(handler.isOnNoninitialLoadForIntentNavigationChain());
         Assert.assertFalse(handler.hasNewResolver(
                 queryIntentActivities(sMoblieYtIntent), mQueryIntentFunction));
         Assert.assertTrue(
@@ -131,10 +135,11 @@
         handler.updateIntent(sYtIntent, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
-        handler.updateNewUrlLoading(PageTransition.FORM_SUBMIT, false, false, 0, 1, false);
-        Assert.assertTrue(handler.isOnEffectiveIntentRedirectChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false, false);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
+        handler.updateNewUrlLoading(PageTransition.FORM_SUBMIT, false, false, 0, 1, false, true);
+        Assert.assertTrue(handler.isOnNoninitialLoadForIntentNavigationChain());
         Assert.assertFalse(handler.hasNewResolver(
                 queryIntentActivities(sMoblieYtIntent), mQueryIntentFunction));
         Assert.assertTrue(
@@ -154,10 +159,12 @@
         handler.updateIntent(null, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, true, false, 0, 0, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false, false);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, true, false, 0, 0, false, false);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
         Assert.assertTrue(handler.hasNewResolver(
                 queryIntentActivities(sMoblieYtIntent), mQueryIntentFunction));
         Assert.assertTrue(
@@ -177,10 +184,12 @@
         handler.updateIntent(sYtIntent, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, true, false, 0, 0, false);
-        Assert.assertTrue(handler.isOnEffectiveIntentRedirectChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false, false);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, true, false, 0, 0, false, false);
+        Assert.assertTrue(handler.isOnNoninitialLoadForIntentNavigationChain());
         Assert.assertFalse(handler.hasNewResolver(
                 queryIntentActivities(sMoblieYtIntent), mQueryIntentFunction));
 
@@ -205,10 +214,10 @@
         handler.updateIntent(sYtIntent, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
-        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
+        handler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false, false);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
+        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false, true);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
         Assert.assertTrue(handler.hasNewResolver(
                 queryIntentActivities(sMoblieYtIntent), mQueryIntentFunction));
         Assert.assertTrue(
@@ -228,10 +237,12 @@
         handler.updateIntent(sYtIntent, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, true, false, 0, 0, false);
-        Assert.assertTrue(handler.isOnEffectiveIntentRedirectChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false, false);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, true, false, 0, 0, false, false);
+        Assert.assertTrue(handler.isOnNoninitialLoadForIntentNavigationChain());
         Assert.assertFalse(handler.hasNewResolver(
                 queryIntentActivities(sMoblieYtIntent), mQueryIntentFunction));
 
@@ -240,8 +251,8 @@
 
         SystemClock.sleep(1);
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 1, false);
-        Assert.assertFalse(handler.isOnEffectiveIntentRedirectChain());
+                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 1, false, true);
+        Assert.assertFalse(handler.isOnNoninitialLoadForIntentNavigationChain());
         Assert.assertTrue(handler.hasNewResolver(
                 queryIntentActivities(sMoblieYtIntent), mQueryIntentFunction));
         Assert.assertTrue(
@@ -263,10 +274,11 @@
         handler.updateIntent(fooIntent, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false, false);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertFalse(handler.shouldStayInApp(true));
-        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false);
+        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false, true);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertFalse(handler.shouldStayInApp(true));
 
@@ -275,7 +287,7 @@
 
         SystemClock.sleep(1);
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false);
+                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false, true);
         Assert.assertFalse(handler.shouldStayInApp(false));
         Assert.assertFalse(handler.shouldStayInApp(true));
 
@@ -291,9 +303,9 @@
         handler.updateIntent(sYtIntent, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false);
+        handler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0, false, false);
         Assert.assertTrue(handler.isNavigationFromUserTyping());
-        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false);
+        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false, true);
         Assert.assertTrue(handler.isNavigationFromUserTyping());
 
         Assert.assertTrue(handler.isOnNavigation());
@@ -301,7 +313,7 @@
 
         SystemClock.sleep(1);
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false);
+                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false, true);
         Assert.assertFalse(handler.isNavigationFromUserTyping());
 
         Assert.assertTrue(handler.isOnNavigation());
@@ -318,10 +330,11 @@
         handler.updateIntent(fooIntent, false, false, false);
         Assert.assertFalse(handler.isOnNavigation());
 
-        handler.updateNewUrlLoading(TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false);
+        handler.updateNewUrlLoading(
+                TRANS_TYPE_OF_LINK_FROM_INTENT, false, false, 0, 0, false, false);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertFalse(handler.shouldStayInApp(true));
-        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false);
+        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false, true);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertFalse(handler.shouldStayInApp(true));
 
@@ -330,7 +343,7 @@
 
         SystemClock.sleep(1);
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false);
+                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false, true);
         Assert.assertFalse(handler.shouldStayInApp(false));
         Assert.assertFalse(handler.shouldStayInApp(true));
 
@@ -348,11 +361,11 @@
         RedirectHandler handler = RedirectHandler.create();
         handler.updateIntent(sYtIntent, false, false, false);
 
-        handler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
+        handler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false, true);
         Assert.assertFalse(handler.shouldNotOverrideUrlLoading());
         handler.setShouldNotOverrideUrlLoadingOnCurrentRedirectChain();
 
-        handler.updateNewUrlLoading(PageTransition.LINK, true, false, 0, 0, false);
+        handler.updateNewUrlLoading(PageTransition.LINK, true, false, 0, 0, false, true);
         Assert.assertTrue(handler.shouldNotOverrideUrlLoading());
         Assert.assertEquals(0, handler.getLastCommittedEntryIndexBeforeStartingNavigation());
 
@@ -362,12 +375,12 @@
         handler = RedirectHandler.create();
         handler.updateIntent(sYtIntent, false, false, false);
 
-        handler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
+        handler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false, true);
         Assert.assertFalse(handler.shouldNotOverrideUrlLoading());
         handler.setShouldNotOverrideUrlLoadingOnCurrentRedirectChain();
 
         // Effective redirection occurred.
-        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false);
+        handler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false, true);
         Assert.assertTrue(handler.shouldNotOverrideUrlLoading());
         Assert.assertEquals(0, handler.getLastCommittedEntryIndexBeforeStartingNavigation());
 
@@ -376,7 +389,7 @@
         /////////////////////////////////////////////////////
         SystemClock.sleep(1);
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false);
+                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false, true);
         Assert.assertFalse(handler.shouldNotOverrideUrlLoading());
         Assert.assertEquals(2, handler.getLastCommittedEntryIndexBeforeStartingNavigation());
     }
@@ -391,11 +404,11 @@
 
         long lastUserInteractionTime = SystemClock.elapsedRealtime();
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, false, lastUserInteractionTime, 0, false);
+                PageTransition.LINK, false, false, lastUserInteractionTime, 0, false, true);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertTrue(handler.shouldStayInApp(true));
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, false, lastUserInteractionTime, 1, false);
+                PageTransition.LINK, false, false, lastUserInteractionTime, 1, false, true);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertTrue(handler.shouldStayInApp(true));
 
@@ -404,7 +417,7 @@
 
         SystemClock.sleep(1);
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false);
+                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false, true);
         Assert.assertFalse(handler.shouldStayInApp(false));
         Assert.assertFalse(handler.shouldStayInApp(true));
 
@@ -422,11 +435,11 @@
 
         long lastUserInteractionTime = SystemClock.elapsedRealtime();
         handler.updateNewUrlLoading(
-                PageTransition.RELOAD, false, false, lastUserInteractionTime, 0, false);
+                PageTransition.RELOAD, false, false, lastUserInteractionTime, 0, false, false);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertTrue(handler.shouldStayInApp(true));
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, false, lastUserInteractionTime, 1, false);
+                PageTransition.LINK, false, false, lastUserInteractionTime, 1, false, true);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertTrue(handler.shouldStayInApp(true));
 
@@ -435,7 +448,7 @@
 
         SystemClock.sleep(1);
         handler.updateNewUrlLoading(
-                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false);
+                PageTransition.LINK, false, true, SystemClock.elapsedRealtime(), 2, false, true);
         Assert.assertFalse(handler.shouldStayInApp(false));
         Assert.assertFalse(handler.shouldStayInApp(true));
 
@@ -453,13 +466,13 @@
 
         long lastUserInteractionTime = SystemClock.elapsedRealtime();
         handler.updateNewUrlLoading(PageTransition.FORM_SUBMIT | PageTransition.FORWARD_BACK, false,
-                true, lastUserInteractionTime, 0, false);
+                true, lastUserInteractionTime, 0, false, false);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertTrue(handler.shouldStayInApp(true));
         Assert.assertTrue(handler.hasUserStartedNonInitialNavigation());
 
         handler.updateNewUrlLoading(PageTransition.LINK, false, false, lastUserInteractionTime, 1,
-                false /* isInitialNavigation */);
+                false /* isInitialNavigation */, true);
         Assert.assertTrue(handler.shouldStayInApp(false));
         Assert.assertTrue(handler.shouldStayInApp(true));
 
@@ -469,7 +482,7 @@
 
         SystemClock.sleep(1);
         handler.updateNewUrlLoading(PageTransition.LINK, false, true, SystemClock.elapsedRealtime(),
-                2, false /* isInitialNavigation */);
+                2, false /* isInitialNavigation */, true);
         Assert.assertFalse(handler.shouldStayInApp(false));
         Assert.assertFalse(handler.shouldStayInApp(true));
 
@@ -490,7 +503,7 @@
         Assert.assertFalse(handler.isOnNavigation());
         handler.updateNewUrlLoading(PageTransition.LINK, false, true,
                 uninitializedUserInteractionTime, RedirectHandler.NO_COMMITTED_ENTRY_INDEX,
-                true /* isInitialNavigation */);
+                true /* isInitialNavigation */, true);
         Assert.assertTrue(handler.isOnNavigation());
         Assert.assertEquals(RedirectHandler.NO_COMMITTED_ENTRY_INDEX,
                 handler.getLastCommittedEntryIndexBeforeStartingNavigation());
@@ -512,7 +525,8 @@
 
         // Navigation to a page that will trigger a client redirect.
         handler.updateNewUrlLoading(PageTransition.LINK, false /* isRedirect */,
-                false /* hasUserGesture */, 0, 0, true /* isInitialNavigation */);
+                false /* hasUserGesture */, 0, 0, true /* isInitialNavigation */,
+                true /* isRendererInitiated */);
         Assert.assertTrue(handler.shouldStayInApp(true));
         Assert.assertFalse(handler.shouldStayInApp(true, true));
         Assert.assertFalse(handler.hasUserStartedNonInitialNavigation());
@@ -520,7 +534,7 @@
         // Client redirect navigation.
         handler.updateNewUrlLoading(PageTransition.LINK | PageTransition.CLIENT_REDIRECT,
                 false /* isRedirect */, false /* hasUserGesture */, 0, 0,
-                false /* isInitialNavigation */);
+                false /* isInitialNavigation */, true /* isRendererInitiated */);
         Assert.assertTrue(handler.shouldStayInApp(true));
         Assert.assertFalse(handler.shouldStayInApp(true, true));
         Assert.assertFalse(handler.hasUserStartedNonInitialNavigation());
@@ -533,12 +547,32 @@
         int lastIndex = 1234;
         RedirectHandler handler = RedirectHandler.create();
         handler.updateNewUrlLoading(PageTransition.LINK, false /* isRedirect */,
-                false /* hasUserGesture */, 0, lastIndex, true /* isInitialNavigation */);
+                false /* hasUserGesture */, 0, lastIndex, true /* isInitialNavigation */,
+                true /* isRendererInitiated */);
         handler.clear();
         Assert.assertEquals(
                 lastIndex, handler.getLastCommittedEntryIndexBeforeStartingNavigation());
     }
 
+    @Test
+    @SmallTest
+    @Feature({"IntentHandling"})
+    public void testNavigationChainExpired() {
+        long navigationId = 1234;
+        AtomicLong currentTime = new AtomicLong(0);
+        RedirectHandler handler = new RedirectHandler() {
+            @Override
+            public long currentRealtime() {
+                return currentTime.get();
+            }
+        };
+        handler.updateNewUrlLoading(PageTransition.LINK, false /* isRedirect */,
+                true /* hasUserGesture */, 0, 0, true /* isInitialNavigation */,
+                true /* isRendererInitiated */);
+        currentTime.set(RedirectHandler.NAVIGATION_CHAIN_TIMEOUT_MILLIS + 1);
+        Assert.assertTrue(handler.isNavigationChainExpired());
+    }
+
     private static class TestPackageManager extends MockPackageManager {
         @Override
         public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
diff --git a/components/feed/core/v2/metrics_reporter.cc b/components/feed/core/v2/metrics_reporter.cc
index 359c377b26..3423d41 100644
--- a/components/feed/core/v2/metrics_reporter.cc
+++ b/components/feed/core/v2/metrics_reporter.cc
@@ -52,6 +52,9 @@
 // timeout.
 constexpr base::TimeDelta kTimeSpentInFeedInteractionTimeout =
     base::Seconds(30);
+// The maximum time between sequential interactions with the feed that are
+// considered as a single visit.
+constexpr base::TimeDelta kVisitTimeout = base::Minutes(5);
 
 void ReportEngagementTypeHistogram(const StreamType& stream_type,
                                    FeedEngagementType engagement_type) {
@@ -203,6 +206,31 @@
   }
 }
 
+void ReportSubscriptionCountAtEngagementTime(const StreamType& stream_type,
+                                             int subscription_count) {
+  if (stream_type.IsForYou()) {
+    base::UmaHistogramSparse("ContentSuggestions.Feed.FollowCount.Engaged2",
+                             subscription_count);
+  } else {
+    DCHECK(stream_type.IsWebFeed());
+    base::UmaHistogramSparse(
+        "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged2",
+        subscription_count);
+  }
+}
+
+void ReportCombinedSubscriptionCountAtEngagementTime(int subscription_count) {
+  base::UmaHistogramSparse(
+      "ContentSuggestions.Feed.AllFeeds.FollowCount.Engaged2",
+      subscription_count);
+  // TODO(b/228342051): The histogram below is being obsoleted because it has a
+  // misleading name. Once the new *.Engaged2 series collects a large enough
+  // sample history, it will be effectively removed/obsoleted.
+  base::UmaHistogramSparse(
+      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged",
+      subscription_count);
+}
+
 }  // namespace
 
 MetricsReporter::SurfaceWaiting::SurfaceWaiting() = default;
@@ -314,13 +342,12 @@
                                        int scroll_distance_dp,
                                        bool interacted) {
   scroll_distance_dp = std::abs(scroll_distance_dp);
-  // Determine if this interaction is part of a new 'session'.
+  // Determine if this interaction is part of a new feed 'visit'.
   base::TimeTicks now = base::TimeTicks::Now();
-  const base::TimeDelta kVisitTimeout = base::Minutes(5);
   if (now - visit_start_time_ > kVisitTimeout) {
     FinalizeVisit();
   }
-  // Reset the last active time for session measurement.
+  // Reset the last active time for visit measurement.
   visit_start_time_ = now;
 
   TrackTimeSpentInFeed(true);
@@ -352,12 +379,19 @@
     data.engaged_reported = true;
     if (!combined_stats_.engaged_reported) {
       ReportCombinedEngagementTypeHistogram(FeedEngagementType::kFeedEngaged);
-      // Unretained is safe because MetricsReporter outlives `FeedStream`.
+      // Reports subscription count for the specific feed and for the combined
+      // histogram.
       delegate_->SubscribedWebFeedCount(base::BindOnce(
-          &MetricsReporter::ReportSubscriptionCountAtEngagementTime,
-          base::Unretained(this)));
-
+          [](const StreamType& st, int sc) {
+            ReportSubscriptionCountAtEngagementTime(st, sc);
+            ReportCombinedSubscriptionCountAtEngagementTime(sc);
+          },
+          stream_type));
       combined_stats_.engaged_reported = true;
+    } else {
+      // Reports subscription count for the specific feed only.
+      delegate_->SubscribedWebFeedCount(base::BindOnce(
+          &ReportSubscriptionCountAtEngagementTime, stream_type));
     }
   }
 }
@@ -976,13 +1010,6 @@
   }
 }
 
-void MetricsReporter::ReportSubscriptionCountAtEngagementTime(
-    int subscription_count) {
-  base::UmaHistogramSparse(
-      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged",
-      subscription_count);
-}
-
 void MetricsReporter::ReportFollowCountOnLoad(bool content_shown,
                                               int subscription_count) {
   base::UmaHistogramSparse(
diff --git a/components/feed/core/v2/metrics_reporter.h b/components/feed/core/v2/metrics_reporter.h
index 38cd6aa..21cf62d 100644
--- a/components/feed/core/v2/metrics_reporter.h
+++ b/components/feed/core/v2/metrics_reporter.h
@@ -184,7 +184,6 @@
   void ReportGetMoreIfNeeded(SurfaceId surface_id, bool success);
   void FinalizeMetrics();
   void FinalizeVisit();
-  void ReportSubscriptionCountAtEngagementTime(int subscription_count);
   void ReportFollowCountOnLoad(bool content_shown, int subscription_count);
 
   StreamStats& ForStream(const StreamType& stream_type);
diff --git a/components/feed/core/v2/metrics_reporter_unittest.cc b/components/feed/core/v2/metrics_reporter_unittest.cc
index 35e3833..a2c3613 100644
--- a/components/feed/core/v2/metrics_reporter_unittest.cc
+++ b/components/feed/core/v2/metrics_reporter_unittest.cc
@@ -126,6 +126,14 @@
   });
   EXPECT_EQ(want, ReportedEngagementType(kForYouStream));
   EXPECT_EQ(want, ReportedEngagementType(kCombinedStreams));
+  histogram_.ExpectTotalCount("ContentSuggestions.Feed.FollowCount.Engaged2",
+                              0);
+  histogram_.ExpectTotalCount(
+      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged2", 0);
+  histogram_.ExpectTotalCount(
+      "ContentSuggestions.Feed.AllFeeds.FollowCount.Engaged2", 0);
+  histogram_.ExpectTotalCount(
+      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged", 0);
 }
 
 TEST_F(MetricsReporterTest, ScrollingCanTriggerEngaged) {
@@ -138,6 +146,16 @@
   });
   EXPECT_EQ(want, ReportedEngagementType(kForYouStream));
   EXPECT_EQ(want, ReportedEngagementType(kCombinedStreams));
+  histogram_.ExpectUniqueSample("ContentSuggestions.Feed.FollowCount.Engaged2",
+                                kSubscriptionCount, 1);
+  histogram_.ExpectTotalCount(
+      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged2", 0);
+  histogram_.ExpectUniqueSample(
+      "ContentSuggestions.Feed.AllFeeds.FollowCount.Engaged2",
+      kSubscriptionCount, 1);
+  histogram_.ExpectUniqueSample(
+      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged", kSubscriptionCount,
+      1);
 }
 
 TEST_F(MetricsReporterTest, OpeningContentIsInteracting) {
@@ -247,6 +265,14 @@
       {FeedEngagementType::kFeedScrolled, 1},
   });
   EXPECT_EQ(want_1c, ReportedEngagementType(kCombinedStreams));
+  histogram_.ExpectUniqueSample("ContentSuggestions.Feed.FollowCount.Engaged2",
+                                kSubscriptionCount, 1);
+  histogram_.ExpectUniqueSample(
+      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged2",
+      kSubscriptionCount, 1);
+  histogram_.ExpectUniqueSample(
+      "ContentSuggestions.Feed.AllFeeds.FollowCount.Engaged2",
+      kSubscriptionCount, 1);
   histogram_.ExpectUniqueSample(
       "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged", kSubscriptionCount,
       1);
@@ -272,6 +298,14 @@
       {FeedEngagementType::kFeedScrolled, 2},
   });
   EXPECT_EQ(want_2c, ReportedEngagementType(kCombinedStreams));
+  histogram_.ExpectUniqueSample("ContentSuggestions.Feed.FollowCount.Engaged2",
+                                kSubscriptionCount, 2);
+  histogram_.ExpectUniqueSample(
+      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged2",
+      kSubscriptionCount, 1);
+  histogram_.ExpectUniqueSample(
+      "ContentSuggestions.Feed.AllFeeds.FollowCount.Engaged2",
+      kSubscriptionCount, 2);
   histogram_.ExpectUniqueSample(
       "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged", kSubscriptionCount,
       2);
@@ -556,6 +590,13 @@
   histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserActions",
                                 FeedUserActionType::kTappedOnCard, 1);
   histogram_.ExpectUniqueSample("NewTabPage.ContentSuggestions.Opened", 5, 1);
+  histogram_.ExpectUniqueSample("ContentSuggestions.Feed.FollowCount.Engaged2",
+                                kSubscriptionCount, 1);
+  histogram_.ExpectTotalCount(
+      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged2", 0);
+  histogram_.ExpectUniqueSample(
+      "ContentSuggestions.Feed.AllFeeds.FollowCount.Engaged2",
+      kSubscriptionCount, 1);
   histogram_.ExpectUniqueSample(
       "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged", kSubscriptionCount,
       1);
@@ -576,6 +617,14 @@
   histogram_.ExpectUniqueSample("ContentSuggestions.Feed.UserActions",
                                 FeedUserActionType::kTappedOnCard, 1);
   histogram_.ExpectUniqueSample("ContentSuggestions.Feed.WebFeed.Opened", 5, 1);
+  histogram_.ExpectTotalCount("ContentSuggestions.Feed.FollowCount.Engaged2",
+                              0);
+  histogram_.ExpectUniqueSample(
+      "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged2",
+      kSubscriptionCount, 1);
+  histogram_.ExpectUniqueSample(
+      "ContentSuggestions.Feed.AllFeeds.FollowCount.Engaged2",
+      kSubscriptionCount, 1);
   histogram_.ExpectUniqueSample(
       "ContentSuggestions.Feed.WebFeed.FollowCount.Engaged", kSubscriptionCount,
       1);
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h
index 2c03696..f19f6f8d 100644
--- a/components/history/core/browser/history_types.h
+++ b/components/history/core/browser/history_types.h
@@ -78,7 +78,14 @@
     return visit_time < other.visit_time;
   }
 
-  // ID of this row (visit ID, used a a referrer for other visits).
+  // Row ID of this visit in the table. Some nuances with this ID:
+  //  - Do NOT assume that a higher `visit_id` implies a more recent visit.
+  //    For example: A Mobile phone that recently got back online can sync a
+  //    bunch of older visits onto a Desktop machine all at once.
+  //  - Do NOT assume that `visit_id` for the same synced visit matches across
+  //    devices. This is just a local AUTOINCREMENTed SQL row ID that has no
+  //    special meaning or uniqueness guarantee outside of this local machine.
+  //  - See `originator_cache_guid` and `originator_visit_id` for more details.
   VisitID visit_id = 0;
 
   // Row ID into the URL table of the URL that this page is.
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java
index 5c0e523..1f6989e1 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeControllerTest.java
@@ -289,7 +289,7 @@
             boolean isSameDocument, boolean isReload, boolean didCommit, GURL url) {
         NavigationHandle handle = new NavigationHandle(0, url, null, null, isMainFrame,
                 isSameDocument, true, null, 0, false, false, false, false, -1, false, isReload);
-        handle.didFinish(url, false, didCommit, false, false, false, 0, 0, 0);
+        handle.didFinish(url, false, didCommit, false, false, false, 0, 0, 0, false);
         return handle;
     }
 }
diff --git a/components/omnibox/browser/history_fuzzy_provider.cc b/components/omnibox/browser/history_fuzzy_provider.cc
index 5e65bd9..e310ddb 100644
--- a/components/omnibox/browser/history_fuzzy_provider.cc
+++ b/components/omnibox/browser/history_fuzzy_provider.cc
@@ -32,6 +32,7 @@
 #include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/bookmark_provider.h"
 #include "components/omnibox/browser/history_quick_provider.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/search_engines/omnibox_focus_type.h"
 #include "components/url_formatter/elide_url.h"
 #include "url/gurl.h"
@@ -46,6 +47,12 @@
 const char kMetricMatchConversionBookmark[] =
     "Omnibox.FuzzyMatchConversion.Bookmark";
 
+// This cap ensures the search trie will not grow without bound. Up to half
+// the total capacity may be filled at startup from loaded significant URLs.
+// The enforced limit may be further constrained by
+// `MaxNumHQPUrlsIndexedAtStartup`.
+constexpr int kMaxTerminalCount = 1000;
+
 // This utility function reduces a URL to the most meaningful and likely part
 // of the hostname to be matched against, i.e. the domain, the URL's TLD+1.
 // May return an empty string if the given URL is not a good candidate for
@@ -224,26 +231,38 @@
 
 Node::~Node() = default;
 
-void Node::Insert(const std::u16string& text, size_t from) {
-  if (from >= text.length()) {
+void Node::Insert(const std::u16string& text, size_t text_index) {
+  if (text_index >= text.length()) {
+    relevance_total += 1 - relevance;
     relevance = 1;
     return;
   }
-  std::unique_ptr<Node>& node = next[text[from]];
+  std::unique_ptr<Node>& node = next[text[text_index]];
   if (!node) {
     node = std::make_unique<Node>();
   }
-  node->Insert(text, from + 1);
+  relevance_total -= node->relevance_total;
+  node->Insert(text, text_index + 1);
+  relevance_total += node->relevance_total;
 }
 
-bool Node::Delete(const std::u16string& text, size_t from) {
-  if (from < text.length()) {
-    auto it = next.find(text[from]);
-    if (it != next.end() && it->second->Delete(text, from + 1)) {
-      next.erase(it);
+void Node::Delete(const std::u16string& text, size_t text_index) {
+  if (text_index < text.length()) {
+    auto it = next.find(text[text_index]);
+    if (it != next.end()) {
+      Node* const node = it->second.get();
+      relevance_total -= node->relevance_total;
+      node->Delete(text, text_index + 1);
+      if (node->relevance_total == 0) {
+        next.erase(it);
+      } else {
+        relevance_total += node->relevance_total;
+      }
     }
+  } else {
+    relevance_total -= relevance;
+    relevance = 0;
   }
-  return next.empty();
 }
 
 void Node::Clear() {
@@ -407,21 +426,18 @@
   return false;
 }
 
-void Node::Log(std::u16string built) const {
-  if (relevance > 0) {
-    DVLOG(1) << "  <" << built << ">";
-  }
-  for (const auto& entry : next) {
-    entry.second->Log(built + entry.first);
-  }
-}
-
 size_t Node::EstimateMemoryUsage() const {
   size_t res = 0;
   res += base::trace_event::EstimateMemoryUsage(next);
   return res;
 }
 
+int Node::TerminalCount() const {
+  // This works as long as `relevance` values mark terminals with 1 and
+  // non-terminals with 0; see `Insert()`.
+  return relevance_total;
+}
+
 // This task class loads URLs considered significant according to
 // `HistoryDatabase::InitURLEnumeratorForSignificant` but there's nothing
 // special about that implementation; we may do something different for
@@ -446,7 +462,17 @@
     if (db && db->InitURLEnumeratorForSignificant(&enumerator)) {
       DVLOG(1) << "Got InMemoryDatabase";
       history::URLRow row;
-      while (enumerator.GetNextURL(&row)) {
+      // The `MaxNumHQPUrlsIndexedAtStartup` dependency here is to ensure
+      // that we keep a lower cap for mobile; it's much higher on desktop.
+      // Note the divide, which ensures at least half the capacity will be kept
+      // for later visited domains. `GetNextUrl` takes the most significant
+      // URLs from the database (enumerator order) and duplicates won't count.
+      const int max_terminal_count =
+          std::min(OmniboxFieldTrial::MaxNumHQPUrlsIndexedAtStartup(),
+                   kMaxTerminalCount) /
+          2;
+      while (enumerator.GetNextURL(&row) &&
+             node_.TerminalCount() < max_terminal_count) {
         DVLOG(1) << "url #" << row.id() << ": " << row.url().host();
         node_.Insert(UrlDomainReduction(row.url()), 0);
       }
@@ -534,74 +560,64 @@
              << "'";
     return;
   }
-  if (text[text.length() - 1] == u'!') {
-    if (text == u"log!") {
-      DVLOG(1) << "Trie Log: !{";
-      root_.Log(std::u16string());
-      DVLOG(1) << "}!";
-    } else {
-      root_.Insert(text.substr(0, text.length() - 1), 0);
+  std::vector<fuzzy::Correction> corrections;
+  DVLOG(1) << "FindCorrections: <" << text << "> ---> ?{";
+  if (root_.FindCorrections(text, kToleranceSchedule, corrections)) {
+    DVLOG(1) << "Trie contains input; no fuzzy results needed";
+  }
+  if (!corrections.empty()) {
+    // Use of `scoped_refptr` is required here because destructor is private.
+    scoped_refptr<HistoryQuickProvider> history_quick_provider =
+        new HistoryQuickProvider(client());
+    scoped_refptr<BookmarkProvider> bookmark_provider =
+        new BookmarkProvider(client());
+    int count_history_quick = 0;
+    int count_bookmark = 0;
+    for (const auto& correction : corrections) {
+      std::u16string fixed = text;
+      correction.ApplyTo(fixed);
+      DVLOG(1) << ":  " << fixed;
+
+      // Note the `cursor_position` could be changed by insert or delete
+      // corrections, but this is easy to adapt since we only fuzzy
+      // match when cursor is at end of input; just move to new end.
+      DCHECK_EQ(autocomplete_input_.cursor_position(),
+                autocomplete_input_.text().length());
+      AutocompleteInput corrected_input(
+          fixed, fixed.length(),
+          autocomplete_input_.current_page_classification(),
+          client()->GetSchemeClassifier());
+
+      history_quick_provider->Start(corrected_input, false);
+      DCHECK(history_quick_provider->done());
+      bookmark_provider->Start(corrected_input, false);
+      DCHECK(bookmark_provider->done());
+
+      count_history_quick +=
+          AddConvertedMatches(history_quick_provider->matches());
+      count_bookmark += AddConvertedMatches(bookmark_provider->matches());
     }
-  } else {
-    std::vector<fuzzy::Correction> corrections;
-    DVLOG(1) << "FindCorrections: <" << text << "> ---> ?{";
-    if (root_.FindCorrections(text, kToleranceSchedule, corrections)) {
-      DVLOG(1) << "Trie contains input; no fuzzy results needed";
-    }
-    if (!corrections.empty()) {
-      // Use of `scoped_refptr` is required here because destructor is private.
-      scoped_refptr<HistoryQuickProvider> history_quick_provider =
-          new HistoryQuickProvider(client());
-      scoped_refptr<BookmarkProvider> bookmark_provider =
-          new BookmarkProvider(client());
-      int count_history_quick = 0;
-      int count_bookmark = 0;
-      for (const auto& correction : corrections) {
-        std::u16string fixed = text;
-        correction.ApplyTo(fixed);
-        DVLOG(1) << ":  " << fixed;
-
-        // Note the `cursor_position` could be changed by insert or delete
-        // corrections, but this is easy to adapt since we only fuzzy
-        // match when cursor is at end of input; just move to new end.
-        DCHECK_EQ(autocomplete_input_.cursor_position(),
-                  autocomplete_input_.text().length());
-        AutocompleteInput corrected_input(
-            fixed, fixed.length(),
-            autocomplete_input_.current_page_classification(),
-            client()->GetSchemeClassifier());
-
-        history_quick_provider->Start(corrected_input, false);
-        DCHECK(history_quick_provider->done());
-        bookmark_provider->Start(corrected_input, false);
-        DCHECK(bookmark_provider->done());
-
-        count_history_quick +=
-            AddConvertedMatches(history_quick_provider->matches());
-        count_bookmark += AddConvertedMatches(bookmark_provider->matches());
-      }
-      if (matches_.size() > provider_max_matches_) {
-        // When too many matches are generated, take only the most relevant
-        // matches and correct the counts for accurate metrics.
-        std::partial_sort(matches_.begin(),
-                          matches_.begin() + provider_max_matches_,
-                          matches_.end(), AutocompleteMatch::MoreRelevant);
-        for (size_t i = provider_max_matches_; i < matches_.size(); i++) {
-          DCHECK(matches_[i].provider == history_quick_provider ||
-                 matches_[i].provider == bookmark_provider)
-              << matches_[i].provider->GetName();
-          if (matches_[i].provider == history_quick_provider) {
-            count_history_quick--;
-          } else {
-            count_bookmark--;
-          }
+    if (matches_.size() > provider_max_matches_) {
+      // When too many matches are generated, take only the most relevant
+      // matches and correct the counts for accurate metrics.
+      std::partial_sort(matches_.begin(),
+                        matches_.begin() + provider_max_matches_,
+                        matches_.end(), AutocompleteMatch::MoreRelevant);
+      for (size_t i = provider_max_matches_; i < matches_.size(); i++) {
+        DCHECK(matches_[i].provider == history_quick_provider ||
+               matches_[i].provider == bookmark_provider)
+            << matches_[i].provider->GetName();
+        if (matches_[i].provider == history_quick_provider) {
+          count_history_quick--;
+        } else {
+          count_bookmark--;
         }
-        matches_.resize(provider_max_matches_);
       }
-      RecordMatchConversion(kMetricMatchConversionHistoryQuick,
-                            count_history_quick);
-      RecordMatchConversion(kMetricMatchConversionBookmark, count_bookmark);
+      matches_.resize(provider_max_matches_);
     }
+    RecordMatchConversion(kMetricMatchConversionHistoryQuick,
+                          count_history_quick);
+    RecordMatchConversion(kMetricMatchConversionBookmark, count_bookmark);
     DVLOG(1) << "}?";
   }
 }
@@ -656,7 +672,11 @@
     const history::URLRow& row,
     base::Time visit_time) {
   DVLOG(1) << "URL Visit: " << row.url();
-  root_.Insert(UrlDomainReduction(row.url()), 0);
+  if (root_.TerminalCount() <
+      std::min(OmniboxFieldTrial::MaxNumHQPUrlsIndexedAtStartup(),
+               kMaxTerminalCount)) {
+    root_.Insert(UrlDomainReduction(row.url()), 0);
+  }
 }
 
 void HistoryFuzzyProvider::OnURLsDeleted(
diff --git a/components/omnibox/browser/history_fuzzy_provider.h b/components/omnibox/browser/history_fuzzy_provider.h
index 8a319d5b..ebd2f29 100644
--- a/components/omnibox/browser/history_fuzzy_provider.h
+++ b/components/omnibox/browser/history_fuzzy_provider.h
@@ -108,13 +108,12 @@
   ~Node();
 
   // Walk the trie, injecting nodes as necessary to build the given `text`
-  // starting at index `from`. The `from` parameter advances as an index into
-  // `text` and ensures recursion is bounded.
-  void Insert(const std::u16string& text, size_t from);
+  // starting at `text_index`. The `text_index` parameter advances as an index
+  // into `text` and ensures recursion is bounded.
+  void Insert(const std::u16string& text, size_t text_index);
 
   // Delete nodes as necessary to remove given `text` from the trie.
-  // Returns true if this node is left empty and may be deleted.
-  bool Delete(const std::u16string& text, size_t from);
+  void Delete(const std::u16string& text, size_t text_index);
 
   // Delete all nodes to clear the trie.
   void Clear();
@@ -133,18 +132,22 @@
                        ToleranceSchedule tolerance_schedule,
                        std::vector<Correction>& corrections) const;
 
-  // TODO(orinj): Remove this. It's a development-only debugging utility.
-  void Log(std::u16string built) const;
-
   // Estimates dynamic memory usage.
   // See base/trace_event/memory_usage_estimator.h for more info.
   size_t EstimateMemoryUsage() const;
 
+  // Returns number of terminals contained within this trie (may include self).
+  int TerminalCount() const;
+
   // This is used to distinguish terminal nodes in the trie (nonzero values).
-  // TODO(orinj): Consider removing this if we only correct inputs and leave
-  //  scoring to other autocomplete machinery.
   int relevance = 0;
 
+  // This maintains the sum of `relevance` plus all `relevance_total` values
+  // contained within `next`. As long as `relevance` values are 0 or 1, this can
+  // be used as a count of contained terminals. When it drops to zero, the
+  // node may be deleted from the trie.
+  int relevance_total = 0;
+
   // Note: Some C++ implementations of unordered_map support using the
   // containing struct (Node) as the element type, but some do not. To avoid
   // potential build issues in downstream projects that use Chromium code,
@@ -218,14 +221,7 @@
 
   AutocompleteInput autocomplete_input_;
 
-  // TODO(orinj): For now this is memory resident for proof of concept, but
-  //  most likely the full implementation will store the tree in a SQL table
-  //  for persistence and to minimize RAM usage. Queries can be minimized by
-  //  making the algorithm stateful and incremental. As the user types, only
-  //  the last character is needed to take another step along the trie. Total
-  //  input changes are the rarer, more expensive case, and we might even
-  //  consider skipping them since fuzzy matching somewhat assumes human errors
-  //  generated while typing, not copy/pasting, etc.
+  // This is the trie facilitating search for input alternatives.
   fuzzy::Node root_;
 
   // This provides a thread-safe way to check that loading has completed.
diff --git a/components/omnibox/browser/history_fuzzy_provider_unittest.cc b/components/omnibox/browser/history_fuzzy_provider_unittest.cc
index 1f7a0696..e20b270 100644
--- a/components/omnibox/browser/history_fuzzy_provider_unittest.cc
+++ b/components/omnibox/browser/history_fuzzy_provider_unittest.cc
@@ -480,18 +480,67 @@
 
 TEST_F(HistoryFuzzyProviderTest, NodesDeleteAndPreserveStructure) {
   fuzzy::Node node;
+  const auto checked_delete = [&](const std::u16string& s) {
+    node.Delete(s, 0);
+    return node.TerminalCount() == 0;
+  };
   node.Insert(u"abc", 0);
-  CHECK(node.Delete(u"abc", 0));
+  CHECK(checked_delete(u"abc"));
   CHECK(node.next.empty());
   node.Insert(u"def", 0);
-  CHECK(!node.Delete(u"de", 0));
+  CHECK(!checked_delete(u"de"));
   CHECK(!node.next.empty());
-  CHECK(node.Delete(u"def", 0));
+  CHECK(checked_delete(u"def"));
   node.Insert(u"ghi", 0);
   node.Insert(u"ghost", 0);
-  CHECK(!node.Delete(u"gh", 0));
+  CHECK(!checked_delete(u"gh"));
   CHECK(!node.next.empty());
-  CHECK(!node.Delete(u"ghi", 0));
-  CHECK(node.Delete(u"ghost", 0));
+  CHECK(!checked_delete(u"ghi"));
+  CHECK(checked_delete(u"ghost"));
   CHECK(node.next.empty());
 }
+
+TEST_F(HistoryFuzzyProviderTest, NodesMaintainRelevanceTotalTerminalCount) {
+  fuzzy::Node node;
+
+  // Start with no terminals.
+  CHECK_EQ(node.TerminalCount(), 0);
+
+  // Support including the empty string as terminal.
+  node.Insert(u"", 0);
+  CHECK_EQ(node.TerminalCount(), 1);
+
+  // Ensure repeated insertions don't cause growth.
+  node.Insert(u"abc", 0);
+  CHECK_EQ(node.TerminalCount(), 2);
+  node.Insert(u"abc", 0);
+  CHECK_EQ(node.TerminalCount(), 2);
+
+  // Check further additions on different, same, and partially same paths.
+  node.Insert(u"def", 0);
+  CHECK_EQ(node.TerminalCount(), 3);
+  node.Insert(u"abcd", 0);
+  CHECK_EQ(node.TerminalCount(), 4);
+  node.Insert(u"ab", 0);
+  CHECK_EQ(node.next[u'a']->TerminalCount(), 3);
+  CHECK_EQ(node.next[u'd']->TerminalCount(), 1);
+  CHECK_EQ(node.TerminalCount(), 5);
+
+  // Check deletion, including no-op and empty string deletion.
+  node.Delete(u"a", 0);
+  CHECK_EQ(node.TerminalCount(), 5);
+  node.Delete(u"x", 0);
+  CHECK_EQ(node.TerminalCount(), 5);
+  node.Delete(u"", 0);
+  CHECK_EQ(node.TerminalCount(), 4);
+  node.Delete(u"abc", 0);
+  CHECK_EQ(node.TerminalCount(), 3);
+  node.Delete(u"abcd", 0);
+  CHECK_EQ(node.TerminalCount(), 2);
+  node.Delete(u"ab", 0);
+  CHECK_EQ(node.TerminalCount(), 1);
+  node.Delete(u"defx", 0);
+  CHECK_EQ(node.TerminalCount(), 1);
+  node.Delete(u"def", 0);
+  CHECK_EQ(node.TerminalCount(), 0);
+}
diff --git a/components/prefs/pref_value_map_unittest.cc b/components/prefs/pref_value_map_unittest.cc
index 234a76c..b4cfe13c 100644
--- a/components/prefs/pref_value_map_unittest.cc
+++ b/components/prefs/pref_value_map_unittest.cc
@@ -21,7 +21,7 @@
   EXPECT_TRUE(map.SetValue("key", Value("hi mom!")));
 
   EXPECT_TRUE(map.GetValue("key", &result));
-  EXPECT_TRUE(Value("hi mom!").Equals(result));
+  EXPECT_EQ("hi mom!", *result);
 }
 
 TEST(PrefValueMapTest, GetAndSetIntegerValue) {
diff --git a/components/services/unzip/public/cpp/unzip.cc b/components/services/unzip/public/cpp/unzip.cc
index 90e7536..b08792a 100644
--- a/components/services/unzip/public/cpp/unzip.cc
+++ b/components/services/unzip/public/cpp/unzip.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/task/bind_post_task.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
@@ -69,6 +70,9 @@
     return &listener_receiver_;
   }
 
+  // Set when the task runner completes cancellation work.
+  base::AtomicFlag cancel_is_done;
+
  private:
   friend class base::RefCounted<UnzipParams>;
 
@@ -157,6 +161,44 @@
   GetExtractedInfoCallback callback_;
 };
 
+bool CheckZipFileValid(const base::FilePath& zip_path,
+                       const base::File& zip_file) {
+  if (!zip_file.IsValid()) {
+    LOG(ERROR) << "Cannot open ZIP archive " << Redact(zip_path) << ": "
+               << base::File::ErrorToString(zip_file.error_details());
+    return false;
+  }
+  return true;
+}
+
+void PrepareUnzipParams(
+    scoped_refptr<UnzipParams> unzip_params,
+    const base::FilePath& output_dir,
+    UnzipFilterCallback filter_callback,
+    UnzipListenerCallback listener_callback,
+    mojo::PendingRemote<filesystem::mojom::Directory>& directory_remote,
+    mojo::PendingRemote<unzip::mojom::UnzipFilter>& filter_remote,
+    mojo::PendingRemote<unzip::mojom::UnzipListener>& listener_remote) {
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<filesystem::DirectoryImpl>(output_dir, nullptr, nullptr),
+      directory_remote.InitWithNewPipeAndPassReceiver());
+
+  unzip_params->unzipper().set_disconnect_handler(
+      base::BindOnce(&UnzipParams::InvokeCallback, unzip_params, false));
+
+  if (filter_callback) {
+    unzip_params->SetFilter(filter_remote.InitWithNewPipeAndPassReceiver(),
+                            std::move(filter_callback));
+  }
+
+  if (listener_callback) {
+    mojo::Receiver<unzip::mojom::UnzipListener>* listener =
+        unzip_params->GetListenerReceiver();
+    unzip_params->SetListener(std::move(listener_callback));
+    listener_remote = listener->BindNewPipeAndPassRemote();
+  }
+}
+
 void DoUnzip(mojo::PendingRemote<mojom::Unzipper> unzipper,
              const base::FilePath& zip_path,
              const base::FilePath& output_dir,
@@ -165,40 +207,23 @@
              mojom::UnzipOptionsPtr options,
              UnzipCallback result_callback) {
   base::File zip_file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
-  if (!zip_file.IsValid()) {
-    LOG(ERROR) << "Cannot open ZIP archive " << Redact(zip_path) << ": "
-               << base::File::ErrorToString(zip_file.error_details());
+  if (!CheckZipFileValid(zip_path, zip_file)) {
     std::move(result_callback).Run(false);
     return;
   }
 
-  mojo::PendingRemote<filesystem::mojom::Directory> directory_remote;
-  mojo::MakeSelfOwnedReceiver(
-      std::make_unique<filesystem::DirectoryImpl>(output_dir, nullptr, nullptr),
-      directory_remote.InitWithNewPipeAndPassReceiver());
-
   // |result_callback| is shared between the connection error handler and the
   // Unzip call using a refcounted UnzipParams object that owns
   // |result_callback|.
   auto unzip_params = base::MakeRefCounted<UnzipParams>(
       std::move(unzipper), std::move(result_callback));
 
-  unzip_params->unzipper().set_disconnect_handler(
-      base::BindOnce(&UnzipParams::InvokeCallback, unzip_params, false));
-
+  mojo::PendingRemote<filesystem::mojom::Directory> directory_remote;
   mojo::PendingRemote<unzip::mojom::UnzipFilter> filter_remote;
-  if (filter_callback) {
-    unzip_params->SetFilter(filter_remote.InitWithNewPipeAndPassReceiver(),
-                            std::move(filter_callback));
-  }
-
   mojo::PendingRemote<unzip::mojom::UnzipListener> listener_remote;
-  if (listener_callback) {
-    mojo::Receiver<unzip::mojom::UnzipListener>* listener =
-        unzip_params->GetListenerReceiver();
-    unzip_params->SetListener(std::move(listener_callback));
-    listener_remote = listener->BindNewPipeAndPassRemote();
-  }
+  PrepareUnzipParams(unzip_params, output_dir, filter_callback,
+                     listener_callback, directory_remote, filter_remote,
+                     listener_remote);
 
   unzip_params->unzipper()->Unzip(
       std::move(zip_file), std::move(directory_remote), std::move(options),
@@ -338,4 +363,91 @@
                                         std::move(result_callback))));
 }
 
+ZipFileUnpacker::ZipFileUnpacker() = default;
+
+ZipFileUnpacker::~ZipFileUnpacker() = default;
+
+void DoUnzipWithParams(mojo::PendingRemote<mojom::Unzipper> unzipper,
+                       scoped_refptr<UnzipParams>& unzip_params,
+                       const base::FilePath& zip_path,
+                       const base::FilePath& output_dir,
+                       UnzipFilterCallback filter_callback,
+                       UnzipListenerCallback listener_callback,
+                       mojom::UnzipOptionsPtr options,
+                       UnzipCallback result_callback) {
+  base::File zip_file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!CheckZipFileValid(zip_path, zip_file)) {
+    std::move(result_callback).Run(false);
+    return;
+  }
+
+  // |result_callback| is shared between the connection error handler and the
+  // Unzip call using a refcounted UnzipParams object that owns
+  // |result_callback|.
+  unzip_params = base::MakeRefCounted<UnzipParams>(std::move(unzipper),
+                                                   std::move(result_callback));
+
+  mojo::PendingRemote<filesystem::mojom::Directory> directory_remote;
+  mojo::PendingRemote<unzip::mojom::UnzipFilter> filter_remote;
+  mojo::PendingRemote<unzip::mojom::UnzipListener> listener_remote;
+  PrepareUnzipParams(unzip_params, output_dir, filter_callback,
+                     listener_callback, directory_remote, filter_remote,
+                     listener_remote);
+
+  unzip_params->unzipper()->Unzip(
+      std::move(zip_file), std::move(directory_remote), std::move(options),
+      std::move(filter_remote), std::move(listener_remote),
+      base::BindOnce(&UnzipParams::InvokeCallback, unzip_params));
+}
+
+void ZipFileUnpacker::Unpack(mojo::PendingRemote<mojom::Unzipper> unzipper,
+                             const base::FilePath& zip_file,
+                             const base::FilePath& output_dir,
+                             mojom::UnzipOptionsPtr options,
+                             UnzipListenerCallback listener_callback,
+                             UnzipCallback result_callback) {
+  DCHECK(!result_callback.is_null());
+
+  runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&::unzip::DoUnzipWithParams, std::move(unzipper),
+                     std::ref(params_), zip_file, output_dir,
+                     UnzipFilterCallback(),
+                     base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
+                                        std::move(listener_callback)),
+                     std::move(options),
+                     base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
+                                        std::move(result_callback))));
+}
+
+void ReleaseParams(scoped_refptr<UnzipParams>& unzip_params) {
+  if (unzip_params) {
+    unzip_params.reset();
+  }
+}
+
+void EndUnpack(scoped_refptr<UnzipParams>& unzip_params) {
+  if (unzip_params) {
+    unzip_params->InvokeCallback(false);
+    unzip_params->cancel_is_done.Set();
+    ReleaseParams(unzip_params);
+  }
+}
+
+void ZipFileUnpacker::Stop() {
+  runner_->PostTask(FROM_HERE, base::BindOnce(&EndUnpack, std::ref(params_)));
+}
+
+bool ZipFileUnpacker::CancelDone() {
+  if (params_) {
+    return params_->cancel_is_done.IsSet();
+  }
+  return true;
+}
+
+void ZipFileUnpacker::CleanUp() {
+  runner_->PostTask(FROM_HERE,
+                    base::BindOnce(&ReleaseParams, std::ref(params_)));
+}
+
 }  // namespace unzip
diff --git a/components/services/unzip/public/cpp/unzip.h b/components/services/unzip/public/cpp/unzip.h
index ead7290..b455fe4 100644
--- a/components/services/unzip/public/cpp/unzip.h
+++ b/components/services/unzip/public/cpp/unzip.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_SERVICES_UNZIP_PUBLIC_CPP_UNZIP_H_
 
 #include "base/callback_forward.h"
+#include "base/task/thread_pool.h"
 #include "components/services/unzip/public/mojom/unzipper.mojom.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/ced/src/util/encodings/encodings.h"
@@ -51,6 +52,45 @@
                       const base::FilePath& zip_file,
                       GetExtractedInfoCallback result_callback);
 
+namespace {
+class UnzipParams;
+}
+
+// Class that wraps the unzip service to manage the lifetime of its
+// mojo conncections to enable cancellation, etc.
+class ZipFileUnpacker : public base::RefCountedThreadSafe<ZipFileUnpacker> {
+ public:
+  ZipFileUnpacker();
+  void Unpack(mojo::PendingRemote<mojom::Unzipper> unzipper,
+              const base::FilePath& zip_file,
+              const base::FilePath& output_dir,
+              mojom::UnzipOptionsPtr options,
+              UnzipListenerCallback listener_callback,
+              UnzipCallback result_callback);
+
+  void Stop();
+
+  bool CancelDone();
+
+  void CleanUp();
+
+ private:
+  friend class base::RefCountedThreadSafe<ZipFileUnpacker>;
+
+  ~ZipFileUnpacker();
+
+  const scoped_refptr<base::SequencedTaskRunner> runner_ =
+      base::ThreadPool::CreateSequencedTaskRunner(
+          {base::TaskPriority::USER_VISIBLE, base::MayBlock(),
+           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+
+  base::File zip_file_;
+  scoped_refptr<UnzipParams> params_;
+  mojo::PendingRemote<filesystem::mojom::Directory> directory_remote_;
+  mojo::PendingRemote<unzip::mojom::UnzipFilter> filter_remote_;
+  mojo::PendingRemote<unzip::mojom::UnzipListener> listener_remote_;
+};
+
 }  // namespace unzip
 
 #endif  // COMPONENTS_SERVICES_UNZIP_PUBLIC_CPP_UNZIP_H_
diff --git a/components/services/unzip/unzipper_impl.cc b/components/services/unzip/unzipper_impl.cc
index a0f5ba9..598931c 100644
--- a/components/services/unzip/unzipper_impl.cc
+++ b/components/services/unzip/unzipper_impl.cc
@@ -150,7 +150,10 @@
 UnzipperImpl::UnzipperImpl() = default;
 
 UnzipperImpl::UnzipperImpl(mojo::PendingReceiver<mojom::Unzipper> receiver)
-    : receiver_(this, std::move(receiver)) {}
+    : receiver_(this, std::move(receiver)) {
+  receiver_.set_disconnect_handler(base::BindOnce(
+      &UnzipperImpl::OnReceiverDisconnect, weak_ptr_factory_.GetWeakPtr()));
+}
 
 UnzipperImpl::~UnzipperImpl() = default;
 
@@ -202,15 +205,13 @@
        .password = std::move(password)});
 }
 
-void UnzipperImpl::Unzip(
+bool RunUnzip(
     base::File zip_file,
     mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
-    mojom::UnzipOptionsPtr set_options,
+    std::string encoding_name,
+    std::string password,
     mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
-    mojo::PendingRemote<mojom::UnzipListener> listener_remote,
-    UnzipCallback callback) {
-  DCHECK(zip_file.IsValid());
-
+    mojo::PendingRemote<mojom::UnzipListener> listener_remote) {
   mojo::Remote<filesystem::mojom::Directory> output_dir(
       std::move(output_dir_remote));
 
@@ -226,6 +227,24 @@
     progress_cb =
         base::BindRepeating(&UnzipperImpl::Listener, std::move(listener));
   }
+  return zip::Unzip(
+      zip_file.GetPlatformFile(),
+      base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
+      base::BindRepeating(&CreateDirectory, output_dir.get()),
+      {.encoding = std::move(encoding_name),
+       .filter = std::move(filter_cb),
+       .progress = std::move(progress_cb),
+       .password = std::move(password)});
+}
+
+void UnzipperImpl::Unzip(
+    base::File zip_file,
+    mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
+    mojom::UnzipOptionsPtr set_options,
+    mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
+    mojo::PendingRemote<mojom::UnzipListener> listener_remote,
+    UnzipCallback callback) {
+  DCHECK(zip_file.IsValid());
 
   std::string encoding_name;
   if (set_options->encoding == "auto") {
@@ -237,11 +256,12 @@
     encoding_name = set_options->encoding;
   }
 
-  base::SequencedTaskRunnerHandle::Get()->PostTaskAndReplyWithResult(
+  runner_->PostTaskAndReplyWithResult(
       FROM_HERE,
-      base::BindOnce(&DoUnzip, std::move(zip_file), std::move(output_dir),
-                     std::move(encoding_name), std::move(set_options->password),
-                     std::move(filter_cb), std::move(progress_cb)),
+      base::BindOnce(&RunUnzip, std::move(zip_file),
+                     std::move(output_dir_remote), std::move(encoding_name),
+                     std::move(set_options->password), std::move(filter_remote),
+                     std::move(listener_remote)),
       base::BindOnce(std::move(callback)));
 }
 
@@ -294,4 +314,9 @@
   std::move(callback).Run(std::move(info));
 }
 
+void UnzipperImpl::OnReceiverDisconnect() {
+  DCHECK(receiver_.is_bound());
+  receiver_.reset();
+}
+
 }  // namespace unzip
diff --git a/components/services/unzip/unzipper_impl.h b/components/services/unzip/unzipper_impl.h
index 89f132ad..2bd9541 100644
--- a/components/services/unzip/unzipper_impl.h
+++ b/components/services/unzip/unzipper_impl.h
@@ -6,6 +6,8 @@
 #define COMPONENTS_SERVICES_UNZIP_UNZIPPER_IMPL_H_
 
 #include "base/files/file.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/thread_pool.h"
 #include "components/services/unzip/public/mojom/unzipper.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -26,6 +28,9 @@
   UnzipperImpl(const UnzipperImpl&) = delete;
   UnzipperImpl& operator=(const UnzipperImpl&) = delete;
 
+  static void Listener(const mojo::Remote<mojom::UnzipListener>& listener,
+                       uint64_t bytes);
+
   ~UnzipperImpl() override;
 
  private:
@@ -44,10 +49,19 @@
   void GetExtractedInfo(base::File zip_file,
                         GetExtractedInfoCallback callback) override;
 
-  static void Listener(const mojo::Remote<mojom::UnzipListener>& listener,
-                       uint64_t bytes);
+  // Disconnect handler for the receiver.
+  void OnReceiverDisconnect();
+
+  // Task runner for ZIP extraction.
+  using RunnerPtr = scoped_refptr<base::SequencedTaskRunner>;
+  const RunnerPtr runner_ = base::ThreadPool::CreateSequencedTaskRunner(
+      {base::MayBlock(), base::WithBaseSyncPrimitives(),
+       base::TaskPriority::USER_BLOCKING,
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
 
   mojo::Receiver<mojom::Unzipper> receiver_{this};
+
+  base::WeakPtrFactory<UnzipperImpl> weak_ptr_factory_{this};
 };
 
 }  // namespace unzip
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 4fe2de2..1680c7b5 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -693,8 +693,8 @@
     "code_cache/generated_code_cache.h",
     "code_cache/generated_code_cache_context.cc",
     "code_cache/generated_code_cache_context.h",
-    "code_cache/simple_lru_cache_index.cc",
-    "code_cache/simple_lru_cache_index.h",
+    "code_cache/simple_lru_cache.cc",
+    "code_cache/simple_lru_cache.h",
     "compositor/surface_utils.cc",
     "compositor/surface_utils.h",
     "compute_pressure/compute_pressure_host.cc",
@@ -1412,6 +1412,8 @@
     "preloading/prefetch/prefetch_url_loader_interceptor.h",
     "preloading/prefetch/prefetched_mainframe_response_container.cc",
     "preloading/prefetch/prefetched_mainframe_response_container.h",
+    "preloading/preloading.cc",
+    "preloading/preloading.h",
     "preloading/preloading_attempt_impl.cc",
     "preloading/preloading_attempt_impl.h",
     "preloading/preloading_data_impl.cc",
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index 87df1e4..563da7d 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -309,15 +309,8 @@
   // Get expectation lines from expectation file if any.
   base::FilePath expected_file =
       test_helper_.GetExpectationFilePath(file_path, expectations_qualifier);
-  if (!expected_file.empty()) {
+  if (!expected_file.empty())
     expected_lines = test_helper_.LoadExpectationFile(expected_file);
-  } else if (GetParam() == ui::AXApiType::kWinUIA) {
-    // TODO: UIA is not yet supported, see crbug.com/1327652, crbug.com/1329523,
-    // crbug.com/1329847.
-    LOG(INFO) << "No expectation file present, ignoring test on this "
-                 "platform.";
-    return;
-  }
 
   // Get the test URL.
   GURL url(embedded_test_server()->GetURL(
diff --git a/content/browser/android/navigation_handle_proxy.cc b/content/browser/android/navigation_handle_proxy.cc
index e644be9..1c77b0e 100644
--- a/content/browser/android/navigation_handle_proxy.cc
+++ b/content/browser/android/navigation_handle_proxy.cc
@@ -50,7 +50,8 @@
   JNIEnv* env = AttachCurrentThread();
   Java_NavigationHandle_didRedirect(
       env, java_navigation_handle_,
-      url::GURLAndroid::FromNativeGURL(env, cpp_navigation_handle_->GetURL()));
+      url::GURLAndroid::FromNativeGURL(env, cpp_navigation_handle_->GetURL()),
+      cpp_navigation_handle_->IsExternalProtocol());
 }
 
 void NavigationHandleProxy::DidFinish() {
@@ -94,7 +95,8 @@
       // crbug/690041.
       cpp_navigation_handle_->GetResponseHeaders()
           ? cpp_navigation_handle_->GetResponseHeaders()->response_code()
-          : 200);
+          : 200,
+      cpp_navigation_handle_->IsExternalProtocol());
 }
 
 NavigationHandleProxy::~NavigationHandleProxy() {
diff --git a/content/browser/code_cache/generated_code_cache.cc b/content/browser/code_cache/generated_code_cache.cc
index b549349..ae0d9dca 100644
--- a/content/browser/code_cache/generated_code_cache.cc
+++ b/content/browser/code_cache/generated_code_cache.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "components/services/storage/public/cpp/big_io_buffer.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/url_constants.h"
 #include "crypto/sha2.h"
 #include "net/base/completion_once_callback.h"
@@ -200,7 +201,7 @@
   DCHECK_EQ(cache_type_, CodeCacheType::kJavaScript);
   base::UmaHistogramCustomCounts(
       "SiteIsolatedCodeCache.JS.PotentialMemoryBackedCodeCacheSize2",
-      lru_cache_index_.GetSize(),
+      lru_cache_.GetSize(),
       /*min=*/0,
       /*exclusive_max=*/kLruCacheCapacity,
       /*buckets=*/50);
@@ -293,20 +294,22 @@
                        mojo_base::BigBuffer data) {
     if (code_cache->cache_type_ == CodeCacheType::kJavaScript) {
       const bool code_cache_hit = data.size() > 0;
-      const bool hypothetical_in_memory_code_cache_hit =
-          code_cache->lru_cache_index_.Get(key_);
-      if (code_cache_hit && !hypothetical_in_memory_code_cache_hit) {
-        code_cache->lru_cache_index_.Put(key_, data.size());
+      const bool in_memory_code_cache_hit = code_cache->lru_cache_.Has(key_);
+      if (code_cache_hit && !in_memory_code_cache_hit) {
+        code_cache->lru_cache_.Put(key_, response_time, base::make_span(data));
       }
-      if (code_cache_hit && hypothetical_in_memory_code_cache_hit) {
-        base::UmaHistogramTimes(
-            "SiteIsolatedCodeCache.JS.MemoryBackedCodeCachePotentialImpact",
-            base::TimeTicks::Now() - start_time_);
+      if (!base::FeatureList::IsEnabled(features::kInMemoryCodeCache)) {
+        if (code_cache_hit && in_memory_code_cache_hit) {
+          base::UmaHistogramTimes(
+              "SiteIsolatedCodeCache.JS.MemoryBackedCodeCachePotentialImpact",
+              base::TimeTicks::Now() - start_time_);
+        }
+        base::UmaHistogramBoolean("SiteIsolatedCodeCache.JS.Hit",
+                                  code_cache_hit);
+        base::UmaHistogramBoolean(
+            "SiteIsolatedCodeCache.JS.PotentialMemoryBackedCodeCacheHit",
+            in_memory_code_cache_hit);
       }
-      base::UmaHistogramBoolean("SiteIsolatedCodeCache.JS.Hit", code_cache_hit);
-      base::UmaHistogramBoolean(
-          "SiteIsolatedCodeCache.JS.PotentialMemoryBackedCodeCacheHit",
-          hypothetical_in_memory_code_cache_hit);
     }
     std::move(read_callback_).Run(response_time, std::move(data));
   }
@@ -410,9 +413,14 @@
   if (data.size() >= std::numeric_limits<int32_t>::max())
     return;
 
+  const std::string key = GetCacheKey(url, origin_lock, nik, cache_type_);
+  if (cache_type_ == CodeCacheType::kJavaScript) {
+    lru_cache_.Put(key, response_time, base::make_span(data));
+  }
+
   scoped_refptr<net::IOBufferWithSize> small_buffer;
   scoped_refptr<BigIOBuffer> large_buffer;
-  uint32_t data_size = static_cast<uint32_t>(data.size());
+  const uint32_t data_size = static_cast<uint32_t>(data.size());
   // We have three different cache entry layouts, depending on data size.
   if (data_size <= kInlineDataLimit) {
     // 1. Inline
@@ -472,11 +480,9 @@
   WriteCommonDataHeader(small_buffer, response_time, data_size);
 
   // Create the write operation.
-  std::string key = GetCacheKey(url, origin_lock, nik, cache_type_);
   auto op = std::make_unique<PendingOperation>(Operation::kWrite, key,
                                                small_buffer, large_buffer);
   EnqueueOperation(std::move(op));
-  lru_cache_index_.Put(key, data_size);
 }
 
 void GeneratedCodeCache::FetchEntry(const GURL& url,
@@ -509,7 +515,7 @@
   auto op = std::make_unique<PendingOperation>(Operation::kDelete, key);
   EnqueueOperation(std::move(op));
 
-  lru_cache_index_.Delete(key);
+  lru_cache_.Delete(key);
 }
 
 void GeneratedCodeCache::CreateBackend() {
@@ -696,6 +702,14 @@
 void GeneratedCodeCache::FetchEntryImpl(PendingOperation* op) {
   DCHECK(Operation::kFetch == op->operation() ||
          Operation::kFetchWithSHAKey == op->operation());
+  if (base::FeatureList::IsEnabled(features::kInMemoryCodeCache)) {
+    if (auto result = lru_cache_.Get(op->key())) {
+      op->RunReadCallback(this, result->response_time, std::move(result->data));
+      CloseOperationAndIssueNext(op);
+      return;
+    }
+  }
+
   if (backend_state_ != kInitialized) {
     op->RunReadCallback(this, base::Time(), mojo_base::BigBuffer());
     CloseOperationAndIssueNext(op);
diff --git a/content/browser/code_cache/generated_code_cache.h b/content/browser/code_cache/generated_code_cache.h
index 495a03b..b44895d3 100644
--- a/content/browser/code_cache/generated_code_cache.h
+++ b/content/browser/code_cache/generated_code_cache.h
@@ -12,7 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
-#include "content/browser/code_cache/simple_lru_cache_index.h"
+#include "content/browser/code_cache/simple_lru_cache.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "net/base/io_buffer.h"
@@ -235,7 +235,7 @@
   CodeCacheType cache_type_;
 
   // A hypothetical memory-backed code cache. Used to collect UMAs.
-  SimpleLruCacheIndex lru_cache_index_{kLruCacheCapacity};
+  SimpleLruCache lru_cache_{kLruCacheCapacity};
   base::RepeatingTimer histograms_timer_;
   static const int64_t kLruCacheCapacity = 50 * 1024 * 1024;
 
diff --git a/content/browser/code_cache/simple_lru_cache.cc b/content/browser/code_cache/simple_lru_cache.cc
new file mode 100644
index 0000000..295a53c
--- /dev/null
+++ b/content/browser/code_cache/simple_lru_cache.cc
@@ -0,0 +1,133 @@
+// 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 "content/browser/code_cache/simple_lru_cache.h"
+
+#include <limits>
+
+#include "base/feature_list.h"
+#include "base/numerics/clamped_math.h"
+#include "content/public/common/content_features.h"
+#include "net/base/url_util.h"
+
+namespace content {
+
+using GetResult = SimpleLruCache::GetResult;
+
+GetResult::GetResult(base::Time response_time, mojo_base::BigBuffer data)
+    : response_time(response_time), data(std::move(data)) {}
+GetResult::~GetResult() = default;
+
+GetResult::GetResult(GetResult&&) = default;
+GetResult& GetResult::operator=(GetResult&&) = default;
+
+SimpleLruCache::Value::Value(Age age, base::Time response_time, uint32_t size)
+    : age(age), response_time(response_time), size(size) {
+  DCHECK(!base::FeatureList::IsEnabled(features::kInMemoryCodeCache));
+}
+
+SimpleLruCache::Value::Value(Age age,
+                             base::Time response_time,
+                             uint32_t size,
+                             base::span<const uint8_t> data)
+    : age(age),
+      response_time(response_time),
+      size(size),
+      data(data.begin(), data.end()) {
+  DCHECK(base::FeatureList::IsEnabled(features::kInMemoryCodeCache));
+}
+
+SimpleLruCache::Value::~Value() = default;
+
+SimpleLruCache::Value::Value(Value&&) = default;
+SimpleLruCache::Value& SimpleLruCache::Value::operator=(Value&&) = default;
+
+SimpleLruCache::SimpleLruCache(uint64_t capacity) : capacity_(capacity) {}
+SimpleLruCache::~SimpleLruCache() = default;
+
+absl::optional<GetResult> SimpleLruCache::Get(const std::string& key) {
+  base::Time response_time;
+  mojo_base::BigBuffer data;
+  if (!GetInternal(key, &response_time, &data)) {
+    return absl::nullopt;
+  }
+  return absl::make_optional<GetResult>(response_time, std::move(data));
+}
+
+bool SimpleLruCache::Has(const std::string& key) {
+  return GetInternal(key, /*response_time=*/nullptr, /*data=*/nullptr);
+}
+
+void SimpleLruCache::Put(const std::string& key,
+                         base::Time response_time,
+                         base::span<const uint8_t> payload) {
+  Delete(key);
+
+  const uint64_t size = base::ClampedNumeric<uint64_t>(key.size()) +
+                        payload.size() + kEmptyEntrySize;
+
+  if (size > capacity_) {
+    // Ignore a too big entry.
+    return;
+  }
+
+  const Age age = GetNextAge();
+  if (base::FeatureList::IsEnabled(features::kInMemoryCodeCache)) {
+    entries_.emplace(key, Value(age, response_time, size, payload));
+  } else {
+    entries_.emplace(key, Value(age, response_time, size));
+  }
+  access_list_.emplace(age, std::move(key));
+  size_ += size;
+  Evict();
+}
+
+void SimpleLruCache::Delete(const std::string& key) {
+  const auto it = entries_.find(key);
+  if (it == entries_.end()) {
+    return;
+  }
+
+  DCHECK_GE(size_, it->second.size);
+  size_ -= it->second.size;
+  access_list_.erase(it->second.age);
+  entries_.erase(it);
+}
+
+uint64_t SimpleLruCache::GetSize() const {
+  return size_;
+}
+
+bool SimpleLruCache::GetInternal(const std::string& key,
+                                 base::Time* response_time,
+                                 mojo_base::BigBuffer* data) {
+  const auto it = entries_.find(key);
+  if (it == entries_.end()) {
+    return false;
+  }
+  const Age age = GetNextAge();
+  access_list_.erase(it->second.age);
+  it->second.age = age;
+  access_list_.emplace(age, it->first);
+
+  if (response_time) {
+    *response_time = it->second.response_time;
+  }
+  if (data && base::FeatureList::IsEnabled(features::kInMemoryCodeCache)) {
+    *data = mojo_base::BigBuffer(it->second.data);
+  }
+  return true;
+}
+
+void SimpleLruCache::Evict() {
+  while (capacity_ < size_) {
+    auto it = access_list_.begin();
+    DCHECK(it != access_list_.end());
+    DCHECK(entries_.find(it->second) != entries_.end());
+
+    Delete(it->second);
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/code_cache/simple_lru_cache.h b/content/browser/code_cache/simple_lru_cache.h
new file mode 100644
index 0000000..86e3808
--- /dev/null
+++ b/content/browser/code_cache/simple_lru_cache.h
@@ -0,0 +1,105 @@
+// 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 CONTENT_BROWSER_CODE_CACHE_SIMPLE_LRU_CACHE_H_
+#define CONTENT_BROWSER_CODE_CACHE_SIMPLE_LRU_CACHE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/containers/span.h"
+#include "base/time/time.h"
+#include "base/types/strong_alias.h"
+#include "content/common/content_export.h"
+#include "mojo/public/cpp/base/big_buffer.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace content {
+
+// A simple LRU cache, to measure the potential performance impact of
+// memory-backed code cache.
+class CONTENT_EXPORT SimpleLruCache {
+ public:
+  explicit SimpleLruCache(uint64_t capacity);
+  ~SimpleLruCache();
+
+  SimpleLruCache(const SimpleLruCache&) = delete;
+  SimpleLruCache& operator=(const SimpleLruCache&) = delete;
+
+  struct CONTENT_EXPORT GetResult {
+    GetResult(base::Time response_time, mojo_base::BigBuffer data);
+    ~GetResult();
+
+    GetResult(const GetResult&) = delete;
+    GetResult& operator=(const GetResult&) = delete;
+
+    GetResult(GetResult&&);
+    GetResult& operator=(GetResult&&);
+
+    base::Time response_time;
+    mojo_base::BigBuffer data;
+  };
+
+  // Returns the contents of the entry for `key`, if any. The `data` member of
+  // GetResult is filled only when features::kInMemoryCodeCache is enabled.
+  // This updates the entry access time.
+  absl::optional<GetResult> Get(const std::string& key);
+  // Returns whether there is an entry for `key`. This updates the entry access
+  // time.
+  bool Has(const std::string& key);
+  // Puts an entry.
+  void Put(const std::string& key,
+           base::Time response_time,
+           base::span<const uint8_t> data);
+  // Deletes an entry for `key` in the cache. If there is no such an entry, this
+  // does nothing.
+  void Delete(const std::string& key);
+  // Returns the total size of the cache.
+  uint64_t GetSize() const;
+
+  static constexpr uint32_t kEmptyEntrySize = 1024;
+
+ private:
+  using Age = base::StrongAlias<class AgeTag, uint32_t>;
+  using Key = std::string;
+  struct Value final {
+    Value(Age age, base::Time response_time, uint32_t size);
+    Value(Age age,
+          base::Time response_time,
+          uint32_t size,
+          base::span<const uint8_t> data);
+    ~Value();
+
+    Value(const Value&) = delete;
+    Value& operator=(const Value&) = delete;
+    Value(Value&&);
+    Value& operator=(Value&&);
+
+    Age age;
+    base::Time response_time;
+    uint32_t size;
+    // This is used when features::kInMemoryCodeCache is enabled.
+    std::vector<uint8_t> data;
+  };
+
+  bool GetInternal(const std::string& key,
+                   base::Time* response_time,
+                   mojo_base::BigBuffer* data);
+
+  Age GetNextAge() { return Age(age_source_++); }
+  void Evict();
+
+  const uint64_t capacity_;
+  std::map<Key, Value> entries_;
+  std::map<Age, Key> access_list_;
+  uint32_t age_source_ = 0;
+  uint64_t size_ = 0;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_CODE_CACHE_SIMPLE_LRU_CACHE_H_
diff --git a/content/browser/code_cache/simple_lru_cache_index.cc b/content/browser/code_cache/simple_lru_cache_index.cc
deleted file mode 100644
index 24b04fd..0000000
--- a/content/browser/code_cache/simple_lru_cache_index.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// 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 "content/browser/code_cache/simple_lru_cache_index.h"
-
-#include <limits>
-
-#include "base/numerics/clamped_math.h"
-#include "net/base/url_util.h"
-
-namespace content {
-
-SimpleLruCacheIndex::SimpleLruCacheIndex(uint64_t capacity)
-    : capacity_(capacity) {}
-SimpleLruCacheIndex::~SimpleLruCacheIndex() = default;
-
-bool SimpleLruCacheIndex::Get(const std::string& key) {
-  const auto it = entries_.find(key);
-  if (it == entries_.end()) {
-    return false;
-  }
-  const Age age = GetNextAge();
-  access_list_.erase(it->second.age);
-  it->second.age = age;
-  access_list_.emplace(age, it->first);
-  return true;
-}
-
-void SimpleLruCacheIndex::Put(const std::string& key, uint32_t payload_size) {
-  Delete(key);
-
-  const uint64_t size = base::ClampedNumeric<uint64_t>(key.size()) +
-                        payload_size + kEmptyEntrySize;
-
-  if (size > capacity_) {
-    // Ignore a too big entry.
-    return;
-  }
-
-  const Age age = GetNextAge();
-  entries_.emplace(key, Value(age, size));
-  access_list_.emplace(age, std::move(key));
-  size_ += size;
-  Evict();
-}
-
-void SimpleLruCacheIndex::Delete(const std::string& key) {
-  const auto it = entries_.find(key);
-  if (it == entries_.end()) {
-    return;
-  }
-
-  DCHECK_GE(size_, it->second.size);
-  size_ -= it->second.size;
-  access_list_.erase(it->second.age);
-  entries_.erase(it);
-}
-
-uint64_t SimpleLruCacheIndex::GetSize() const {
-  return size_;
-}
-
-void SimpleLruCacheIndex::Evict() {
-  while (capacity_ < size_) {
-    auto it = access_list_.begin();
-    DCHECK(it != access_list_.end());
-    DCHECK(entries_.find(it->second) != entries_.end());
-
-    Delete(it->second);
-  }
-}
-
-}  // namespace content
diff --git a/content/browser/code_cache/simple_lru_cache_index.h b/content/browser/code_cache/simple_lru_cache_index.h
deleted file mode 100644
index 16415fd..0000000
--- a/content/browser/code_cache/simple_lru_cache_index.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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 CONTENT_BROWSER_CODE_CACHE_SIMPLE_LRU_CACHE_INDEX_H_
-#define CONTENT_BROWSER_CODE_CACHE_SIMPLE_LRU_CACHE_INDEX_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <string>
-
-#include "base/types/strong_alias.h"
-#include "content/common/content_export.h"
-
-namespace content {
-
-// A simple LRU cache index, to measure the potential performance impact of
-// memory-backed code cache.
-class CONTENT_EXPORT SimpleLruCacheIndex {
- public:
-  explicit SimpleLruCacheIndex(uint64_t capacity);
-  ~SimpleLruCacheIndex();
-
-  SimpleLruCacheIndex(const SimpleLruCacheIndex&) = delete;
-  SimpleLruCacheIndex& operator=(const SimpleLruCacheIndex&) = delete;
-
-  // Returns whether an entry for `key` exists in the cache.
-  bool Get(const std::string& key);
-  // Puts an entry whose payload size is `size` to the cache.
-  void Put(const std::string& key, uint32_t size);
-  // Deletes an entry for `key` in the cache. If there is no such an entry, this
-  // does nothing.
-  void Delete(const std::string& key);
-  // Returns the total size of the cache.
-  uint64_t GetSize() const;
-
-  static constexpr uint32_t kEmptyEntrySize = 1024;
-
- private:
-  using Age = base::StrongAlias<class AgeTag, uint32_t>;
-  using Key = std::string;
-  struct Value {
-    Value(Age age, uint32_t size) : age(age), size(size) {}
-
-    Age age;
-    uint32_t size;
-  };
-
-  Age GetNextAge() { return Age(age_source_++); }
-  void Evict();
-
-  const uint64_t capacity_;
-  std::map<Key, Value> entries_;
-  std::map<Age, Key> access_list_;
-  uint32_t age_source_ = 0;
-  uint64_t size_ = 0;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_CODE_CACHE_SIMPLE_LRU_CACHE_INDEX_H_
diff --git a/content/browser/code_cache/simple_lru_cache_index_unittest.cc b/content/browser/code_cache/simple_lru_cache_index_unittest.cc
deleted file mode 100644
index 11cb52b..0000000
--- a/content/browser/code_cache/simple_lru_cache_index_unittest.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-// 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 "content/browser/code_cache/simple_lru_cache_index.h"
-#include "net/base/schemeful_site.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-namespace {
-
-constexpr auto kEmptyEntrySize = SimpleLruCacheIndex::kEmptyEntrySize;
-
-TEST(SimpleLruCacheIndexTest, Empty) {
-  const std::string kKey = "hello";
-  SimpleLruCacheIndex cache(/*capacity=*/100 * 1024);
-
-  EXPECT_EQ(cache.GetSize(), 0u);
-  EXPECT_FALSE(cache.Get(kKey));
-}
-
-TEST(SimpleLruCacheIndexTest, PutAndGet) {
-  const std::string kKey1("key1");
-  const std::string kKey2("key2");
-  const std::string kKey3("key3");
-  const std::string kKey4("key4");
-
-  SimpleLruCacheIndex cache(/*capacity=*/100 * 1024);
-
-  EXPECT_EQ(cache.GetSize(), 0u);
-  EXPECT_FALSE(cache.Get(kKey1));
-  EXPECT_FALSE(cache.Get(kKey2));
-  EXPECT_FALSE(cache.Get(kKey3));
-  EXPECT_FALSE(cache.Get(kKey4));
-
-  cache.Put(kKey1, 1);
-  EXPECT_EQ(cache.GetSize(), 1 + 4 + kEmptyEntrySize);
-  EXPECT_TRUE(cache.Get(kKey1));
-  EXPECT_FALSE(cache.Get(kKey2));
-  EXPECT_FALSE(cache.Get(kKey3));
-  EXPECT_FALSE(cache.Get(kKey4));
-
-  cache.Put(kKey1, 2);
-  EXPECT_EQ(cache.GetSize(), 2 + 4 + kEmptyEntrySize);
-  EXPECT_TRUE(cache.Get(kKey1));
-  EXPECT_FALSE(cache.Get(kKey2));
-  EXPECT_FALSE(cache.Get(kKey3));
-  EXPECT_FALSE(cache.Get(kKey4));
-
-  cache.Put(kKey2, 3);
-  EXPECT_EQ(cache.GetSize(), 5 + 8 + 2 * kEmptyEntrySize);
-  EXPECT_TRUE(cache.Get(kKey1));
-  EXPECT_TRUE(cache.Get(kKey2));
-  EXPECT_FALSE(cache.Get(kKey3));
-  EXPECT_FALSE(cache.Get(kKey4));
-
-  cache.Put(kKey4, 4);
-  EXPECT_EQ(cache.GetSize(), 9 + 12 + 3 * kEmptyEntrySize);
-  EXPECT_TRUE(cache.Get(kKey1));
-  EXPECT_TRUE(cache.Get(kKey2));
-  EXPECT_FALSE(cache.Get(kKey3));
-  EXPECT_TRUE(cache.Get(kKey4));
-}
-
-TEST(SimpleLruCacheIndexTest, PutAndEvict) {
-  const std::string kKey("key1");
-
-  SimpleLruCacheIndex cache(/*capacity=*/kEmptyEntrySize + kKey.size() + 1);
-
-  EXPECT_EQ(cache.GetSize(), 0u);
-  EXPECT_FALSE(cache.Get(kKey));
-
-  // This entry is immediately evicted because the size excceeds the capacity.
-  cache.Put(kKey, 2);
-  EXPECT_EQ(cache.GetSize(), 0u);
-  EXPECT_FALSE(cache.Get(kKey));
-
-  // This entry stays.
-  cache.Put(kKey, 1);
-  EXPECT_EQ(cache.GetSize(), 1 + kKey.size() + kEmptyEntrySize);
-  EXPECT_TRUE(cache.Get(kKey));
-
-  // An updated entry can also be evicted.
-  cache.Put(kKey, 2);
-  EXPECT_EQ(cache.GetSize(), 0u);
-  EXPECT_FALSE(cache.Get(kKey));
-}
-
-TEST(SimpleLruCacheIndexTest, LRU) {
-  const std::string kKey1("key1");
-  const std::string kKey2("key2");
-  const std::string kKey3("key3");
-  const std::string kKey4("key4");
-
-  SimpleLruCacheIndex cache(kEmptyEntrySize * 2 + 8);
-
-  cache.Put(kKey1, 0);
-  cache.Put(kKey2, 0);
-  cache.Put(kKey3, 0);
-  cache.Put(kKey4, 0);
-
-  // The last two entries are kept.
-  EXPECT_EQ(cache.GetSize(), 2 * kEmptyEntrySize + 8);
-  EXPECT_FALSE(cache.Get(kKey1));
-  EXPECT_FALSE(cache.Get(kKey2));
-  EXPECT_TRUE(cache.Get(kKey3));
-  EXPECT_TRUE(cache.Get(kKey4));
-}
-
-TEST(SimpleLruCacheIndexTest, LRUAndGet) {
-  const std::string kKey1("key1");
-  const std::string kKey2("key2");
-  const std::string kKey3("key3");
-  const std::string kKey4("key4");
-
-  SimpleLruCacheIndex cache(kEmptyEntrySize * 2 + 8);
-
-  cache.Put(kKey1, 0);
-  cache.Put(kKey2, 0);
-  // This call updates the access time.
-  EXPECT_TRUE(cache.Get(kKey1));
-  cache.Put(kKey3, 0);
-
-  EXPECT_EQ(cache.GetSize(), 2 * kEmptyEntrySize + 8);
-  EXPECT_TRUE(cache.Get(kKey1));
-  EXPECT_FALSE(cache.Get(kKey2));
-  EXPECT_TRUE(cache.Get(kKey3));
-  EXPECT_FALSE(cache.Get(kKey4));
-}
-
-TEST(SimpleLruCacheIndexTest, Delete) {
-  const std::string kKey1("key1");
-  const std::string kKey2("key2");
-  const std::string kKey3("key3");
-  const std::string kKey4("key4");
-
-  SimpleLruCacheIndex cache(/*capacity=*/1024 * 1024);
-
-  cache.Put(kKey1, 1);
-  cache.Put(kKey2, 2);
-  cache.Put(kKey3, 3);
-  cache.Put(kKey4, 4);
-
-  EXPECT_EQ(cache.GetSize(), 4 * kEmptyEntrySize + 16 + 10);
-  EXPECT_TRUE(cache.Get(kKey1));
-  EXPECT_TRUE(cache.Get(kKey2));
-  EXPECT_TRUE(cache.Get(kKey3));
-  EXPECT_TRUE(cache.Get(kKey4));
-
-  cache.Delete(kKey2);
-  EXPECT_EQ(cache.GetSize(), 3 * kEmptyEntrySize + 12 + 8);
-  EXPECT_TRUE(cache.Get(kKey1));
-  EXPECT_FALSE(cache.Get(kKey2));
-  EXPECT_TRUE(cache.Get(kKey3));
-  EXPECT_TRUE(cache.Get(kKey4));
-
-  cache.Delete(kKey2);
-  EXPECT_EQ(cache.GetSize(), 3 * kEmptyEntrySize + 12 + 8);
-  EXPECT_TRUE(cache.Get(kKey1));
-  EXPECT_FALSE(cache.Get(kKey2));
-  EXPECT_TRUE(cache.Get(kKey3));
-  EXPECT_TRUE(cache.Get(kKey4));
-
-  cache.Delete(kKey1);
-  cache.Delete(kKey2);
-  cache.Delete(kKey3);
-  cache.Delete(kKey4);
-  EXPECT_EQ(cache.GetSize(), 0u);
-  EXPECT_FALSE(cache.Get(kKey1));
-  EXPECT_FALSE(cache.Get(kKey2));
-  EXPECT_FALSE(cache.Get(kKey3));
-  EXPECT_FALSE(cache.Get(kKey4));
-}
-
-}  // namespace
-}  // namespace content
diff --git a/content/browser/code_cache/simple_lru_cache_unittest.cc b/content/browser/code_cache/simple_lru_cache_unittest.cc
new file mode 100644
index 0000000..6c2d1cb6
--- /dev/null
+++ b/content/browser/code_cache/simple_lru_cache_unittest.cc
@@ -0,0 +1,242 @@
+// 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 "content/browser/code_cache/simple_lru_cache.h"
+
+#include "base/feature_list.h"
+#include "base/test/scoped_feature_list.h"
+#include "content/public/common/content_features.h"
+#include "net/base/schemeful_site.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+
+constexpr auto kEmptyEntrySize = SimpleLruCache::kEmptyEntrySize;
+
+class SimpleLruCacheTest : public testing::TestWithParam<bool> {
+ public:
+  SimpleLruCacheTest() {
+    scoped_feature_list_.InitWithFeatureState(features::kInMemoryCodeCache,
+                                              GetParam());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+std::vector<uint8_t> ToVector(const mojo_base::BigBuffer& buffer) {
+  return std::vector<uint8_t>(buffer.byte_span().begin(),
+                              buffer.byte_span().end());
+}
+
+TEST_P(SimpleLruCacheTest, Empty) {
+  const std::string kKey = "hello";
+  SimpleLruCache cache(/*capacity=*/100 * 1024);
+
+  EXPECT_EQ(cache.GetSize(), 0u);
+  EXPECT_FALSE(cache.Has(kKey));
+}
+
+TEST_P(SimpleLruCacheTest, PutAndGet) {
+  const std::string kKey1("key1");
+  const std::string kKey2("key2");
+  const std::string kKey3("key3");
+  const std::string kKey4("key4");
+  // Note that kKey{i} doesn't necessarily correspond to kData{i} or
+  // kResponseTime{i}.
+  const std::vector<uint8_t> kData1 = {0x08};
+  const std::vector<uint8_t> kData2 = {0x02, 0x09};
+  const std::vector<uint8_t> kData3 = {0x03, 0x0a, 0xf1};
+  const std::vector<uint8_t> kData4 = {0x04, 0x02, 0x00, 0x30};
+  const base::Time kResponseTime1 = base::Time::UnixEpoch() + base::Hours(1);
+  const base::Time kResponseTime2 = base::Time::UnixEpoch() + base::Hours(2);
+  const base::Time kResponseTime3 = base::Time::UnixEpoch() + base::Hours(3);
+  const base::Time kResponseTime4 = base::Time::UnixEpoch() + base::Hours(4);
+
+  SimpleLruCache cache(/*capacity=*/100 * 1024);
+
+  EXPECT_EQ(cache.GetSize(), 0u);
+  EXPECT_FALSE(cache.Has(kKey1));
+  EXPECT_FALSE(cache.Has(kKey2));
+  EXPECT_FALSE(cache.Has(kKey3));
+  EXPECT_FALSE(cache.Has(kKey4));
+
+  cache.Put(kKey1, kResponseTime1, base::make_span(kData1));
+  EXPECT_EQ(cache.GetSize(), 1 + 4 + kEmptyEntrySize);
+  EXPECT_TRUE(cache.Has(kKey1));
+  EXPECT_FALSE(cache.Has(kKey2));
+  EXPECT_FALSE(cache.Has(kKey3));
+  EXPECT_FALSE(cache.Has(kKey4));
+
+  // Updates the entry.
+  cache.Put(kKey1, kResponseTime2, base::make_span(kData2));
+  EXPECT_EQ(cache.GetSize(), 2 + 4 + kEmptyEntrySize);
+  EXPECT_TRUE(cache.Has(kKey1));
+  EXPECT_FALSE(cache.Has(kKey2));
+  EXPECT_FALSE(cache.Has(kKey3));
+  EXPECT_FALSE(cache.Has(kKey4));
+
+  cache.Put(kKey2, kResponseTime3, base::make_span(kData3));
+  EXPECT_EQ(cache.GetSize(), 5 + 8 + 2 * kEmptyEntrySize);
+  EXPECT_TRUE(cache.Has(kKey1));
+  EXPECT_TRUE(cache.Has(kKey2));
+  EXPECT_FALSE(cache.Has(kKey3));
+  EXPECT_FALSE(cache.Has(kKey4));
+
+  // We don't create an entry for `kKey3` intentionally.
+  cache.Put(kKey4, kResponseTime4, base::make_span(kData4));
+  EXPECT_EQ(cache.GetSize(), 9 + 12 + 3 * kEmptyEntrySize);
+  const auto result1 = cache.Get(kKey1);
+  ASSERT_TRUE(result1.has_value());
+  EXPECT_EQ(result1->response_time, kResponseTime2);
+  const auto result2 = cache.Get(kKey2);
+  ASSERT_TRUE(result2.has_value());
+  EXPECT_EQ(result2->response_time, kResponseTime3);
+  const auto result3 = cache.Get(kKey3);
+  ASSERT_FALSE(result3.has_value());
+  const auto result4 = cache.Get(kKey4);
+  ASSERT_TRUE(result4.has_value());
+  EXPECT_EQ(result4->response_time, kResponseTime4);
+
+  if (base::FeatureList::IsEnabled(features::kInMemoryCodeCache)) {
+    EXPECT_EQ(ToVector(result1->data), kData2);
+    EXPECT_EQ(ToVector(result2->data), kData3);
+    EXPECT_EQ(ToVector(result4->data), kData4);
+  } else {
+    EXPECT_EQ(result1->data.size(), 0u);
+    EXPECT_EQ(result2->data.size(), 0u);
+    EXPECT_EQ(result4->data.size(), 0u);
+  }
+}
+
+TEST_P(SimpleLruCacheTest, PutAndEvict) {
+  const std::string kKey("key1");
+  const uint8_t kData1[2] = {0x08, 0x09};
+  const uint8_t kData2[1] = {0x02};
+  const uint8_t kData3[2] = {0x03, 0x0a};
+  const base::Time kResponseTime1 = base::Time::UnixEpoch() + base::Hours(1);
+  const base::Time kResponseTime2 = base::Time::UnixEpoch() + base::Hours(2);
+  const base::Time kResponseTime3 = base::Time::UnixEpoch() + base::Hours(3);
+
+  SimpleLruCache cache(/*capacity=*/kEmptyEntrySize + kKey.size() + 1);
+
+  EXPECT_EQ(cache.GetSize(), 0u);
+  EXPECT_FALSE(cache.Has(kKey));
+
+  // This entry is immediately evicted because the size excceeds the capacity.
+  cache.Put(kKey, kResponseTime1, base::make_span(kData1));
+  EXPECT_EQ(cache.GetSize(), 0u);
+  EXPECT_FALSE(cache.Has(kKey));
+
+  // This entry stays.
+  cache.Put(kKey, kResponseTime2, base::make_span(kData2));
+  EXPECT_EQ(cache.GetSize(), 1 + kKey.size() + kEmptyEntrySize);
+  EXPECT_TRUE(cache.Has(kKey));
+
+  // An updated entry can also be evicted.
+  cache.Put(kKey, kResponseTime3, base::make_span(kData3));
+  EXPECT_EQ(cache.GetSize(), 0u);
+  EXPECT_FALSE(cache.Has(kKey));
+}
+
+TEST_P(SimpleLruCacheTest, LRU) {
+  const std::string kKey1("key1");
+  const std::string kKey2("key2");
+  const std::string kKey3("key3");
+  const std::string kKey4("key4");
+  const base::Time kResponseTime = base::Time::UnixEpoch();
+
+  SimpleLruCache cache(kEmptyEntrySize * 2 + 8);
+
+  cache.Put(kKey1, kResponseTime, base::span<uint8_t>());
+  cache.Put(kKey2, kResponseTime, base::span<uint8_t>());
+  cache.Put(kKey3, kResponseTime, base::span<uint8_t>());
+  cache.Put(kKey4, kResponseTime, base::span<uint8_t>());
+
+  // The last two entries are kept.
+  EXPECT_EQ(cache.GetSize(), 2 * kEmptyEntrySize + 8);
+  EXPECT_FALSE(cache.Has(kKey1));
+  EXPECT_FALSE(cache.Has(kKey2));
+  EXPECT_TRUE(cache.Has(kKey3));
+  EXPECT_TRUE(cache.Has(kKey4));
+}
+
+TEST_P(SimpleLruCacheTest, LRUAndGet) {
+  const std::string kKey1("key1");
+  const std::string kKey2("key2");
+  const std::string kKey3("key3");
+  const std::string kKey4("key4");
+  const base::Time kResponseTime = base::Time::UnixEpoch();
+
+  SimpleLruCache cache(kEmptyEntrySize * 2 + 8);
+
+  cache.Put(kKey1, kResponseTime, base::span<uint8_t>());
+  cache.Put(kKey2, kResponseTime, base::span<uint8_t>());
+  // This call updates the access time.
+  EXPECT_TRUE(cache.Has(kKey1));
+  cache.Put(kKey3, kResponseTime, base::span<uint8_t>());
+
+  EXPECT_EQ(cache.GetSize(), 2 * kEmptyEntrySize + 8);
+  EXPECT_TRUE(cache.Has(kKey1));
+  EXPECT_FALSE(cache.Has(kKey2));
+  EXPECT_TRUE(cache.Has(kKey3));
+  EXPECT_FALSE(cache.Has(kKey4));
+}
+
+TEST_P(SimpleLruCacheTest, Delete) {
+  const base::Time kResponseTime = base::Time::UnixEpoch();
+  const std::string kKey1("key1");
+  const std::string kKey2("key2");
+  const std::string kKey3("key3");
+  const std::string kKey4("key4");
+  const uint8_t kData1[1] = {0x08};
+  const uint8_t kData2[2] = {0x02, 0x09};
+  const uint8_t kData3[3] = {0x03, 0x0a, 0xf1};
+  const uint8_t kData4[4] = {0x04, 0x02, 0x00, 0x30};
+
+  SimpleLruCache cache(/*capacity=*/1024 * 1024);
+
+  cache.Put(kKey1, kResponseTime, base::make_span(kData1));
+  cache.Put(kKey2, kResponseTime, base::make_span(kData2));
+  cache.Put(kKey3, kResponseTime, base::make_span(kData3));
+  cache.Put(kKey4, kResponseTime, base::make_span(kData4));
+
+  EXPECT_EQ(cache.GetSize(), 4 * kEmptyEntrySize + 16 + 10);
+  EXPECT_TRUE(cache.Has(kKey1));
+  EXPECT_TRUE(cache.Has(kKey2));
+  EXPECT_TRUE(cache.Has(kKey3));
+  EXPECT_TRUE(cache.Has(kKey4));
+
+  cache.Delete(kKey2);
+  EXPECT_EQ(cache.GetSize(), 3 * kEmptyEntrySize + 12 + 8);
+  EXPECT_TRUE(cache.Has(kKey1));
+  EXPECT_FALSE(cache.Has(kKey2));
+  EXPECT_TRUE(cache.Has(kKey3));
+  EXPECT_TRUE(cache.Has(kKey4));
+
+  cache.Delete(kKey2);
+  EXPECT_EQ(cache.GetSize(), 3 * kEmptyEntrySize + 12 + 8);
+  EXPECT_TRUE(cache.Has(kKey1));
+  EXPECT_FALSE(cache.Has(kKey2));
+  EXPECT_TRUE(cache.Has(kKey3));
+  EXPECT_TRUE(cache.Has(kKey4));
+
+  cache.Delete(kKey1);
+  cache.Delete(kKey2);
+  cache.Delete(kKey3);
+  cache.Delete(kKey4);
+  EXPECT_EQ(cache.GetSize(), 0u);
+  EXPECT_FALSE(cache.Has(kKey1));
+  EXPECT_FALSE(cache.Has(kKey2));
+  EXPECT_FALSE(cache.Has(kKey3));
+  EXPECT_FALSE(cache.Has(kKey4));
+}
+
+INSTANTIATE_TEST_SUITE_P(SimpleLruCacheTest,
+                         SimpleLruCacheTest,
+                         testing::Bool());
+
+}  // namespace
+}  // namespace content
diff --git a/content/browser/preloading/preloading.cc b/content/browser/preloading/preloading.cc
new file mode 100644
index 0000000..9e134dd
--- /dev/null
+++ b/content/browser/preloading/preloading.cc
@@ -0,0 +1,14 @@
+// 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 "content/browser/preloading/preloading.h"
+
+namespace content {
+
+PreloadingPredictor ToPreloadingPredictor(
+    ContentPreloadingPredictor predictor) {
+  return static_cast<PreloadingPredictor>(predictor);
+}
+
+}  // namespace content
diff --git a/content/browser/preloading/preloading.h b/content/browser/preloading/preloading.h
new file mode 100644
index 0000000..0bdc56260
--- /dev/null
+++ b/content/browser/preloading/preloading.h
@@ -0,0 +1,42 @@
+// 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 CONTENT_BROWSER_PRELOADING_PRELOADING_H_
+#define CONTENT_BROWSER_PRELOADING_PRELOADING_H_
+
+#include "content/public/browser/preloading.h"
+
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Defines various //content triggering mechanisms which trigger different
+// preloading operations mentioned in content/public/browser/preloading.h.
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class ContentPreloadingPredictor {
+  // Numbering starts from `kPreloadingPredictorContentStart` defined in
+  // //content/public/preloading.h. Advance numbering by +1 after adding a new
+  // element.
+
+  // This API allows an origin to list possible navigation URLs that the user
+  // might navigate to in order to perform preloading operations.
+  // For more details please see:
+  // https://wicg.github.io/nav-speculation/prerendering.html#speculation-rules
+  kSpeculationRules =
+      static_cast<int>(PreloadingPredictor::kPreloadingPredictorContentStart),
+
+  // TODO(crbug.com/1309934): Add more predictors as we integrate Preloading
+  // logging.
+};
+
+// Helper method to convert ContentPreloadingPredictor to
+// content::PreloadingPredictor to avoid casting.
+PreloadingPredictor CONTENT_EXPORT
+ToPreloadingPredictor(ContentPreloadingPredictor predictor);
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_PRELOADING_PRELOADING_H_
diff --git a/content/browser/preloading/preloading_attempt_impl.cc b/content/browser/preloading/preloading_attempt_impl.cc
index b772885..04fe037 100644
--- a/content/browser/preloading/preloading_attempt_impl.cc
+++ b/content/browser/preloading/preloading_attempt_impl.cc
@@ -111,19 +111,13 @@
 PreloadingAttemptImpl::~PreloadingAttemptImpl() = default;
 
 void PreloadingAttemptImpl::RecordPreloadingAttemptUKMs(
-    ukm::SourceId navigated_page_source_id,
-    const GURL& navigated_url) {
+    ukm::SourceId navigated_page_source_id) {
   ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
 
-  DCHECK(url_match_predicate_);
-  // Use the predicate to match the URLs as the matching logic varies for each
-  // predictor.
-  bool accurate_triggering = url_match_predicate_.Run(navigated_url);
-
   // Ensure that when the `triggering_outcome_` is kSuccess, then the
   // accurate_triggering should be true.
   if (triggering_outcome_ == PreloadingTriggeringOutcome::kSuccess) {
-    DCHECK(accurate_triggering)
+    DCHECK(is_accurate_triggering_)
         << "TriggeringOutcome set to kSuccess without correct prediction\n";
   }
 
@@ -136,7 +130,7 @@
         .SetHoldbackStatus(static_cast<int64_t>(holdback_status_))
         .SetTriggeringOutcome(static_cast<int64_t>(triggering_outcome_))
         .SetFailureReason(static_cast<int64_t>(failure_reason_))
-        .SetAccurateTriggering(accurate_triggering)
+        .SetAccurateTriggering(is_accurate_triggering_)
         .Record(ukm_recorder);
   }
 
@@ -149,11 +143,19 @@
         .SetHoldbackStatus(static_cast<int64_t>(holdback_status_))
         .SetTriggeringOutcome(static_cast<int64_t>(triggering_outcome_))
         .SetFailureReason(static_cast<int64_t>(failure_reason_))
-        .SetAccurateTriggering(accurate_triggering)
+        .SetAccurateTriggering(is_accurate_triggering_)
         .Record(ukm_recorder);
   }
 }
 
+void PreloadingAttemptImpl::SetIsAccurateTriggering(const GURL& navigated_url) {
+  DCHECK(url_match_predicate_);
+
+  // Use the predicate to match the URLs as the matching logic varies for each
+  // predictor.
+  is_accurate_triggering_ |= url_match_predicate_.Run(navigated_url);
+}
+
 // Used for StateTransitions matching.
 std::ostream& operator<<(std::ostream& os,
                          const PreloadingTriggeringOutcome& outcome) {
diff --git a/content/browser/preloading/preloading_attempt_impl.h b/content/browser/preloading/preloading_attempt_impl.h
index 3e11238..db5b120 100644
--- a/content/browser/preloading/preloading_attempt_impl.h
+++ b/content/browser/preloading/preloading_attempt_impl.h
@@ -29,11 +29,14 @@
   // Only difference is that the Preloading_Attempt_PreviousPrimaryPage UKM is
   // associated with the WebContents primary page that triggered the preloading
   // attempt. This is done to easily analyze the impact of the preloading
-  // attempt on the primary visible page. Here `navigated_page` and
-  // `navigated_url` represent the ukm::SourceId and URL of the navigated page.
-  // If the navigation doesn't happen these could be invalid/ empty.
-  void RecordPreloadingAttemptUKMs(ukm::SourceId navigated_page,
-                                   const GURL& navigated_url);
+  // attempt on the primary visible page. Here `navigated_page` represent the
+  // ukm::SourceId of the navigated page. If the navigation doesn't happen this
+  // could be invalid.
+  void RecordPreloadingAttemptUKMs(ukm::SourceId navigated_page);
+
+  // Sets `is_accurate_triggering_` to true if `navigated_url` matches the
+  // predicate URL logic.
+  void SetIsAccurateTriggering(const GURL& navigated_url);
 
   explicit PreloadingAttemptImpl(
       PreloadingPredictor predictor,
@@ -72,6 +75,9 @@
   // Triggers can specify their own predicate for judging whether two URLs are
   // considered as pointing to the same destination.
   const PreloadingURLMatchCallback url_match_predicate_;
+
+  // Set to true if this PreloadingAttempt was used for the next navigation.
+  bool is_accurate_triggering_ = false;
 };
 
 // Used when DCHECK_STATE_TRANSITION triggers.
diff --git a/content/browser/preloading/preloading_data_impl.cc b/content/browser/preloading/preloading_data_impl.cc
index 58cd8d7..1080995 100644
--- a/content/browser/preloading/preloading_data_impl.cc
+++ b/content/browser/preloading/preloading_data_impl.cc
@@ -6,6 +6,7 @@
 
 #include "content/browser/preloading/preloading_attempt_impl.h"
 #include "content/browser/preloading/preloading_prediction.h"
+#include "content/browser/renderer_host/navigation_request.h"
 #include "content/public/browser/page.h"
 #include "content/public/browser/web_contents.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
@@ -13,6 +14,16 @@
 namespace content {
 
 // static
+PreloadingURLMatchCallback PreloadingData::GetSameURLMatcher(
+    const GURL& destination_url) {
+  return base::BindRepeating(
+      [](const GURL& predicted_url, const GURL& navigated_url) {
+        return predicted_url == navigated_url;
+      },
+      destination_url);
+}
+
+// static
 PreloadingData* PreloadingData::GetOrCreateForWebContents(
     WebContents* web_contents) {
   return PreloadingDataImpl::GetOrCreateForWebContents(web_contents);
@@ -76,49 +87,74 @@
 void PreloadingDataImpl::PrimaryPageChanged(Page& page) {
   ukm::SourceId navigated_page_source_id =
       page.GetMainDocument().GetPageUkmSourceId();
-  GURL navigated_url = page.GetMainDocument().GetLastCommittedURL();
 
   // Log the UKMs also on navigation when the user ends up navigating. Please
   // note that we currently log the metrics on the primary page to analyze
   // preloading impact on user-visible primary pages.
-  RecordUKMForPreloadingAttempts(navigated_page_source_id, navigated_url);
-  RecordUKMForPreloadingPredictions(navigated_page_source_id, navigated_url);
+  RecordUKMForPreloadingAttempts(navigated_page_source_id);
+  RecordUKMForPreloadingPredictions(navigated_page_source_id);
 
   // Delete the user data after logging.
   web_contents()->RemoveUserData(UserDataKey());
 }
 
+void PreloadingDataImpl::DidStartNavigation(
+    NavigationHandle* navigation_handle) {
+  auto* navigation_request = NavigationRequest::From(navigation_handle);
+
+  // Only observe for the navigation in the primary frame tree to log the
+  // metrics after which this class will be deleted.
+  if (!navigation_request->IsInPrimaryMainFrame())
+    return;
+
+  // Ignore same-document navigations as preloading is not served for these
+  // cases.
+  if (navigation_request->IsSameDocument())
+    return;
+
+  // Match the preloading based on the URL the frame is navigating to rather
+  // than the committed URL as they could be different because of redirects. We
+  // set accurate triggering and prediction bits in DidStartNavigation before
+  // PrimaryPageChanged is invoked where the metrics are logged to capture if
+  // the prediction/triggering was accurate. This doesn't imply that the user
+  // navigated to the predicted URL.
+  SetIsAccurateTriggeringAndPrediction(navigation_request->GetURL());
+}
+
 void PreloadingDataImpl::WebContentsDestroyed() {
   // Log the UKMs also on WebContentsDestroyed event to avoid losing the data
   // in case the user doesn't end up navigating. When the WebContents is
   // destroyed before navigation, we pass ukm::kInvalidSourceId and empty URL to
   // avoid the UKM associated to wrong page.
-  RecordUKMForPreloadingAttempts(ukm::kInvalidSourceId, GURL());
-  RecordUKMForPreloadingPredictions(ukm::kInvalidSourceId, GURL());
+  RecordUKMForPreloadingAttempts(ukm::kInvalidSourceId);
+  RecordUKMForPreloadingPredictions(ukm::kInvalidSourceId);
 
   // Delete the user data after logging.
   web_contents()->RemoveUserData(UserDataKey());
 }
 
-void PreloadingDataImpl::RecordUKMForPreloadingAttempts(
-    ukm::SourceId navigated_page_source_id,
+void PreloadingDataImpl::SetIsAccurateTriggeringAndPrediction(
     const GURL& navigated_url) {
-  for (auto& attempt : preloading_attempts_) {
-    attempt->RecordPreloadingAttemptUKMs(navigated_page_source_id,
-                                         navigated_url);
-  }
+  for (auto& attempt : preloading_attempts_)
+    attempt->SetIsAccurateTriggering(navigated_url);
+
+  for (auto& prediction : preloading_predictions_)
+    prediction->SetIsAccuratePrediction(navigated_url);
+}
+
+void PreloadingDataImpl::RecordUKMForPreloadingAttempts(
+    ukm::SourceId navigated_page_source_id) {
+  for (auto& attempt : preloading_attempts_)
+    attempt->RecordPreloadingAttemptUKMs(navigated_page_source_id);
 
   // Clear all records once we record the UKMs.
   preloading_attempts_.clear();
 }
 
 void PreloadingDataImpl::RecordUKMForPreloadingPredictions(
-    ukm::SourceId navigated_page_source_id,
-    const GURL& navigated_url) {
-  for (auto& prediction : preloading_predictions_) {
-    prediction->RecordPreloadingPredictionUKMs(navigated_page_source_id,
-                                               navigated_url);
-  }
+    ukm::SourceId navigated_page_source_id) {
+  for (auto& prediction : preloading_predictions_)
+    prediction->RecordPreloadingPredictionUKMs(navigated_page_source_id);
 
   // Clear all records once we record the UKMs.
   preloading_predictions_.clear();
diff --git a/content/browser/preloading/preloading_data_impl.h b/content/browser/preloading/preloading_data_impl.h
index 473dd3e2..406b219d 100644
--- a/content/browser/preloading/preloading_data_impl.h
+++ b/content/browser/preloading/preloading_data_impl.h
@@ -49,6 +49,7 @@
       PreloadingURLMatchCallback url_match_predicate) override;
 
   // WebContentsObserver override.
+  void DidStartNavigation(NavigationHandle* navigation_handle) override;
   void PrimaryPageChanged(Page& page) override;
   void WebContentsDestroyed() override;
 
@@ -57,10 +58,10 @@
   friend class WebContentsUserData<PreloadingDataImpl>;
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
-  void RecordUKMForPreloadingAttempts(ukm::SourceId navigated_page_source_id,
-                                      const GURL& navigated_url);
-  void RecordUKMForPreloadingPredictions(ukm::SourceId navigated_page_source_id,
-                                         const GURL& navigated_url);
+  void RecordUKMForPreloadingAttempts(ukm::SourceId navigated_page_source_id);
+  void RecordUKMForPreloadingPredictions(
+      ukm::SourceId navigated_page_source_id);
+  void SetIsAccurateTriggeringAndPrediction(const GURL& navigated_url);
 
   // Stores all the preloading attempts that are happening for the next
   // navigation until the navigation takes place.
diff --git a/content/browser/preloading/preloading_prediction.cc b/content/browser/preloading/preloading_prediction.cc
index a00cb3f2..4a4d1ad 100644
--- a/content/browser/preloading/preloading_prediction.cc
+++ b/content/browser/preloading/preloading_prediction.cc
@@ -23,21 +23,15 @@
 PreloadingPrediction::~PreloadingPrediction() = default;
 
 void PreloadingPrediction::RecordPreloadingPredictionUKMs(
-    ukm::SourceId navigated_page_source_id,
-    const GURL& navigated_url) {
+    ukm::SourceId navigated_page_source_id) {
   ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
 
-  DCHECK(url_match_predicate_);
-  // Use the predicate to match the URLs as the matching logic varies for each
-  // predictor.
-  bool accurate_prediction = url_match_predicate_.Run(navigated_url);
-
   // Don't log when the source id is invalid.
   if (navigated_page_source_id != ukm::kInvalidSourceId) {
     ukm::builders::Preloading_Prediction(navigated_page_source_id)
         .SetPreloadingPredictor(static_cast<int64_t>(predictor_type_))
         .SetConfidence(confidence_)
-        .SetAccuratePrediction(accurate_prediction)
+        .SetAccuratePrediction(is_accurate_prediction_)
         .Record(ukm_recorder);
   }
 
@@ -46,9 +40,17 @@
         triggered_primary_page_source_id_)
         .SetPreloadingPredictor(static_cast<int64_t>(predictor_type_))
         .SetConfidence(confidence_)
-        .SetAccuratePrediction(accurate_prediction)
+        .SetAccuratePrediction(is_accurate_prediction_)
         .Record(ukm_recorder);
   }
 }
 
+void PreloadingPrediction::SetIsAccuratePrediction(const GURL& navigated_url) {
+  DCHECK(url_match_predicate_);
+
+  // Use the predicate to match the URLs as the matching logic varies for each
+  // predictor.
+  is_accurate_prediction_ |= url_match_predicate_.Run(navigated_url);
+}
+
 }  // namespace content
diff --git a/content/browser/preloading/preloading_prediction.h b/content/browser/preloading/preloading_prediction.h
index 3a71897..51f666a 100644
--- a/content/browser/preloading/preloading_prediction.h
+++ b/content/browser/preloading/preloading_prediction.h
@@ -32,8 +32,11 @@
   // is associated with the WebContents primary page that triggered the
   // preloading prediction. This is done to easily analyze the impact of the
   // preloading prediction on the primary visible page.
-  void RecordPreloadingPredictionUKMs(ukm::SourceId navigated_page_source_id,
-                                      const GURL& navigated_url);
+  void RecordPreloadingPredictionUKMs(ukm::SourceId navigated_page_source_id);
+
+  // Sets `is_accurate_prediction_` to true if `navigated_url` matches the URL
+  // predicate.
+  void SetIsAccuratePrediction(const GURL& navigated_url);
 
   explicit PreloadingPrediction(
       PreloadingPredictor predictor,
@@ -56,6 +59,10 @@
   // considered as pointing to the same destination as this varies for different
   // predictors.
   const PreloadingURLMatchCallback url_match_predicate_;
+
+  // Set to true when preloading prediction was correct i.e., when the
+  // navigation happens to the same predicted URL.
+  bool is_accurate_prediction_ = false;
 };
 
 }  // namespace content
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index 66929c2..e284e9b 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -30,6 +30,7 @@
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/browser/file_system_access/file_system_chooser_test_helpers.h"
 #include "content/browser/portal/portal.h"
+#include "content/browser/preloading/preloading.h"
 #include "content/browser/preloading/prerender/prerender_host.h"
 #include "content/browser/preloading/prerender/prerender_host_registry.h"
 #include "content/browser/preloading/prerender/prerender_metrics.h"
@@ -63,6 +64,7 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/mock_web_contents_observer.h"
 #include "content/public/test/navigation_handle_observer.h"
+#include "content/public/test/preloading_test_util.h"
 #include "content/public/test/prerender_test_util.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_navigation_throttle.h"
@@ -158,6 +160,18 @@
 
 DOCUMENT_USER_DATA_KEY_IMPL(DocumentData);
 
+using UkmEntry = ukm::TestUkmRecorder::HumanReadableUkmEntry;
+using ukm::builders::Preloading_Attempt;
+using ukm::builders::Preloading_Prediction;
+
+PreloadingFailureReason ToPreloadingFailureReason(
+    PrerenderHost::FinalStatus status) {
+  return static_cast<PreloadingFailureReason>(
+      static_cast<int>(status) +
+      static_cast<int>(
+          PreloadingFailureReason::kPreloadingFailureReasonCommonEnd));
+}
+
 class PrerenderBrowserTest : public ContentBrowserTest {
  public:
   using LifecycleStateImpl = RenderFrameHostImpl::LifecycleStateImpl;
@@ -177,6 +191,15 @@
   void SetUpOnMainThread() override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     host_resolver()->AddRule("*", "127.0.0.1");
+    attempt_ukm_entry_builder_ =
+        std::make_unique<test::PreloadingAttemptUkmEntryBuilder>(
+            PreloadingType::kPrerender,
+            ToPreloadingPredictor(
+                ContentPreloadingPredictor::kSpeculationRules));
+    prediction_ukm_entry_builder_ =
+        std::make_unique<test::PreloadingPredictionUkmEntryBuilder>(
+            ToPreloadingPredictor(
+                ContentPreloadingPredictor::kSpeculationRules));
     ssl_server_.AddDefaultHandlers(GetTestDataFilePath());
     ssl_server_.SetSSLConfig(
         net::test_server::EmbeddedTestServer::CERT_TEST_NAMES);
@@ -282,6 +305,23 @@
     return web_contents_impl()->GetPrimaryMainFrame();
   }
 
+  ukm::TestAutoSetUkmRecorder* test_ukm_recorder() {
+    return ukm_recorder_.get();
+  }
+
+  ukm::SourceId PrimaryPageSourceId() {
+    return current_frame_host()->GetPageUkmSourceId();
+  }
+
+  const test::PreloadingAttemptUkmEntryBuilder& attempt_ukm_entry_builder() {
+    return *attempt_ukm_entry_builder_;
+  }
+
+  const test::PreloadingPredictionUkmEntryBuilder&
+  prediction_ukm_entry_builder() {
+    return *prediction_ukm_entry_builder_;
+  }
+
   void TestHostPrerenderingState(const GURL& prerender_url) {
     const GURL kInitialUrl = GetUrl("/empty.html");
 
@@ -406,6 +446,10 @@
 
   base::HistogramTester histogram_tester_;
   std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
+  std::unique_ptr<test::PreloadingAttemptUkmEntryBuilder>
+      attempt_ukm_entry_builder_;
+  std::unique_ptr<test::PreloadingPredictionUkmEntryBuilder>
+      prediction_ukm_entry_builder_;
 };
 }  // namespace
 
@@ -432,6 +476,38 @@
   // Activating the prerendered page should not issue a request.
   EXPECT_EQ(GetRequestCount(kPrerenderingUrl), 1);
   ExpectFinalStatusForSpeculationRule(PrerenderHost::FinalStatus::kActivated);
+
+  {
+    // Cross-check that both Preloading.Prediction and Preloading.Attempt UKMs
+    // are logged on successful activation for speculation rules prerender.
+    ukm::SourceId ukm_source_id = PrimaryPageSourceId();
+    auto attempt_ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName, test::kPreloadingAttemptUkmMetrics);
+    auto prediction_ukm_entries =
+        test_ukm_recorder()->GetEntries(Preloading_Prediction::kEntryName,
+                                        test::kPreloadingPredictionUkmMetrics);
+    EXPECT_EQ(prediction_ukm_entries.size(), 1u);
+    EXPECT_EQ(attempt_ukm_entries.size(), 1u);
+
+    UkmEntry attempt_expected_entry = attempt_ukm_entry_builder().BuildEntry(
+        ukm_source_id, PreloadingEligibility::kEligible,
+        PreloadingHoldbackStatus::kAllowed,
+        PreloadingTriggeringOutcome::kSuccess,
+        PreloadingFailureReason::kUnspecified,
+        /*accurate=*/true);
+
+    UkmEntry prediction_expected_entry =
+        prediction_ukm_entry_builder().BuildEntry(ukm_source_id,
+                                                  /*confidence=*/100,
+                                                  /*accurate_prediction=*/true);
+
+    EXPECT_EQ(attempt_ukm_entries[0], attempt_expected_entry)
+        << test::ActualVsExpectedUkmEntryToString(attempt_ukm_entries[0],
+                                                  attempt_expected_entry);
+    EXPECT_EQ(prediction_ukm_entries[0], prediction_expected_entry)
+        << test::ActualVsExpectedUkmEntryToString(prediction_ukm_entries[1],
+                                                  prediction_expected_entry);
+  }
 }
 
 // Tests that the speculationrules-triggered prerender would be destroyed after
@@ -451,6 +527,37 @@
 
   // The prerender host should be destroyed.
   EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl));
+
+  {
+    // Cross-check that in case where the navigation happens to a different
+    // page, we log the correct metrics.
+    ukm::SourceId ukm_source_id = PrimaryPageSourceId();
+    auto attempt_ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName, test::kPreloadingAttemptUkmMetrics);
+    auto prediction_ukm_entries =
+        test_ukm_recorder()->GetEntries(Preloading_Prediction::kEntryName,
+                                        test::kPreloadingPredictionUkmMetrics);
+    EXPECT_EQ(prediction_ukm_entries.size(), 1u);
+    EXPECT_EQ(attempt_ukm_entries.size(), 1u);
+
+    UkmEntry attempt_expected_entry = attempt_ukm_entry_builder().BuildEntry(
+        ukm_source_id, PreloadingEligibility::kEligible,
+        PreloadingHoldbackStatus::kAllowed, PreloadingTriggeringOutcome::kReady,
+        PreloadingFailureReason::kUnspecified,
+        /*accurate=*/false);
+
+    UkmEntry prediction_expected_entry =
+        prediction_ukm_entry_builder().BuildEntry(
+            ukm_source_id, /*confidence=*/100,
+            /*accurate_prediction=*/false);
+
+    EXPECT_EQ(attempt_ukm_entries[0], attempt_expected_entry)
+        << test::ActualVsExpectedUkmEntryToString(attempt_ukm_entries[0],
+                                                  attempt_expected_entry);
+    EXPECT_EQ(prediction_ukm_entries[0], prediction_expected_entry)
+        << test::ActualVsExpectedUkmEntryToString(prediction_ukm_entries[1],
+                                                  prediction_expected_entry);
+  }
 }
 
 // Tests that clicking a link can activate a prerender.
@@ -662,6 +769,29 @@
   EXPECT_EQ(GetHostForUrl(kPrerenderingUrl),
             RenderFrameHost::kNoFrameTreeNodeId);
 
+  // Navigate primary page to flush the metrics.
+  const GURL kNavigatedURL = GetUrl("/title2.html");
+  ASSERT_TRUE(NavigateToURL(shell(), kNavigatedURL));
+
+  {
+    // Cross-check that Preloading.Attempt logs the correct failure reason.
+    ukm::SourceId ukm_source_id = PrimaryPageSourceId();
+    auto attempt_ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName, test::kPreloadingAttemptUkmMetrics);
+
+    UkmEntry attempt_expected_entry = attempt_ukm_entry_builder().BuildEntry(
+        ukm_source_id, PreloadingEligibility::kEligible,
+        PreloadingHoldbackStatus::kAllowed,
+        PreloadingTriggeringOutcome::kFailure,
+        ToPreloadingFailureReason(
+            PrerenderHost::FinalStatus::kLoginAuthRequested),
+        /*accurate=*/false);
+
+    EXPECT_EQ(attempt_ukm_entries[0], attempt_expected_entry)
+        << test::ActualVsExpectedUkmEntryToString(attempt_ukm_entries[0],
+                                                  attempt_expected_entry);
+  }
+
   // Cancellation must have occurred due to authentication request.
   ExpectFinalStatusForSpeculationRule(
       PrerenderHost::FinalStatus::kLoginAuthRequested);
@@ -1069,6 +1199,26 @@
   ASSERT_EQ(1u, activation_redirect_chain_observer.redirect_chain().size());
   EXPECT_EQ(kRedirectedUrl,
             activation_redirect_chain_observer.redirect_chain()[0]);
+
+  {
+    // Cross-check that in case redirection when the prerender navigates and
+    // user ends up navigating to the redirected URL. accurate_triggering is
+    // true.
+    ukm::SourceId ukm_source_id = PrimaryPageSourceId();
+    auto attempt_ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName, test::kPreloadingAttemptUkmMetrics);
+
+    UkmEntry attempt_expected_entry = attempt_ukm_entry_builder().BuildEntry(
+        ukm_source_id, PreloadingEligibility::kEligible,
+        PreloadingHoldbackStatus::kAllowed,
+        PreloadingTriggeringOutcome::kSuccess,
+        PreloadingFailureReason::kUnspecified,
+        /*accurate=*/true);
+
+    EXPECT_EQ(attempt_ukm_entries[0], attempt_expected_entry)
+        << test::ActualVsExpectedUkmEntryToString(attempt_ukm_entries[0],
+                                                  attempt_expected_entry);
+  }
 }
 
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, CrossOriginRedirection) {
@@ -3430,6 +3580,27 @@
   EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl));
   ExpectFinalStatusForSpeculationRule(
       PrerenderHost::FinalStatus::kLowEndDevice);
+
+  // Navigate primary page to flush the metrics.
+  NavigatePrimaryPage(kPrerenderingUrl);
+  {
+    // Cross-check that in case of low memory the eligibility reason points to
+    // kLowMemory.
+    ukm::SourceId ukm_source_id = PrimaryPageSourceId();
+    auto attempt_ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName, test::kPreloadingAttemptUkmMetrics);
+
+    UkmEntry attempt_expected_entry = attempt_ukm_entry_builder().BuildEntry(
+        ukm_source_id, PreloadingEligibility::kLowMemory,
+        PreloadingHoldbackStatus::kUnspecified,
+        PreloadingTriggeringOutcome::kUnspecified,
+        PreloadingFailureReason::kUnspecified,
+        /*accurate=*/true);
+
+    EXPECT_EQ(attempt_ukm_entries[0], attempt_expected_entry)
+        << test::ActualVsExpectedUkmEntryToString(attempt_ukm_entries[0],
+                                                  attempt_expected_entry);
+  }
 }
 
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
@@ -4884,6 +5055,41 @@
   EXPECT_FALSE(HasHostForUrl(kSecondPrerenderingUrl));
   ExpectFinalStatusForSpeculationRule(
       PrerenderHost::FinalStatus::kMaxNumOfRunningPrerendersExceeded);
+
+  // Navigate primary page to flush the metrics.
+  NavigatePrimaryPage(kFirstPrerenderingUrl);
+  {
+    // Verify that we log the correct metrics associated with case when we hit
+    // max prerenders exceeded.
+    ukm::SourceId ukm_source_id = PrimaryPageSourceId();
+    auto ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName, test::kPreloadingAttemptUkmMetrics);
+    auto prediction_ukm_entries =
+        test_ukm_recorder()->GetEntries(Preloading_Prediction::kEntryName,
+                                        test::kPreloadingPredictionUkmMetrics);
+    EXPECT_EQ(prediction_ukm_entries.size(), 3u);
+    EXPECT_EQ(ukm_entries.size(), 2u);
+
+    std::vector<UkmEntry> expected_entries = {
+        attempt_ukm_entry_builder().BuildEntry(
+            ukm_source_id, PreloadingEligibility::kEligible,
+            PreloadingHoldbackStatus::kAllowed,
+            PreloadingTriggeringOutcome::kSuccess,
+            PreloadingFailureReason::kUnspecified,
+            /*accurate=*/true),
+        attempt_ukm_entry_builder().BuildEntry(
+            ukm_source_id, PreloadingEligibility::kEligible,
+            PreloadingHoldbackStatus::kAllowed,
+            PreloadingTriggeringOutcome::kFailure,
+            PreloadingFailureReason::kUnspecified,
+            /*accurate=*/false),
+    };
+
+    EXPECT_THAT(ukm_entries,
+                testing::UnorderedElementsAreArray(expected_entries))
+        << content::test::ActualVsExpectedUkmEntriesToString(ukm_entries,
+                                                             expected_entries);
+  }
 }
 
 // Tests that PrerenderHostRegistry can hold up to two prerendering for the
@@ -4903,7 +5109,8 @@
           kFirstPrerenderingUrl, PrerenderTriggerType::kEmbedder,
           "EmbedderSuffixForTest",
           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+          nullptr);
   EXPECT_TRUE(prerender_handle1);
 
   histogram_tester.ExpectBucketCount(
@@ -4918,7 +5125,8 @@
           kSecondPrerenderingUrl, PrerenderTriggerType::kEmbedder,
           "EmbedderSuffixForTest",
           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+          nullptr);
   EXPECT_TRUE(prerender_handle2);
 
   histogram_tester.ExpectBucketCount(
@@ -4933,7 +5141,8 @@
           kThirdPrerenderingUrl, PrerenderTriggerType::kEmbedder,
           "EmbedderSuffixForTest",
           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+          nullptr);
   EXPECT_FALSE(prerender_handle3);
 
   histogram_tester.ExpectBucketCount(
@@ -4972,7 +5181,8 @@
           kSecondPrerenderingUrl, PrerenderTriggerType::kEmbedder,
           "EmbedderSuffixForTest",
           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+          nullptr);
   EXPECT_TRUE(prerender_handle2);
 
   histogram_tester.ExpectBucketCount(
@@ -4987,7 +5197,8 @@
           kThirdPrerenderingUrl, PrerenderTriggerType::kEmbedder,
           "EmbedderSuffixForTest",
           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+          nullptr);
   EXPECT_FALSE(prerender_handle3);
 
   histogram_tester.ExpectBucketCount(
@@ -5002,7 +5213,8 @@
       kThirdPrerenderingUrl, PrerenderTriggerType::kEmbedder,
       "EmbedderSuffixForTest",
       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+      nullptr);
   EXPECT_TRUE(prerender_handle3);
 }
 
@@ -5040,6 +5252,28 @@
 
   ExpectFinalStatusForSpeculationRule(
       PrerenderHost::FinalStatus::kCrossOriginNavigation);
+
+  ASSERT_TRUE(NavigateToURL(shell(), kPrerenderingUrl));
+  {
+    // Cross-check that in case of cross-origin navigation the eligibility
+    // reason points to kCrossOrigin.
+    ukm::SourceId ukm_source_id = PrimaryPageSourceId();
+    auto attempt_ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName, test::kPreloadingAttemptUkmMetrics);
+    EXPECT_EQ(attempt_ukm_entries.size(), 1u);
+
+    UkmEntry attempt_expected_entry = attempt_ukm_entry_builder().BuildEntry(
+        ukm_source_id, PreloadingEligibility::kCrossOrigin,
+        PreloadingHoldbackStatus::kUnspecified,
+        PreloadingTriggeringOutcome::kUnspecified,
+        PreloadingFailureReason::kUnspecified,
+
+        /*accurate=*/true);
+
+    EXPECT_EQ(attempt_ukm_entries[0], attempt_expected_entry)
+        << test::ActualVsExpectedUkmEntryToString(attempt_ukm_entries[0],
+                                                  attempt_expected_entry);
+  }
 }
 
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
@@ -5060,7 +5294,8 @@
           kPrerenderingUrl, PrerenderTriggerType::kEmbedder,
           "EmbedderSuffixForTest",
           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+          nullptr);
   EXPECT_TRUE(prerender_handle);
   test::PrerenderTestHelper::WaitForPrerenderLoadCompletion(
       *shell()->web_contents(), kPrerenderingUrl);
@@ -5084,7 +5319,8 @@
           prerendering_url, PrerenderTriggerType::kEmbedder,
           "EmbedderSuffixForTest",
           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
-                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+          nullptr);
   EXPECT_TRUE(prerender_handle);
   test::PrerenderTestHelper::WaitForPrerenderLoadCompletion(web_contents,
                                                             prerendering_url);
@@ -6019,6 +6255,57 @@
                   ->has_received_user_gesture_before_nav());
 }
 
+class PrerenderPreloaderHoldbackBrowserTest : public PrerenderBrowserTest {
+ public:
+  PrerenderPreloaderHoldbackBrowserTest() {
+    feature_list_.InitWithFeaturesAndParameters(
+        {{blink::features::kPrerender2, {{"prerender_holdback", "true"}}}},
+        {/* disabled_features */});
+  }
+  ~PrerenderPreloaderHoldbackBrowserTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PrerenderPreloaderHoldbackBrowserTest,
+                       PrerenderHoldbackTest) {
+  const GURL kInitialUrl = GetUrl("/empty.html");
+  const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender");
+
+  // Navigate to an initial page.
+  ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl));
+
+  // Start prerendering `kPrerenderingUrl` this should fail as we are in
+  // holdback.
+  test::PrerenderHostRegistryObserver registry_observer(*web_contents_impl());
+  AddPrerenderAsync(kPrerenderingUrl);
+
+  // Wait for PrerenderHostRegistry to receive the holdback prerender
+  // request, and it should be ignored.
+  registry_observer.WaitForTrigger(kPrerenderingUrl);
+  EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl));
+
+  NavigatePrimaryPage(kPrerenderingUrl);
+  {
+    // Cross-check that PreloadingHoldbackStatus is correctly set.
+    ukm::SourceId ukm_source_id = PrimaryPageSourceId();
+    auto attempt_ukm_entries = test_ukm_recorder()->GetEntries(
+        Preloading_Attempt::kEntryName, test::kPreloadingAttemptUkmMetrics);
+
+    UkmEntry attempt_expected_entry = attempt_ukm_entry_builder().BuildEntry(
+        ukm_source_id, PreloadingEligibility::kEligible,
+        PreloadingHoldbackStatus::kHoldback,
+        PreloadingTriggeringOutcome::kUnspecified,
+        PreloadingFailureReason::kUnspecified,
+        /*accurate=*/true);
+
+    EXPECT_EQ(attempt_ukm_entries[0], attempt_expected_entry)
+        << test::ActualVsExpectedUkmEntryToString(attempt_ukm_entries[0],
+                                                  attempt_expected_entry);
+  }
+}
+
 class PrerenderFencedFrameBrowserTest
     : public PrerenderBrowserTest,
       public testing::WithParamInterface<bool /* shadow_dom_fenced_frames */> {
diff --git a/content/browser/preloading/prerender/prerender_host.cc b/content/browser/preloading/prerender/prerender_host.cc
index 754c803..23b9b769 100644
--- a/content/browser/preloading/prerender/prerender_host.cc
+++ b/content/browser/preloading/prerender/prerender_host.cc
@@ -63,6 +63,14 @@
          potential_activation_headers.ToString();
 }
 
+PreloadingFailureReason ToPreloadingFailureReason(
+    PrerenderHost::FinalStatus status) {
+  return static_cast<PreloadingFailureReason>(
+      static_cast<int>(status) +
+      static_cast<int>(
+          PreloadingFailureReason::kPreloadingFailureReasonCommonEnd));
+}
+
 }  // namespace
 
 class PrerenderHost::PageHolder : public FrameTree::Delegate,
@@ -328,8 +336,9 @@
 };
 
 PrerenderHost::PrerenderHost(const PrerenderAttributes& attributes,
-                             WebContents& web_contents)
-    : attributes_(attributes) {
+                             WebContents& web_contents,
+                             PreloadingAttemptImpl* attempt)
+    : attributes_(attributes), attempt_(attempt) {
   DCHECK(blink::features::IsPrerender2Enabled());
   // If the prerendering is browser-initiated, it is expected to have no
   // initiator. All initiator related information should be null or invalid. On
@@ -380,6 +389,9 @@
   // Observe events about the prerendering contents.
   Observe(page_holder_->GetWebContents());
 
+  // Since prerender started we mark it as eligible and set it to running.
+  SetTriggeringOutcome(PreloadingTriggeringOutcome::kRunning);
+
   // Start prerendering navigation.
   NavigationController::LoadURLParams load_url_params(
       attributes_.prerendering_url);
@@ -440,14 +452,12 @@
   if (navigation_request->IsSameDocument())
     return;
 
+  const bool is_inside_prerender_frame_tree =
+      navigation_request->frame_tree_node()->frame_tree() ==
+      page_holder_->frame_tree();
   // Observe navigation only in the prerendering frame tree.
-  if (navigation_request->frame_tree_node()->frame_tree() !=
-      page_holder_->frame_tree()) {
+  if (!is_inside_prerender_frame_tree)
     return;
-  }
-
-  const bool is_prerender_main_frame =
-      navigation_request->GetFrameTreeNodeId() == frame_tree_node_id_;
 
   // Cancel prerendering on navigation request failure.
   //
@@ -455,6 +465,8 @@
   // blocking occurs before NavigationThrottles so cannot be observed in
   // NavigationThrottle::WillFailRequest().
   net::Error net_error = navigation_request->GetNetErrorCode();
+  const bool is_prerender_main_frame =
+      navigation_request->GetFrameTreeNodeId() == frame_tree_node_id_;
   absl::optional<FinalStatus> status;
   if (net_error == net::Error::ERR_BLOCKED_BY_CSP) {
     status = FinalStatus::kNavigationRequestBlockedByCsp;
@@ -475,6 +487,9 @@
   if (is_prerender_main_frame) {
     DCHECK(!is_ready_for_activation_);
     is_ready_for_activation_ = true;
+
+    // Prerender is ready to activate. Set the status to kReady.
+    SetTriggeringOutcome(PreloadingTriggeringOutcome::kReady);
   }
 }
 
@@ -518,6 +533,10 @@
   // source ID.
   RecordFinalStatus(FinalStatus::kActivated, attributes_.initiator_ukm_id,
                     navigation_request.GetNextPageUkmSourceId());
+
+  // Prerender is activated. Set the status to kSuccess.
+  SetTriggeringOutcome(PreloadingTriggeringOutcome::kSuccess);
+
   devtools_instrumentation::DidActivatePrerender(navigation_request);
   return page;
 }
@@ -815,6 +834,10 @@
                                       FinalStatus status) {
   RecordFinalStatus(status, attributes_.initiator_ukm_id,
                     ukm::kInvalidSourceId);
+
+  // Set failure reason for this PreloadingAttempt specific to the
+  // FinalStatus.
+  SetFailureReason(status);
 }
 
 void PrerenderHost::CreatePageHolder(WebContentsImpl& web_contents) {
@@ -863,6 +886,70 @@
             network::mojom::CSPDisposition::CHECK);
 }
 
+void PrerenderHost::SetTriggeringOutcome(PreloadingTriggeringOutcome outcome) {
+  if (!attempt_)
+    return;
+
+  attempt_->SetTriggeringOutcome(outcome);
+}
+
+void PrerenderHost::SetEligibility(PreloadingEligibility eligibility) {
+  if (!attempt_)
+    return;
+
+  attempt_->SetEligibility(eligibility);
+}
+
+void PrerenderHost::SetFailureReason(FinalStatus status) {
+  if (!attempt_)
+    return;
+
+  switch (status) {
+    // When adding a new failure reason, consider whether it should be
+    // propagated to `attempt_`. Most values should be propagated, but we
+    // explicitly do not propagate failure reasons if the prerender was actually
+    // successful (kActivated), or if prerender was successfully prepared but
+    // then destroyed because it wasn't needed for a subsequent navigation
+    // (kTriggerDestroyed, kEmbedderTriggeredAndDestroyed).
+    case FinalStatus::kActivated:
+    case FinalStatus::kEmbedderTriggeredAndDestroyed:
+    case FinalStatus::kTriggerDestroyed:
+      return;
+    case FinalStatus::kDestroyed:
+    case FinalStatus::kLowEndDevice:
+    case FinalStatus::kCrossOriginRedirect:
+    case FinalStatus::kCrossOriginNavigation:
+    case FinalStatus::kInvalidSchemeRedirect:
+    case FinalStatus::kInvalidSchemeNavigation:
+    case FinalStatus::kInProgressNavigation:
+    case FinalStatus::kNavigationRequestBlockedByCsp:
+    case FinalStatus::kMainFrameNavigation:
+    case FinalStatus::kMojoBinderPolicy:
+    case FinalStatus::kRendererProcessCrashed:
+    case FinalStatus::kRendererProcessKilled:
+    case FinalStatus::kDownload:
+    case FinalStatus::kNavigationNotCommitted:
+    case FinalStatus::kNavigationBadHttpStatus:
+    case FinalStatus::kClientCertRequested:
+    case FinalStatus::kNavigationRequestNetworkError:
+    case FinalStatus::kMaxNumOfRunningPrerendersExceeded:
+    case FinalStatus::kCancelAllHostsForTesting:
+    case FinalStatus::kDidFailLoad:
+    case FinalStatus::kStop:
+    case FinalStatus::kSslCertificateError:
+    case FinalStatus::kLoginAuthRequested:
+    case FinalStatus::kUaChangeRequiresReload:
+    case FinalStatus::kBlockedByClient:
+    case FinalStatus::kAudioOutputDeviceRequested:
+    case FinalStatus::kMixedContent:
+    case FinalStatus::kTriggerBackgrounded:
+    case FinalStatus::kEmbedderTriggeredAndSameOriginRedirected:
+    case FinalStatus::kEmbedderTriggeredAndCrossOriginRedirected:
+      attempt_->SetFailureReason(ToPreloadingFailureReason(status));
+      return;
+  }
+}
+
 bool PrerenderHost::IsUrlMatch(const GURL& url) const {
   // If the trigger defines its predicate, respect it.
   if (attributes_.url_match_predicate) {
diff --git a/content/browser/preloading/prerender/prerender_host.h b/content/browser/preloading/prerender/prerender_host.h
index 6167c41..13ddfc5 100644
--- a/content/browser/preloading/prerender/prerender_host.h
+++ b/content/browser/preloading/prerender/prerender_host.h
@@ -11,6 +11,7 @@
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "base/types/pass_key.h"
+#include "content/browser/preloading/preloading_attempt_impl.h"
 #include "content/browser/preloading/prerender/prerender_attributes.h"
 #include "content/browser/renderer_host/back_forward_cache_impl.h"
 #include "content/browser/renderer_host/stored_page.h"
@@ -95,7 +96,8 @@
   };
 
   PrerenderHost(const PrerenderAttributes& attributes,
-                WebContents& web_contents);
+                WebContents& web_contents,
+                PreloadingAttemptImpl* attempt);
   ~PrerenderHost() override;
 
   PrerenderHost(const PrerenderHost&) = delete;
@@ -206,6 +208,13 @@
   // Asks the registry to cancel prerendering.
   void Cancel(FinalStatus status);
 
+  // Sets the PreloadingTriggeringOutcome, PreloadingEligibility,
+  // PreloadingFailureReason for PreloadingAttempt associated with this
+  // PrerenderHost.
+  void SetTriggeringOutcome(PreloadingTriggeringOutcome outcome);
+  void SetEligibility(PreloadingEligibility eligibility);
+  void SetFailureReason(FinalStatus status);
+
   bool AreBeginNavigationParamsCompatibleWithNavigation(
       const blink::mojom::BeginNavigationParams& potential_activation);
   bool AreCommonNavigationParamsCompatibleWithNavigation(
@@ -227,6 +236,9 @@
 
   base::ObserverList<Observer> observers_;
 
+  // Stores the attempt corresponding to this prerender to log various metrics.
+  raw_ptr<PreloadingAttemptImpl> attempt_;
+
   // Navigation parameters for the navigation which loaded the main document of
   // the prerendered page, copied immediately after BeginNavigation when
   // throttles are created. They will be compared with the navigation parameters
diff --git a/content/browser/preloading/prerender/prerender_host_registry.cc b/content/browser/preloading/prerender/prerender_host_registry.cc
index a95512b..0c2b75b 100644
--- a/content/browser/preloading/prerender/prerender_host_registry.cc
+++ b/content/browser/preloading/prerender/prerender_host_registry.cc
@@ -15,6 +15,7 @@
 #include "base/trace_event/common/trace_event_common.h"
 #include "base/trace_event/trace_conversion_helper.h"
 #include "build/build_config.h"
+#include "content/browser/preloading/preloading_attempt_impl.h"
 #include "content/browser/preloading/prerender/prerender_metrics.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/navigation_request.h"
@@ -73,7 +74,8 @@
 
 int PrerenderHostRegistry::CreateAndStartHost(
     const PrerenderAttributes& attributes,
-    WebContents& web_contents) {
+    WebContents& web_contents,
+    PreloadingAttempt* attempt) {
   std::string recorded_url =
       attributes.initiator_origin.has_value()
           ? attributes.initiator_origin.value().GetURL().spec()
@@ -95,6 +97,8 @@
     WebContentsDelegate* web_contents_delegate = web_contents.GetDelegate();
     if (!web_contents_delegate ||
         !web_contents_delegate->IsPrerender2Supported(web_contents)) {
+      if (attempt)
+        attempt->SetEligibility(PreloadingEligibility::kPreloadingDisabled);
       return RenderFrameHost::kNoFrameTreeNodeId;
     }
 
@@ -103,6 +107,8 @@
       RecordPrerenderHostFinalStatus(
           PrerenderHost::FinalStatus::kTriggerBackgrounded, attributes,
           ukm::kInvalidSourceId);
+      if (attempt)
+        attempt->SetEligibility(PreloadingEligibility::kHidden);
       return RenderFrameHost::kNoFrameTreeNodeId;
     }
 
@@ -112,6 +118,8 @@
     if (!DeviceHasEnoughMemoryForPrerender()) {
       RecordPrerenderHostFinalStatus(PrerenderHost::FinalStatus::kLowEndDevice,
                                      attributes, ukm::kInvalidSourceId);
+      if (attempt)
+        attempt->SetEligibility(PreloadingEligibility::kLowMemory);
       return RenderFrameHost::kNoFrameTreeNodeId;
     }
 
@@ -125,26 +133,57 @@
       RecordPrerenderHostFinalStatus(
           PrerenderHost::FinalStatus::kCrossOriginNavigation, attributes,
           ukm::kInvalidSourceId);
+      if (attempt)
+        attempt->SetEligibility(PreloadingEligibility::kCrossOrigin);
       return RenderFrameHost::kNoFrameTreeNodeId;
     }
 
+    // Once all eligibility checks are completed, set the status to kEligible.
+    if (attempt)
+      attempt->SetEligibility(PreloadingEligibility::kEligible);
+
+    // Check for the HoldbackStatus after checking the eligibility.
+    if (base::GetFieldTrialParamByFeatureAsBool(blink::features::kPrerender2,
+                                                "prerender_holdback", false)) {
+      if (attempt)
+        attempt->SetHoldbackStatus(PreloadingHoldbackStatus::kHoldback);
+      return RenderFrameHost::kNoFrameTreeNodeId;
+    }
+    if (attempt)
+      attempt->SetHoldbackStatus(PreloadingHoldbackStatus::kAllowed);
+
     // Ignore prerendering requests for the same URL.
     for (auto& iter : prerender_host_by_frame_tree_node_id_) {
-      if (iter.second->GetInitialUrl() == attributes.prerendering_url)
+      if (iter.second->GetInitialUrl() == attributes.prerendering_url) {
+        if (attempt) {
+          attempt->SetTriggeringOutcome(
+              PreloadingTriggeringOutcome::kDuplicate);
+        }
+
         return RenderFrameHost::kNoFrameTreeNodeId;
+      }
     }
 
     // TODO(crbug.com/1197133): Cancel the started prerender and start a new
     // one if the score of the new candidate is higher than the started one's.
     if (!IsAllowedToStartPrerenderingForTrigger(attributes.trigger_type)) {
+      if (attempt) {
+        // The reason we don't consider limit exceeded as an ineligibility
+        // reason is because we can't replicate the behavior in our other
+        // experiment groups for analysis. To prevent this we set
+        // TriggeringOutcome to kFailure and look into the failure reason to
+        // learn more.
+        attempt->SetTriggeringOutcome(PreloadingTriggeringOutcome::kFailure);
+      }
       RecordPrerenderHostFinalStatus(
           PrerenderHost::FinalStatus::kMaxNumOfRunningPrerendersExceeded,
           attributes, ukm::kInvalidSourceId);
       return RenderFrameHost::kNoFrameTreeNodeId;
     }
 
+    auto* attempt_impl = static_cast<PreloadingAttemptImpl*>(attempt);
     auto prerender_host =
-        std::make_unique<PrerenderHost>(attributes, web_contents);
+        std::make_unique<PrerenderHost>(attributes, web_contents, attempt_impl);
     frame_tree_node_id = prerender_host->frame_tree_node_id();
 
     CHECK(!base::Contains(prerender_host_by_frame_tree_node_id_,
diff --git a/content/browser/preloading/prerender/prerender_host_registry.h b/content/browser/preloading/prerender/prerender_host_registry.h
index aa2590a..6ff71b6 100644
--- a/content/browser/preloading/prerender/prerender_host_registry.h
+++ b/content/browser/preloading/prerender/prerender_host_registry.h
@@ -70,8 +70,14 @@
   // For triggers.
   // Creates and starts a host. Returns the root frame tree node id of the
   // prerendered page, which can be used as the id of the host.
+  // `preloading_attempt` is the attempt corresponding to this prerender, the
+  // default value is set to nullptr as every case of prerendering trigger is
+  // not yet integrated with PreloadingAttempt.
+  // TODO(crbug.com/1325073): Remove the default value as nullptr for
+  // preloading_attempt once prerendering is integrated with Preloading APIs.
   int CreateAndStartHost(const PrerenderAttributes& attributes,
-                         WebContents& web_contents);
+                         WebContents& web_contents,
+                         PreloadingAttempt* preloading_attempt = nullptr);
 
   // Cancels the host registered for `frame_tree_node_id`. The host is
   // immediately removed from the map of non-reserved hosts but asynchronously
diff --git a/content/browser/preloading/speculation_rules/speculation_host_impl.cc b/content/browser/preloading/speculation_rules/speculation_host_impl.cc
index d3075ad5..b336584 100644
--- a/content/browser/preloading/speculation_rules/speculation_host_impl.cc
+++ b/content/browser/preloading/speculation_rules/speculation_host_impl.cc
@@ -7,8 +7,10 @@
 
 #include "base/containers/span.h"
 #include "base/ranges/algorithm.h"
+#include "content/browser/preloading//preloading.h"
 #include "content/browser/preloading/prefetch/prefetch_document_manager.h"
 #include "content/browser/preloading/prefetch/prefetch_features.h"
+#include "content/browser/preloading/preloading_data_impl.h"
 #include "content/browser/preloading/prerender/prerender_attributes.h"
 #include "content/browser/preloading/prerender/prerender_host_registry.h"
 #include "content/browser/renderer_host/render_frame_host_delegate.h"
@@ -113,6 +115,28 @@
   if (render_frame_host().GetParent())
     return;
 
+  WebContents* web_contents =
+      WebContents::FromRenderFrameHost(&render_frame_host());
+
+  for (const auto& candidate : candidates) {
+    // Create new PreloadingPrediction class and pass all fields for all
+    // candidates.
+
+    // In case of speculation rules, the confidence is set as 100 as the URL
+    // was not predicted and confidence in this case is not defined.
+    int64_t confidence = 100;
+    PreloadingURLMatchCallback same_url_matcher =
+        PreloadingData::GetSameURLMatcher(candidate->url);
+
+    auto* preloading_data =
+        PreloadingData::GetOrCreateForWebContents(web_contents);
+    // TODO(crbug.com/1341019): Pass the action requested by speculation rules
+    // to PreloadingPrediction.
+    preloading_data->AddPreloadingPrediction(
+        ToPreloadingPredictor(ContentPreloadingPredictor::kSpeculationRules),
+        confidence, std::move(same_url_matcher));
+  }
+
   if (base::FeatureList::IsEnabled(features::kPrefetchUseContentRefactor)) {
     PrefetchDocumentManager* prefetch_document_manager =
         PrefetchDocumentManager::GetOrCreateForCurrentDocument(
@@ -209,9 +233,24 @@
 
   // Actually start the candidates once the diffing is done.
   auto& rfhi = static_cast<RenderFrameHostImpl&>(render_frame_host());
+  WebContents* web_contents =
+      WebContents::FromRenderFrameHost(&render_frame_host());
   for (const auto& it : candidates_to_start) {
     DCHECK_EQ(it->action, blink::mojom::SpeculationAction::kPrerender);
 
+    auto* preloading_data =
+        PreloadingData::GetOrCreateForWebContents(web_contents);
+
+    // Create new PreloadingAttempt and pass all the values corresponding to
+    // this prerendering attempt.
+    PreloadingURLMatchCallback same_url_matcher =
+        PreloadingData::GetSameURLMatcher(it->url);
+    PreloadingAttempt* preloading_attempt =
+        preloading_data->AddPreloadingAttempt(
+            ToPreloadingPredictor(
+                ContentPreloadingPredictor::kSpeculationRules),
+            PreloadingType::kPrerender, std::move(same_url_matcher));
+
     auto [begin, end] = base::ranges::equal_range(
         started_prerenders_.begin(), started_prerenders_.end(), it->url,
         std::less<>(), &PrerenderInfo::url);
@@ -235,8 +274,6 @@
     }
 
     Referrer referrer(*(it->referrer));
-    WebContents* web_contents =
-        WebContents::FromRenderFrameHost(&render_frame_host());
     int prerender_host_id = registry_->CreateAndStartHost(
         PrerenderAttributes(it->url, PrerenderTriggerType::kSpeculationRule,
                             /*embedder_histogram_suffix=*/"", referrer,
@@ -246,7 +283,7 @@
                             rfhi.GetFrameTreeNodeId(),
                             rfhi.GetPageUkmSourceId(), ui::PAGE_TRANSITION_LINK,
                             /*url_match_predicate=*/absl::nullopt),
-        *web_contents);
+        *web_contents, /*preloading_attempt=*/preloading_attempt);
     started_prerenders_.insert(end, {.url = it->url,
                                      .referrer = referrer,
                                      .prerender_host_id = prerender_host_id});
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index a24e5484..5a4ab204 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -9435,6 +9435,7 @@
     PrerenderTriggerType trigger_type,
     const std::string& embedder_histogram_suffix,
     ui::PageTransition page_transition,
+    PreloadingAttempt* preloading_attempt,
     absl::optional<base::RepeatingCallback<bool(const GURL&)>>
         url_match_predicate) {
   PrerenderAttributes attributes(
@@ -9444,8 +9445,8 @@
       /*initiator_frame_token=*/absl::nullopt,
       /*initiator_frame_tree_node_id=*/RenderFrameHost::kNoFrameTreeNodeId,
       ukm::kInvalidSourceId, page_transition, url_match_predicate);
-  int frame_tree_node_id =
-      GetPrerenderHostRegistry()->CreateAndStartHost(attributes, *this);
+  int frame_tree_node_id = GetPrerenderHostRegistry()->CreateAndStartHost(
+      attributes, *this, preloading_attempt);
 
   if (frame_tree_node_id != FrameTreeNode::kFrameTreeNodeInvalidId) {
     return std::make_unique<PrerenderHandleImpl>(
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 4f96c01..9c90730d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -136,6 +136,7 @@
 struct AXEventNotificationDetails;
 struct LoadNotificationDetails;
 struct MHTMLGenerationParams;
+class PreloadingAttempt;
 
 namespace mojom {
 class CreateNewWindowParams;
@@ -848,6 +849,7 @@
       PrerenderTriggerType trigger_type,
       const std::string& embedder_histogram_suffix,
       ui::PageTransition page_transition,
+      PreloadingAttempt* preloading_attempt,
       absl::optional<base::RepeatingCallback<bool(const GURL&)>>
           url_match_predicate = absl::nullopt) override;
 
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
index fd38b62..157d05c 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
@@ -38,7 +38,7 @@
     private final boolean mIsPost;
     private boolean mHasUserGesture;
     private boolean mIsRedirect;
-    private final boolean mIsExternalProtocol;
+    private boolean mIsExternalProtocol;
     private final long mNavigationId;
     private final boolean mIsPageActivation;
     private final boolean mIsReload;
@@ -73,9 +73,10 @@
      * @param url The new URL.
      */
     @CalledByNative
-    private void didRedirect(GURL url) {
+    private void didRedirect(GURL url, boolean isExternalProtocol) {
         mUrl = url;
         mIsRedirect = true;
+        mIsExternalProtocol = isExternalProtocol;
     }
 
     /**
@@ -85,7 +86,7 @@
     public void didFinish(@NonNull GURL url, boolean isErrorPage, boolean hasCommitted,
             boolean isPrimaryMainFrameFragmentNavigation, boolean isDownload,
             boolean isValidSearchFormUrl, @PageTransition int transition, @NetError int errorCode,
-            int httpStatuscode) {
+            int httpStatuscode, boolean isExternalProtocol) {
         mUrl = url;
         mIsErrorPage = isErrorPage;
         mHasCommitted = hasCommitted;
@@ -95,6 +96,7 @@
         mPageTransition = transition;
         mErrorCode = errorCode;
         mHttpStatusCode = httpStatuscode;
+        mIsExternalProtocol = isExternalProtocol;
     }
 
     /**
diff --git a/content/public/browser/preloading.h b/content/public/browser/preloading.h
index 65191b4..fc576550 100644
--- a/content/public/browser/preloading.h
+++ b/content/public/browser/preloading.h
@@ -48,15 +48,18 @@
   // TODO(crbug.com/1309934): Add more predictors as we integrate Preloading
   // logging.
 
-  // > 100 values are reserved for embedder-specific values, such as the
-  // ChromePreloadingPredictor enum.
-};
+  // This constant is used to define the value from which features can add more
+  // enums beyond this value both inside and outside content. We mask it by 50
+  // and 100 to avoid usage of the same numbers for logging.
 
-// This constant is used to define the value from which embedders can add more
-// enums beyond this value. We mask it by 100 to avoid usage of the same numbers
-// for logging. This constant determines the value of enumerations persisted
-// into logs so should not be changed.
-static constexpr int64_t kPreloadingPredictorContentEnd = 100;
+  // >= 50 values are reserved for content-internal values, such as
+  // ContentPreloadingPredictor enum.
+  kPreloadingPredictorContentStart = 50,
+
+  // >= 100 values are reserved for embedder-specific values, such as the
+  // ChromePreloadingPredictor enum.
+  kPreloadingPredictorContentEnd = 100,
+};
 
 // Defines if a preloading operation is eligible for a given preloading
 // trigger.
@@ -72,13 +75,35 @@
   // predictor.
   kEligible = 1,
 
+  // Preloading operation could be ineligible if it is not triggered
+  // because some precondition was not satisfied. Preloading here could
+  // be ineligible due to various reasons subjective to the preloading
+  // operation like the following.
+  // These values are used in both //chrome and //content after integration with
+  // various preloading features.
+
   // Preloading operation was ineligible because preloading was disabled.
   kPreloadingDisabled = 2,
-};
 
-// This constant is used to define the value from which embedders can add more
-// enums beyond this value.
-static constexpr int64_t kPreloadingEligibilityContentEnd = 100;
+  // Preloading operation was ineligible because it was triggered from the
+  // background or a hidden page.
+  kHidden = 3,
+
+  // Preloading operation was ineligible because it was invoked for cross origin
+  // navigation while preloading was restricted to same-origin navigations.
+  // (It's plausible that some preloading mechanisms in the future could work
+  // for cross-origin navigations as well.)
+  kCrossOrigin = 4,
+
+  // Preloading was ineligible due to low memory restrictions.
+  kLowMemory = 5,
+
+  // TODO(crbug.com/1309934): Add more specific ineligibility reasons subject to
+  // each preloading operation
+  // This constant is used to define the value from which embedders can add more
+  // enums beyond this value.
+  kPreloadingEligibilityContentEnd = 100,
+};
 
 // The outcome of the holdback check. This is not part of eligibility status to
 // clarify that this check needs to happen after we are done verifying the
@@ -139,20 +164,22 @@
   kTriggeredButOutcomeUnknown = 7,
 };
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
 enum class PreloadingFailureReason {
   // The failure reason is unspecified if the triggering outcome is not
   // kFailure.
   kUnspecified = 0,
-};
 
-// This constant is used to define the value from which specifying preloading
-// types can add more enums beyond this value. We mask it by 100 to avoid usage
-// of the same numbers for logging. The semantics of values beyond 100 can vary
-// by preloading type (for example 101 might mean "the page was destroyed" for
-// prerender, but "the user already had cookies for a cross-origin prefetch" for
-// prefetch). This constant determines the value of enumerations persisted into
-// logs so should not be changed.
-static constexpr int64_t kPreloadingFailureReasonContentEnd = 100;
+  // This constant is used to define the value from which specifying preloading
+  // types can add more enums beyond this value. We mask it by 100 to avoid
+  // usage of the same numbers for logging. The semantics of values beyond 100
+  // can vary by preloading type (for example 101 might mean "the page was
+  // destroyed" for prerender, but "the user already had cookies for a
+  // cross-origin prefetch"
+  // for prefetch).
+  kPreloadingFailureReasonCommonEnd = 100,
+};
 
 }  // namespace content
 
diff --git a/content/public/browser/preloading_data.h b/content/public/browser/preloading_data.h
index 4a4062a..af375c9 100644
--- a/content/public/browser/preloading_data.h
+++ b/content/public/browser/preloading_data.h
@@ -79,6 +79,12 @@
   // details.
   static PreloadingData* GetOrCreateForWebContents(WebContents* web_contents);
 
+  // Helper method to return the PreloadingURLMatchCallback for
+  // `destination_url`. This method will return true only for exact matches to
+  // `destination_url`.
+  static PreloadingURLMatchCallback GetSameURLMatcher(
+      const GURL& destination_url);
+
   // Creates a new PreloadingAttempt and returns a pointer associated with the
   // PreloadingAttempt class. Here callers pass the `url_predicate_callback` to
   // verify if the navigated and triggered URLs match based on callers logic.
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index f1b8dee..2f4791f 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -100,6 +100,7 @@
 class WebUI;
 struct DropData;
 struct MHTMLGenerationParams;
+class PreloadingAttempt;
 
 // WebContents is the core class in content/. A WebContents renders web content
 // (usually HTML) in a rectangular area.
@@ -1340,17 +1341,19 @@
   // destruction. If the prerendering failed to start (e.g. if prerendering is
   // disabled, failure happened or because this URL is already being
   // prerendered), this function returns a nullptr.
-  // `url_match_predicate` allows embedders to define their own predicates for
-  // matching same-origin URLs during prerendering activation; it would be
-  // useful if embedders want Prerender2 to ignore some parameter mismatches.
-  // Note that if the mismatched prerender URL will be activated due to the
-  // predicate returning true, the last committed URL in the prerendered
-  // RenderFrameHost will be activated.
+  // PreloadingAttempt helps us to log various metrics associated with
+  // particular prerendering attempt. `url_match_predicate` allows embedders to
+  // define their own predicates for matching same-origin URLs during
+  // prerendering activation; it would be useful if embedders want Prerender2 to
+  // ignore some parameter mismatches. Note that if the mismatched prerender URL
+  // will be activated due to the predicate returning true, the last committed
+  // URL in the prerendered RenderFrameHost will be activated.
   virtual std::unique_ptr<PrerenderHandle> StartPrerendering(
       const GURL& prerendering_url,
       PrerenderTriggerType trigger_type,
       const std::string& embedder_histogram_suffix,
       ui::PageTransition page_transition,
+      PreloadingAttempt* preloading_attempt,
       absl::optional<base::RepeatingCallback<bool(const GURL&)>>
           url_match_predicate = absl::nullopt) = 0;
 
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 5fbb34e..bf77771 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -431,6 +431,10 @@
 const base::Feature kIdleDetection{"IdleDetection",
                                    base::FEATURE_ENABLED_BY_DEFAULT};
 
+// A feature flag for the memory-backed code cache.
+const base::Feature kInMemoryCodeCache{"InMemoryCodeCache",
+                                       base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Historically most navigations required IPC from browser to renderer and
 // from renderer back to browser. This was done to check for before-unload
 // handlers on the current page and occurred regardless of whether a
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 92be2e9..ac96ec2 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -110,6 +110,7 @@
 CONTENT_EXPORT extern const base::Feature kGreaseUACH;
 CONTENT_EXPORT extern const base::Feature kIdentityInCanMakePaymentEventFeature;
 CONTENT_EXPORT extern const base::Feature kIdleDetection;
+CONTENT_EXPORT extern const base::Feature kInMemoryCodeCache;
 CONTENT_EXPORT extern const base::Feature kIncludeIpcOverheadInNavigationStart;
 CONTENT_EXPORT extern const base::Feature kInstalledApp;
 CONTENT_EXPORT extern const base::Feature kInstalledAppProvider;
diff --git a/content/public/test/preloading_test_util.cc b/content/public/test/preloading_test_util.cc
index 1a630ed..1a820a2 100644
--- a/content/public/test/preloading_test_util.cc
+++ b/content/public/test/preloading_test_util.cc
@@ -11,6 +11,7 @@
 
 using UkmEntry = ukm::TestUkmRecorder::HumanReadableUkmEntry;
 using Preloading_Attempt = ukm::builders::Preloading_Attempt;
+using Preloading_Prediction = ukm::builders::Preloading_Prediction;
 
 const std::vector<std::string> kPreloadingAttemptUkmMetrics{
     Preloading_Attempt::kPreloadingTypeName,
@@ -22,6 +23,12 @@
     Preloading_Attempt::kAccurateTriggeringName,
 };
 
+const std::vector<std::string> kPreloadingPredictionUkmMetrics{
+    Preloading_Prediction::kPreloadingPredictorName,
+    Preloading_Prediction::kConfidenceName,
+    Preloading_Prediction::kAccuratePredictionName,
+};
+
 PreloadingAttemptUkmEntryBuilder::PreloadingAttemptUkmEntryBuilder(
     PreloadingType preloading_type,
     PreloadingPredictor predictor)
@@ -53,6 +60,24 @@
       }};
 }
 
+PreloadingPredictionUkmEntryBuilder::PreloadingPredictionUkmEntryBuilder(
+    PreloadingPredictor predictor)
+    : predictor_(predictor) {}
+
+UkmEntry PreloadingPredictionUkmEntryBuilder::BuildEntry(
+    ukm::SourceId source_id,
+    int64_t confidence,
+    bool accurate_prediction) const {
+  return UkmEntry{source_id,
+                  {
+                      {Preloading_Prediction::kPreloadingPredictorName,
+                       static_cast<int64_t>(predictor_)},
+                      {Preloading_Prediction::kConfidenceName, confidence},
+                      {Preloading_Prediction::kAccuratePredictionName,
+                       accurate_prediction ? 1 : 0},
+                  }};
+}
+
 std::string UkmEntryToString(const UkmEntry& entry) {
   std::string result;
   result +=
diff --git a/content/public/test/preloading_test_util.h b/content/public/test/preloading_test_util.h
index f3f1c2a..1982bb15 100644
--- a/content/public/test/preloading_test_util.h
+++ b/content/public/test/preloading_test_util.h
@@ -13,12 +13,13 @@
 
 namespace content::test {
 
-// The set of UKM metric names in the PreloadingAttempt UKM logs. This is
-// useful for calling TestUkmRecorder::GetEntries.
+// The set of UKM metric names in the PreloadingAttempt and PreloadingPrediction
+// UKM logs. This is useful for calling TestUkmRecorder::GetEntries.
 extern const std::vector<std::string> kPreloadingAttemptUkmMetrics;
+extern const std::vector<std::string> kPreloadingPredictionUkmMetrics;
 
 // Utility class to make building expected
-// TestUkmRecorder::HumanReadableUkmEntry for EXPECT_EQ.
+// TestUkmRecorder::HumanReadableUkmEntry for EXPECT_EQ for PreloadingAttempt.
 class PreloadingAttemptUkmEntryBuilder {
  public:
   PreloadingAttemptUkmEntryBuilder(PreloadingType preloading_type,
@@ -37,6 +38,22 @@
   PreloadingPredictor predictor_;
 };
 
+// Utility class to make building expected
+// TestUkmRecorder::HumanReadableUkmEntry for EXPECT_EQ for
+// PreloadingPrediction.
+class PreloadingPredictionUkmEntryBuilder {
+ public:
+  explicit PreloadingPredictionUkmEntryBuilder(PreloadingPredictor predictor);
+
+  ukm::TestUkmRecorder::HumanReadableUkmEntry BuildEntry(
+      ukm::SourceId source_id,
+      int64_t confidence,
+      bool accurate_prediction) const;
+
+ private:
+  PreloadingPredictor predictor_;
+};
+
 // Turns a UKM entry into a human-readable string.
 std::string UkmEntryToString(
     const ukm::TestUkmRecorder::HumanReadableUkmEntry& entry);
diff --git a/content/public/test/prerender_test_util.cc b/content/public/test/prerender_test_util.cc
index a5916957..b18d838b 100644
--- a/content/public/test/prerender_test_util.cc
+++ b/content/public/test/prerender_test_util.cc
@@ -338,7 +338,7 @@
   WebContents* web_contents = GetWebContents();
   return web_contents->StartPrerendering(prerendering_url, trigger_type,
                                          embedder_histogram_suffix,
-                                         page_transition);
+                                         page_transition, nullptr);
 }
 
 void PrerenderTestHelper::NavigatePrerenderedPage(int host_id,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index f8e48aee..1a4ef8dc 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2132,7 +2132,7 @@
     "../browser/child_process_security_policy_unittest.cc",
     "../browser/client_hints/client_hints_unittest.cc",
     "../browser/code_cache/generated_code_cache_unittest.cc",
-    "../browser/code_cache/simple_lru_cache_index_unittest.cc",
+    "../browser/code_cache/simple_lru_cache_unittest.cc",
     "../browser/compute_pressure/compute_pressure_host_unittest.cc",
     "../browser/compute_pressure/compute_pressure_manager_unittest.cc",
     "../browser/compute_pressure/compute_pressure_quantizer_unittest.cc",
diff --git a/extensions/renderer/bindings/api_binding_hooks.cc b/extensions/renderer/bindings/api_binding_hooks.cc
index b806dd04..719e90b 100644
--- a/extensions/renderer/bindings/api_binding_hooks.cc
+++ b/extensions/renderer/bindings/api_binding_hooks.cc
@@ -351,7 +351,8 @@
   if (delegate_) {
     RequestResult result = delegate_->HandleRequest(
         method_name, signature, context, arguments, type_refs);
-    // If the native hooks handled the call or set a custom callback, use that.
+    // If the native hooks handled the call, set a custom callback or a result
+    // modifier, use that.
     if (result.code != RequestResult::NOT_HANDLED ||
         !result.custom_callback.IsEmpty()) {
       return result;
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 50a923a..63bb2bb2 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -234,6 +234,7 @@
       "//chromeos/ash/components/dbus/audio",
       "//chromeos/ash/components/dbus/hermes",
       "//chromeos/dbus",
+      "//chromeos/dbus/cros_disks",
       "//chromeos/dbus/power",
       "//chromeos/login/login_state",
       "//chromeos/network",
diff --git a/extensions/shell/browser/shell_browser_main_parts.cc b/extensions/shell/browser/shell_browser_main_parts.cc
index 44902ce7..de2c738 100644
--- a/extensions/shell/browser/shell_browser_main_parts.cc
+++ b/extensions/shell/browser/shell_browser_main_parts.cc
@@ -71,6 +71,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chromeos/ash/components/dbus/audio/cras_audio_client.h"
+#include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #elif BUILDFLAG(IS_CHROMEOS)
@@ -129,13 +130,16 @@
   if (bus) {
     ash::hermes_clients::Initialize(bus);
     ash::CrasAudioClient::Initialize(bus);
+    chromeos::CrosDisksClient::Initialize(bus);
     chromeos::PowerManagerClient::Initialize(bus);
   } else {
     ash::hermes_clients::InitializeFakes();
     ash::CrasAudioClient::InitializeFake();
+    chromeos::CrosDisksClient::InitializeFake();
     chromeos::PowerManagerClient::InitializeFake();
   }
 
+  // Depends on CrosDisksClient.
   ash::disks::DiskMountManager::Initialize();
 
   chromeos::NetworkHandler::Initialize();
@@ -306,6 +310,7 @@
   chromeos::NetworkHandler::Shutdown();
   ash::disks::DiskMountManager::Shutdown();
   chromeos::PowerManagerClient::Shutdown();
+  chromeos::CrosDisksClient::Shutdown();
   ash::CrasAudioClient::Shutdown();
 #endif
 
diff --git a/fuchsia_web/runners/BUILD.gn b/fuchsia_web/runners/BUILD.gn
index b0d0159..a4e32b4 100644
--- a/fuchsia_web/runners/BUILD.gn
+++ b/fuchsia_web/runners/BUILD.gn
@@ -379,7 +379,6 @@
   deps = [
     "//base",
     "//base/test:test_support",
-    "//components/cast/message_port",
     "//fuchsia_web/common/test:run_all_integration_tests",
     "//fuchsia_web/runners/common/modular",
     "//net:test_support",
diff --git a/fuchsia_web/webengine/BUILD.gn b/fuchsia_web/webengine/BUILD.gn
index 47073133..f39a256 100644
--- a/fuchsia_web/webengine/BUILD.gn
+++ b/fuchsia_web/webengine/BUILD.gn
@@ -593,7 +593,6 @@
     ":web_engine_core",
     ":web_engine_unittests_fake_instance_manifest",
     "//base/test:test_support",
-    "//components/cast_streaming/public/mojom",
     "//components/url_rewrite/browser",
     "//components/url_rewrite/common",
     "//components/url_rewrite/mojom",
diff --git a/ios/chrome/browser/follow/BUILD.gn b/ios/chrome/browser/follow/BUILD.gn
index 048050c..591d3419 100644
--- a/ios/chrome/browser/follow/BUILD.gn
+++ b/ios/chrome/browser/follow/BUILD.gn
@@ -51,11 +51,14 @@
   deps = [
     ":follow",
     "//base",
+    "//components/history/core/browser",
+    "//components/keyed_service/core",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/follow:enums",
     "//ios/chrome/browser/follow:utils",
+    "//ios/chrome/browser/history",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/follow",
     "//ios/web/public",
diff --git a/ios/chrome/browser/follow/follow_tab_helper.mm b/ios/chrome/browser/follow/follow_tab_helper.mm
index c67b21eb..8f4e4958 100644
--- a/ios/chrome/browser/follow/follow_tab_helper.mm
+++ b/ios/chrome/browser/follow/follow_tab_helper.mm
@@ -4,9 +4,15 @@
 
 #import "ios/chrome/browser/follow/follow_tab_helper.h"
 
+#import "base/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#import "base/task/cancelable_task_tracker.h"
+#import "base/time/time.h"
+#import "components/history/core/browser/history_service.h"
+#import "components/history/core/browser/history_types.h"
+#import "components/keyed_service/core/service_access_type.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/chrome_url_util.h"
 #import "ios/chrome/browser/follow/follow_action_state.h"
@@ -14,6 +20,7 @@
 #import "ios/chrome/browser/follow/follow_java_script_feature.h"
 #import "ios/chrome/browser/follow/follow_menu_updater.h"
 #import "ios/chrome/browser/follow/follow_util.h"
+#import "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/follow/follow_provider.h"
@@ -21,6 +28,7 @@
 #include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/web_state.h"
 #include "ui/base/l10n/l10n_util.h"
+#import "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -31,6 +39,10 @@
 // The prefix of domain name that can be removed. It is used when generating the
 // follow item text.
 const std::string kRemovablePrefix = "www.";
+const int kVisitHostoryExclusiveDurationInHours = 1;
+const int kVisitHostoryDurationInDays = 14;
+const int kDefaultDailyVisitMin = 3;
+const int kDefaultNumVisitMin = 3;
 
 }  // namespace.
 
@@ -90,14 +102,16 @@
   // (FollowIPHCoordinator), so this class won't need to access browser_state
   // anymore, which brings convinience to testing.
 
-  // Do not show follow IPH when browsing in incognito.
+  // Do not update follow menu option and do not show IPH when browsing in
+  // incognito.
   if (web_state->GetBrowserState()->IsOffTheRecord()) {
     return;
   }
 
-  // Do not show follow IPH when browsing Chrome URLs, such as NTP, flags,
-  // version, sad tab, etc.
-  if (UrlHasChromeScheme(web_state->GetVisibleURL())) {
+  // Do not update follow menu option and do not show IPH when browsing non
+  // http,https URLs and Chrome URLs, such as NTP, flags, version, sad tab, etc.
+  const GURL& url = web_state->GetVisibleURL();
+  if (UrlHasChromeScheme(url) || !url.SchemeIsHTTPOrHTTPS()) {
     return;
   }
 
@@ -107,17 +121,47 @@
     case web::PageLoadCompletionStatus::SUCCESS:
       FollowJavaScriptFeature::GetInstance()->GetFollowWebPageURLs(
           web_state, base::BindOnce(^(FollowWebPageURLs* web_page_urls) {
+            // Update follow menu option if needed.
             if (follow_menu_updater_ && should_update_follow_item_) {
               UpdateFollowMenuItem(web_page_urls);
             }
+
+            // Show follow in-product help (IPH) if eligible.
             BOOL channel_recommended =
                 ios::GetChromeBrowserProvider()
                     .GetFollowProvider()
                     ->GetRecommendedStatus(web_page_urls);
-            if (channel_recommended) {
-              DCHECK(follow_iph_presenter_);
-              [follow_iph_presenter_ presentFollowWhileBrowsingIPH];
-            }
+
+            // Do not show follow IPH if the site is not recommended.
+            if (!channel_recommended)
+              return;
+
+            // Check if the site has enough visit count.
+            history::HistoryService* history_service =
+                ios::HistoryServiceFactory::GetForBrowserState(
+                    ChromeBrowserState::FromBrowserState(
+                        web_state_->GetBrowserState()),
+                    ServiceAccessType::EXPLICIT_ACCESS);
+            // Ignore any visits within the last hour so that we do not count
+            // the current visit to the page.
+            auto end_time = base::Time::Now() -
+                            base::Hours(kVisitHostoryExclusiveDurationInHours);
+            auto begin_time =
+                base::Time::Now() - base::Days(kVisitHostoryDurationInDays);
+
+            base::CancelableTaskTracker tracker;
+
+            // get history service
+            history_service->GetDailyVisitsToHost(
+                url, begin_time, end_time,
+                base::BindOnce(^(history::DailyVisitsResult result) {
+                  if (result.total_visits >= kDefaultNumVisitMin &&
+                      result.days_with_visits >= kDefaultDailyVisitMin) {
+                    DCHECK(follow_iph_presenter_);
+                    [follow_iph_presenter_ presentFollowWhileBrowsingIPH];
+                  }
+                }),
+                &tracker);
           }));
   }
 }
diff --git a/media/formats/hls/parse_status.cc b/media/formats/hls/parse_status.cc
index 113e240..68d3ea5 100644
--- a/media/formats/hls/parse_status.cc
+++ b/media/formats/hls/parse_status.cc
@@ -26,6 +26,7 @@
     PARSE_STATUS_CODE_CASE(kFailedToParseByteRange);
     PARSE_STATUS_CODE_CASE(kFailedToParseStableId);
     PARSE_STATUS_CODE_CASE(kFailedToParseInstreamId);
+    PARSE_STATUS_CODE_CASE(kFailedToParseAudioChannels);
     PARSE_STATUS_CODE_CASE(kInvalidPlaylistVersion);
     PARSE_STATUS_CODE_CASE(kUnknownPlaylistType);
     PARSE_STATUS_CODE_CASE(kMalformedAttributeList);
diff --git a/media/formats/hls/parse_status.h b/media/formats/hls/parse_status.h
index f6bebeb..88fff6e 100644
--- a/media/formats/hls/parse_status.h
+++ b/media/formats/hls/parse_status.h
@@ -23,6 +23,7 @@
   kFailedToParseByteRange,
   kFailedToParseStableId,
   kFailedToParseInstreamId,
+  kFailedToParseAudioChannels,
   kInvalidPlaylistVersion,
   kUnknownPlaylistType,
   kMalformedAttributeList,
diff --git a/media/formats/hls/source_string.cc b/media/formats/hls/source_string.cc
index d8dee3f..d5ff280 100644
--- a/media/formats/hls/source_string.cc
+++ b/media/formats/hls/source_string.cc
@@ -80,6 +80,15 @@
   return consumed;
 }
 
+template <typename ResolutionState>
+GenericSourceString<ResolutionState>
+GenericSourceString<ResolutionState>::ConsumeDelimiter(char c) {
+  const auto index = Str().find_first_of(c);
+  const auto prefix = Consume(index);
+  Consume(1);
+  return prefix;
+}
+
 template <>
 ResolvedSourceString SourceString::SkipVariableSubstitution() const {
   return ResolvedSourceString(
diff --git a/media/formats/hls/source_string.h b/media/formats/hls/source_string.h
index 9dd615d..899257a 100644
--- a/media/formats/hls/source_string.h
+++ b/media/formats/hls/source_string.h
@@ -83,6 +83,12 @@
   // string. Returns the substring that was consumed.
   GenericSourceString Consume(size_t count = base::StringPiece::npos);
 
+  // Finds the first occurrence of the given character, and returns the
+  // substring prefixing that character. The prefix and character are consumed
+  // from this string. If the given character does not appear anywhere in this
+  // string, the entire string is consumed and returned.
+  GenericSourceString ConsumeDelimiter(char c);
+
   // Produces a `ResolvedSourceString` by bypassing variable substitution.
   // This is useful for passing strings that must not contain variables to
   // functions consuming strings that may or may not have contained variable
diff --git a/media/formats/hls/types.cc b/media/formats/hls/types.cc
index 77fe833..08de04e 100644
--- a/media/formats/hls/types.cc
+++ b/media/formats/hls/types.cc
@@ -446,4 +446,54 @@
   return InstreamId(type, static_cast<uint8_t>(number));
 }
 
+AudioChannels::AudioChannels(DecimalInteger max_channels,
+                             std::vector<std::string> audio_coding_identifiers)
+    : max_channels_(max_channels),
+      audio_coding_identifiers_(std::move(audio_coding_identifiers)) {}
+
+AudioChannels::AudioChannels(const AudioChannels&) = default;
+
+AudioChannels::AudioChannels(AudioChannels&&) = default;
+
+AudioChannels& AudioChannels::operator=(const AudioChannels&) = default;
+
+AudioChannels& AudioChannels::operator=(AudioChannels&&) = default;
+
+AudioChannels::~AudioChannels() = default;
+
+ParseStatus::Or<AudioChannels> AudioChannels::Parse(ResolvedSourceString str) {
+  // First parameter is a decimal-integer indicating the number of channels
+  const auto max_channels_str = str.ConsumeDelimiter('/');
+  auto max_channels_result = ParseDecimalInteger(max_channels_str);
+  if (max_channels_result.has_error()) {
+    return ParseStatus(ParseStatusCode::kFailedToParseAudioChannels)
+        .AddCause(std::move(max_channels_result).error());
+  }
+  const auto max_channels = std::move(max_channels_result).value();
+
+  // Second parameter (optional) is a comma-seperated list of audio coding
+  // identifiers.
+  auto audio_coding_identifiers_str = str.ConsumeDelimiter('/');
+  std::vector<std::string> audio_coding_identifiers;
+  while (!audio_coding_identifiers_str.Empty()) {
+    const auto identifier = audio_coding_identifiers_str.ConsumeDelimiter(',');
+
+    constexpr auto is_valid_coding_identifier_char = [](char c) -> bool {
+      return base::IsAsciiUpper(c) || base::IsAsciiDigit(c) || c == '-';
+    };
+
+    // Each string must be non-empty and consist only of the allowed characters
+    if (identifier.Empty() ||
+        !base::ranges::all_of(identifier.Str(),
+                              is_valid_coding_identifier_char)) {
+      return ParseStatusCode::kFailedToParseAudioChannels;
+    }
+
+    audio_coding_identifiers.emplace_back(identifier.Str());
+  }
+
+  // Ignore any remaining parameters for forward-compatibility
+  return AudioChannels(max_channels, std::move(audio_coding_identifiers));
+}
+
 }  // namespace media::hls::types
diff --git a/media/formats/hls/types.h b/media/formats/hls/types.h
index e02b5c6..1b383cf 100644
--- a/media/formats/hls/types.h
+++ b/media/formats/hls/types.h
@@ -219,6 +219,38 @@
   uint8_t number_;
 };
 
+// Represents the contents of the 'CHANNELS' attribute on the 'EXT-X-MEDIA' tag
+// for an audio stream.
+// https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.6.1:~:text=If%20the%20TYPE%20attribute%20is%20AUDIO%2C%20then%20the%20first%20parameter%20is%20a
+class MEDIA_EXPORT AudioChannels {
+ public:
+  AudioChannels(const AudioChannels&);
+  AudioChannels(AudioChannels&&);
+  AudioChannels& operator=(const AudioChannels&);
+  AudioChannels& operator=(AudioChannels&&);
+  ~AudioChannels();
+
+  static ParseStatus::Or<AudioChannels> Parse(ResolvedSourceString);
+
+  // Returns the max number of independent, simultaneous audio channels present
+  // in any media segment in the associated rendition.
+  DecimalInteger GetMaxChannels() const { return max_channels_; }
+
+  // Returns the list of audio coding identifiers, which are strings of
+  // characters in the set [A-Z], [0-9], '-'. This list may be empty, or may
+  // only contain "-", indicating that the audio is only channel-based.
+  const std::vector<std::string>& GetAudioCodingIdentifiers() const {
+    return audio_coding_identifiers_;
+  }
+
+ private:
+  AudioChannels(DecimalInteger max_channels,
+                std::vector<std::string> audio_coding_identifiers);
+
+  DecimalInteger max_channels_;
+  std::vector<std::string> audio_coding_identifiers_;
+};
+
 }  // namespace media::hls::types
 
 #endif  // MEDIA_FORMATS_HLS_TYPES_H_
diff --git a/media/formats/hls/types_unittest.cc b/media/formats/hls/types_unittest.cc
index a5cdd69..898142c 100644
--- a/media/formats/hls/types_unittest.cc
+++ b/media/formats/hls/types_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "media/formats/hls/types.h"
 
+#include <initializer_list>
+#include <string>
 #include <utility>
 
 #include "base/location.h"
@@ -866,4 +868,58 @@
   ok_test("SERVICE63", types::InstreamId::Type::kService, 63);
 }
 
+TEST(HlsTypesTest, ParseAudioChannels) {
+  constexpr auto ok_test =
+      [](base::StringPiece str, types::DecimalInteger max_channels,
+         const std::initializer_list<std::string>& audio_coding_identifiers,
+         const base::Location& from = base::Location::Current()) {
+        auto result = types::AudioChannels::Parse(
+            ResolvedSourceString::CreateForTesting(str));
+        ASSERT_TRUE(result.has_value()) << from.ToString();
+        const auto value = std::move(result).value();
+        EXPECT_EQ(value.GetMaxChannels(), max_channels) << from.ToString();
+        EXPECT_TRUE(base::ranges::equal(value.GetAudioCodingIdentifiers(),
+                                        audio_coding_identifiers))
+            << from.ToString();
+      };
+  constexpr auto error_test = [](base::StringPiece str,
+                                 const base::Location& from =
+                                     base::Location::Current()) {
+    auto result = types::AudioChannels::Parse(
+        ResolvedSourceString::CreateForTesting(str));
+    ASSERT_TRUE(result.has_error()) << from.ToString();
+    EXPECT_EQ(std::move(result).error().code(),
+              ParseStatusCode::kFailedToParseAudioChannels)
+        << from.ToString();
+  };
+
+  // First parameter must be a valid DecimalInteger
+  error_test("");
+  error_test("/");
+  error_test("-1");
+  error_test("-1/");
+  error_test("/1");
+  error_test("/FOO");
+  error_test("1.5");
+  ok_test("1", 1, {});
+  ok_test("0", 0, {});
+  ok_test("2/", 2, {});
+  ok_test("99/", 99, {});
+
+  // Second parameter must be a valid list of audio coding identifiers
+  error_test("2/foo");
+  error_test("2/+");
+  error_test("2/,");
+  error_test("2/FOO,,");
+  ok_test("2/FOO", 2, {"FOO"});
+  ok_test("2/FOO,", 2, {"FOO"});
+  ok_test("2/FOO,BAR", 2, {"FOO", "BAR"});
+  ok_test("2/FOO-BAR,8AZ", 2, {"FOO-BAR", "8AZ"});
+  ok_test("2/-", 2, {"-"});
+
+  // Additional parameters are ignored
+  ok_test("2//19090zz**-0/", 2, {});
+  ok_test("2/FOO/19090zz**-0", 2, {"FOO"});
+}
+
 }  // namespace media::hls
diff --git a/media/gpu/v4l2/test/av1_decoder.cc b/media/gpu/v4l2/test/av1_decoder.cc
index f0e301ef..5143008 100644
--- a/media/gpu/v4l2/test/av1_decoder.cc
+++ b/media/gpu/v4l2/test/av1_decoder.cc
@@ -480,6 +480,9 @@
     libgav1::RefCountedBufferPtr current_frame,
     scoped_refptr<MmapedBuffer> buffer,
     uint32_t last_queued_buffer_index) {
+  state_->UpdateReferenceFrames(current_frame,
+                                base::strict_cast<int>(refresh_frame_flags));
+
   static_assert(
       kAv1NumRefFrames == sizeof(refresh_frame_flags) * CHAR_BIT,
       "|refresh_frame_flags| size must be equal to |kAv1NumRefFrames|");
@@ -545,9 +548,6 @@
     ref_frames_[i] = buffer;
   }
 
-  state_->UpdateReferenceFrames(current_frame,
-                                base::strict_cast<int>(refresh_frame_flags));
-
   return reusable_buffer_ids;
 }
 
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
index 3b847d6..f884d1f 100644
--- a/net/cert/internal/path_builder.cc
+++ b/net/cert/internal/path_builder.cc
@@ -783,6 +783,7 @@
         }
         AddResultPath(std::move(result_path));
       }
+      out_result_.iteration_count = iteration_count;
       RecordIterationCountHistogram(iteration_count);
       return std::move(out_result_);
     }
@@ -814,6 +815,7 @@
     AddResultPath(std::move(result_path));
 
     if (path_is_good && !explore_all_paths_) {
+      out_result_.iteration_count = iteration_count;
       RecordIterationCountHistogram(iteration_count);
       // Found a valid path, return immediately.
       return std::move(out_result_);
@@ -840,6 +842,9 @@
       out_result_.best_result_index = out_result_.paths.size();
     }
   }
+  if (result_path->certs.size() > out_result_.max_depth_seen) {
+    out_result_.max_depth_seen = result_path->certs.size();
+  }
   out_result_.paths.push_back(std::move(result_path));
 }
 
diff --git a/net/cert/internal/path_builder.h b/net/cert/internal/path_builder.h
index 34e96dfd..8b6e1850 100644
--- a/net/cert/internal/path_builder.h
+++ b/net/cert/internal/path_builder.h
@@ -139,6 +139,12 @@
     // better than invalid, but otherwise nothing is guaranteed.
     size_t best_result_index = 0;
 
+    // The iteration count reached by path building.
+    uint32_t iteration_count = 0;
+
+    // The max depth seen while path building.
+    uint32_t max_depth_seen = 0;
+
     // True if the search stopped because it exceeded the iteration limit
     // configured with |SetIterationLimit|.
     bool exceeded_iteration_limit = false;
diff --git a/net/cert/internal/path_builder_unittest.cc b/net/cert/internal/path_builder_unittest.cc
index 8f5d471..6afb13a 100644
--- a/net/cert/internal/path_builder_unittest.cc
+++ b/net/cert/internal/path_builder_unittest.cc
@@ -256,6 +256,7 @@
   auto result = path_builder.Run();
 
   EXPECT_FALSE(result.HasValidPath());
+  EXPECT_EQ(1U, result.max_depth_seen);
 }
 
 // Test a failed path building when the trust anchor is provided as a
@@ -681,10 +682,12 @@
     EXPECT_EQ(insufficient_limit, result.exceeded_iteration_limit);
 
     if (insufficient_limit) {
+      EXPECT_EQ(2U, result.iteration_count);
       EXPECT_THAT(histogram_tester.GetAllSamples(
                       "Net.CertVerifier.PathBuilderIterationCount"),
                   ElementsAre(base::Bucket(/*sample=*/2, /*count=*/1)));
     } else {
+      EXPECT_EQ(3U, result.iteration_count);
       EXPECT_THAT(histogram_tester.GetAllSamples(
                       "Net.CertVerifier.PathBuilderIterationCount"),
                   ElementsAre(base::Bucket(/*sample=*/3, /*count=*/1)));
@@ -827,6 +830,11 @@
     EXPECT_EQ(!insufficient_limit, result.HasValidPath());
     EXPECT_EQ(insufficient_limit,
               result.AnyPathContainsError(cert_errors::kDepthLimitExceeded));
+    if (insufficient_limit) {
+      EXPECT_EQ(2U, result.max_depth_seen);
+    } else {
+      EXPECT_EQ(4U, result.max_depth_seen);
+    }
   }
 }
 
@@ -1383,6 +1391,7 @@
       EXPECT_EQ(target_, path0.certs[0]);
       EXPECT_EQ(newintermediate_, path0.certs[1]);
       EXPECT_EQ(newroot_, path0.certs[2]);
+      EXPECT_EQ(3U, result.max_depth_seen);
     }
 
     if (expectation.expected_num_paths > 1) {
@@ -1393,6 +1402,7 @@
       EXPECT_EQ(target_, path1.certs[0]);
       EXPECT_EQ(newintermediate_, path1.certs[1]);
       EXPECT_EQ(oldroot_, path1.certs[2]);
+      EXPECT_EQ(3U, result.max_depth_seen);
     }
 
     if (expectation.expected_num_paths > 2) {
@@ -1403,6 +1413,7 @@
       EXPECT_EQ(target_, path2.certs[0]);
       EXPECT_EQ(oldintermediate_, path2.certs[1]);
       EXPECT_EQ(oldroot_, path2.certs[2]);
+      EXPECT_EQ(3U, result.max_depth_seen);
     }
 
     if (expectation.expected_num_paths > 3) {
@@ -1413,6 +1424,7 @@
       EXPECT_EQ(target_, path3.certs[0]);
       EXPECT_EQ(oldintermediate_, path3.certs[1]);
       EXPECT_EQ(newroot_, path3.certs[2]);
+      EXPECT_EQ(3U, result.max_depth_seen);
     }
   }
 }
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc
index 1e41ebe4..d7e0f01b 100644
--- a/pdf/pdf_view_web_plugin.cc
+++ b/pdf/pdf_view_web_plugin.cc
@@ -316,9 +316,8 @@
   // the plugin to be loaded in the extension and print preview to avoid
   // exposing sensitive APIs directly to external websites.
   //
-  // This is enforced before launching the plugin process (see
-  // `ChromeContentBrowserClient::ShouldAllowPluginCreation()`), so below we
-  // just do a CHECK as a defense-in-depth.
+  // This is enforced before creating the plugin (see
+  // `pdf::CreateInternalPlugin()`), so we just `CHECK` for defense-in-depth.
   const std::string& embedder_origin = client_->GetEmbedderOriginString();
   is_print_preview_ = (embedder_origin == kChromePrintHost);
   CHECK(IsPrintPreview() || embedder_origin == kChromeExtensionHost);
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index 3948f5a..475dc1d 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -209,6 +209,10 @@
     ]
   }
 
+  if (is_linux) {
+    deps += [ "//ui/linux:linux_ui" ]
+  }
+
   if (is_mac) {
     sources += [
       "print_settings_initializer_mac.cc",
diff --git a/printing/DEPS b/printing/DEPS
index 0cf5bfa2..e11147fd 100644
--- a/printing/DEPS
+++ b/printing/DEPS
@@ -13,4 +13,5 @@
   "+ui/base/resource",
   "+ui/base/text",
   "+ui/gfx",
+  "+ui/linux",
 ]
diff --git a/printing/printing_context_linux.cc b/printing/printing_context_linux.cc
index 805156b..c8673ea 100644
--- a/printing/printing_context_linux.cc
+++ b/printing/printing_context_linux.cc
@@ -10,6 +10,7 @@
 #include "base/check.h"
 #include "base/notreached.h"
 #include "base/values.h"
+#include "build/buildflag.h"
 #include "printing/buildflags/buildflags.h"
 #include "printing/metafile.h"
 #include "printing/mojom/print.mojom.h"
@@ -17,14 +18,13 @@
 #include "printing/print_job_constants.h"
 #include "printing/units.h"
 
+// Avoid using LinuxUi on Fuchsia.
+#if BUILDFLAG(IS_LINUX)
+#include "ui/linux/linux_ui.h"
+#endif
+
 namespace printing {
 
-namespace {
-
-static PrintingContextLinuxDelegate* g_delegate = nullptr;
-
-}  // namespace
-
 // static
 std::unique_ptr<PrintingContext> PrintingContext::CreateImpl(
     Delegate* delegate,
@@ -47,12 +47,6 @@
     print_dialog_->ReleaseDialog();
 }
 
-// static
-void PrintingContextLinuxDelegate::SetInstance(
-    PrintingContextLinuxDelegate* delegate) {
-  g_delegate = delegate;
-}
-
 void PrintingContextLinux::AskUserForSettings(int max_pages,
                                               bool has_selection,
                                               bool is_scripted,
@@ -74,19 +68,23 @@
 
   ResetSettings();
 
-  if (!g_delegate)
+#if BUILDFLAG(IS_LINUX)
+  if (!ui::LinuxUi::instance())
     return mojom::ResultCode::kSuccess;
 
   if (!print_dialog_)
-    print_dialog_ = g_delegate->CreatePrintDialog(this);
+    print_dialog_ = ui::LinuxUi::instance()->CreatePrintDialog(this);
   print_dialog_->UseDefaultSettings();
+#endif
 
   return mojom::ResultCode::kSuccess;
 }
 
 gfx::Size PrintingContextLinux::GetPdfPaperSizeDeviceUnits() {
-  if (g_delegate)
-    return g_delegate->GetPdfPaperSize(this);
+#if BUILDFLAG(IS_LINUX)
+  if (ui::LinuxUi::instance())
+    return ui::LinuxUi::instance()->GetPdfPaperSize(this);
+#endif
 
   return gfx::Size();
 }
@@ -96,16 +94,18 @@
   DCHECK(!printer_settings.show_system_dialog);
   DCHECK(!in_print_job_);
 
-  if (!g_delegate)
+#if BUILDFLAG(IS_LINUX)
+  if (!ui::LinuxUi::instance())
     return mojom::ResultCode::kSuccess;
 
   if (!print_dialog_)
-    print_dialog_ = g_delegate->CreatePrintDialog(this);
+    print_dialog_ = ui::LinuxUi::instance()->CreatePrintDialog(this);
 
   // PrintDialogGtk::UpdateSettings() calls InitWithSettings() so settings_ will
   // remain non-null after this line.
   print_dialog_->UpdateSettings(std::move(settings_));
   DCHECK(settings_);
+#endif
 
   return mojom::ResultCode::kSuccess;
 }
diff --git a/printing/printing_context_linux.h b/printing/printing_context_linux.h
index dfe49bc..0e2f451 100644
--- a/printing/printing_context_linux.h
+++ b/printing/printing_context_linux.h
@@ -16,19 +16,6 @@
 
 class MetafilePlayer;
 class PrintDialogLinuxInterface;
-class PrintingContextLinux;
-
-class COMPONENT_EXPORT(PRINTING) PrintingContextLinuxDelegate {
- public:
-  virtual ~PrintingContextLinuxDelegate() = default;
-
-  virtual PrintDialogLinuxInterface* CreatePrintDialog(
-      PrintingContextLinux* context) = 0;
-
-  virtual gfx::Size GetPdfPaperSize(PrintingContextLinux* context) = 0;
-
-  static void SetInstance(PrintingContextLinuxDelegate* delegate);
-};
 
 // PrintingContext with optional native UI for print dialog and pdf_paper_size.
 class COMPONENT_EXPORT(PRINTING) PrintingContextLinux : public PrintingContext {
diff --git a/services/device/geolocation/wifi_data_provider_common.cc b/services/device/geolocation/wifi_data_provider_common.cc
index 0e4c39c..b63a85f1 100644
--- a/services/device/geolocation/wifi_data_provider_common.cc
+++ b/services/device/geolocation/wifi_data_provider_common.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/location.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
@@ -67,6 +68,9 @@
   if (!wlan_api_)
     return;
 
+  SCOPED_UMA_HISTOGRAM_TIMER(
+      "Geolocation.WifiDataProviderCommon.WifiScanTaskTime");
+
   bool update_available = false;
   WifiData new_data;
   if (!wlan_api_->GetAccessPointData(&new_data.access_point_data)) {
diff --git a/services/network/network_service_memory_cache.cc b/services/network/network_service_memory_cache.cc
index f76e12e..b5f57e05 100644
--- a/services/network/network_service_memory_cache.cc
+++ b/services/network/network_service_memory_cache.cc
@@ -172,6 +172,7 @@
 
 void NetworkServiceMemoryCache::Clear() {
   entries_.Clear();
+  total_bytes_ = 0;
 }
 
 base::WeakPtr<NetworkServiceMemoryCache>
@@ -263,6 +264,20 @@
   if (max_per_entry_bytes_ < data.size())
     return;
 
+  // Record fresness of the response in seconds.
+  net::HttpResponseHeaders::FreshnessLifetimes lifetimes =
+      response_head->headers->GetFreshnessLifetimes(
+          response_head->response_time);
+  DCHECK(!lifetimes.freshness.is_zero());
+  const int64_t freshness_in_seconds = lifetimes.freshness.InSeconds();
+  constexpr int kMinSeconds = 1;
+  constexpr int kMaxSeconds = 60 * 60 * 24 * 10;  // 10 days.
+  base::UmaHistogramCustomCounts(
+      "NetworkService.MemoryCache.FreshnessAtStore",
+      base::saturated_cast<base::Histogram::Sample>(freshness_in_seconds),
+      kMinSeconds, kMaxSeconds,
+      /*buckets=*/50);
+
   auto prev = entries_.Peek(cache_key);
   if (prev != entries_.end()) {
     DCHECK_GE(total_bytes_, prev->second->content->size());
diff --git a/services/network/network_service_memory_cache.h b/services/network/network_service_memory_cache.h
index 8144ad3..cb122afa 100644
--- a/services/network/network_service_memory_cache.h
+++ b/services/network/network_service_memory_cache.h
@@ -52,6 +52,8 @@
 
   base::WeakPtr<NetworkServiceMemoryCache> GetWeakPtr();
 
+  size_t total_bytes() const { return total_bytes_; }
+
   // Clears all cache entries.
   void Clear();
 
diff --git a/services/network/network_service_memory_cache_unittest.cc b/services/network/network_service_memory_cache_unittest.cc
index e357b00..dd9ac08 100644
--- a/services/network/network_service_memory_cache_unittest.cc
+++ b/services/network/network_service_memory_cache_unittest.cc
@@ -631,6 +631,7 @@
 
   memory_cache().Clear();
 
+  ASSERT_EQ(memory_cache().total_bytes(), 0u);
   ASSERT_FALSE(CanServeFromMemoryCache(request1));
   ASSERT_FALSE(CanServeFromMemoryCache(request2));
   ASSERT_FALSE(CanServeFromMemoryCache(request3));
@@ -646,6 +647,7 @@
           MEMORY_PRESSURE_LEVEL_CRITICAL);
   task_environment().RunUntilIdle();
 
+  ASSERT_EQ(memory_cache().total_bytes(), 0u);
   ASSERT_FALSE(CanServeFromMemoryCache(request));
 }
 
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 97dd7cb..1672786 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8154,15 +8154,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8188,7 +8188,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8239,15 +8239,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8273,7 +8273,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8664,15 +8664,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8698,7 +8698,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8749,15 +8749,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8783,7 +8783,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 320c6a4..f255459 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46513,15 +46513,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46547,7 +46547,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46598,15 +46598,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46632,7 +46632,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47023,15 +47023,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47057,7 +47057,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47108,15 +47108,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47142,7 +47142,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47537,15 +47537,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47571,7 +47571,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47622,15 +47622,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47656,7 +47656,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48047,15 +48047,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48081,7 +48081,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48132,15 +48132,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48166,7 +48166,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48629,15 +48629,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48663,7 +48663,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48714,15 +48714,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48748,7 +48748,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49139,15 +49139,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49173,7 +49173,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49224,15 +49224,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49258,7 +49258,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49721,15 +49721,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49755,7 +49755,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49806,15 +49806,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49840,7 +49840,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -50231,15 +50231,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M103/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -50265,7 +50265,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M103",
-              "revision": "version:103.0.5060.127"
+              "revision": "version:103.0.5060.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -50316,15 +50316,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M104/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -50350,7 +50350,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M104",
-              "revision": "version:104.0.5112.49"
+              "revision": "version:104.0.5112.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index cfe2fc13..7abae43 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -529,16 +529,16 @@
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M104/out/Release',
-      '--impl-version=104'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=104',
     ],
     'identifier': 'with_impl_from_104',
     'swarming': {
@@ -546,23 +546,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M104',
-          'revision': 'version:104.0.5112.49'
+          'revision': 'version:104.0.5112.50',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M103/out/Release',
-      '--impl-version=103'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=103',
     ],
     'identifier': 'with_impl_from_103',
     'swarming': {
@@ -570,10 +570,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.127'
+          'revision': 'version:103.0.5060.128',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -673,16 +673,16 @@
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M104/out/Release',
-      '--impl-version=104'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=104',
     ],
     'identifier': 'with_impl_from_104',
     'swarming': {
@@ -690,23 +690,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M104',
-          'revision': 'version:104.0.5112.49'
+          'revision': 'version:104.0.5112.50',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M103/out/Release',
-      '--impl-version=103'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=103',
     ],
     'identifier': 'with_impl_from_103',
     'swarming': {
@@ -714,10 +714,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.127'
+          'revision': 'version:103.0.5060.128',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -817,16 +817,16 @@
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
+      '--client-outdir',
+      '../../weblayer_instrumentation_test_M104/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
-      '--client-outdir',
-      '../../weblayer_instrumentation_test_M104/out/Release',
-      '--client-version=104'
+      '--client-version=104',
     ],
     'identifier': 'with_client_from_104',
     'swarming': {
@@ -834,23 +834,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M104',
-          'revision': 'version:104.0.5112.49'
+          'revision': 'version:104.0.5112.50',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
+      '--client-outdir',
+      '../../weblayer_instrumentation_test_M103/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
-      '--client-outdir',
-      '../../weblayer_instrumentation_test_M103/out/Release',
-      '--client-version=103'
+      '--client-version=103',
     ],
     'identifier': 'with_client_from_103',
     'swarming': {
@@ -858,10 +858,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.127'
+          'revision': 'version:103.0.5060.128',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 7d4ae303..1174a34 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1848,15 +1848,11 @@
     "BrowsingDataLifetimeManager": [
         {
             "platforms": [
-                "linux",
-                "mac",
-                "chromeos",
-                "chromeos_lacros",
-                "windows"
+                "android"
             ],
             "experiments": [
                 {
-                    "name": "BrowsingDataLifetimeManager",
+                    "name": "Enabled",
                     "enable_features": [
                         "BrowsingDataLifetimeManager"
                     ]
@@ -4109,6 +4105,73 @@
             ]
         }
     ],
+    "GoogleLensDesktopContextMenuStringUpdates": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "EnableSearchPageString_20220707",
+                    "params": {
+                        "enable-lens-fullscreen-search": "false",
+                        "use-google-as-visual-search-provider": "false",
+                        "use-menu-item-alt-text-1": "true"
+                    },
+                    "enable_features": [
+                        "LensSearchOptimizations"
+                    ]
+                },
+                {
+                    "name": "EnableSearchAnyPartOfThePageString_20220707",
+                    "params": {
+                        "enable-lens-fullscreen-search": "false",
+                        "use-google-as-visual-search-provider": "false",
+                        "use-menu-item-alt-text-2": "true"
+                    },
+                    "enable_features": [
+                        "LensSearchOptimizations"
+                    ]
+                },
+                {
+                    "name": "EnableNoLensAttribution_20220707",
+                    "params": {
+                        "enable-lens-fullscreen-search": "false",
+                        "use-google-as-visual-search-provider": "true"
+                    },
+                    "enable_features": [
+                        "LensSearchOptimizations"
+                    ]
+                },
+                {
+                    "name": "EnableNoLensAttributionAndSearchPageString_20220707",
+                    "params": {
+                        "enable-lens-fullscreen-search": "false",
+                        "use-google-as-visual-search-provider": "true",
+                        "use-menu-item-alt-text-1": "true"
+                    },
+                    "enable_features": [
+                        "LensSearchOptimizations"
+                    ]
+                },
+                {
+                    "name": "EnableNoLensAttributionAndSearchAnyPartOfThePageString_20220707",
+                    "params": {
+                        "enable-lens-fullscreen-search": "false",
+                        "use-google-as-visual-search-provider": "true",
+                        "use-menu-item-alt-text-2": "true"
+                    },
+                    "enable_features": [
+                        "LensSearchOptimizations"
+                    ]
+                }
+            ]
+        }
+    ],
     "GoogleLensIOS": [
         {
             "platforms": [
@@ -7292,7 +7355,8 @@
                 {
                     "name": "DrDc",
                     "enable_features": [
-                        "EnableDrDc"
+                        "EnableDrDc",
+                        "EnableDrDcVulkan"
                     ],
                     "disable_features": [
                         "RawDraw"
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 42547d9..3976996 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1493,6 +1493,9 @@
         DelayAsyncScriptDelayType::kFinishedParsing,
         &delay_async_script_execution_delay_types};
 
+const base::Feature kForceDeferScriptIntervention{
+    "ForceDeferScriptIntervention", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kAllowSourceSwitchOnPausedVideoMediaStream{
     "AllowSourceSwitchOnPausedVideoMediaStream",
     base::FEATURE_ENABLED_BY_DEFAULT};
@@ -1551,5 +1554,8 @@
 const base::Feature kCSSOverflowForReplacedElements{
     "CSSOverflowForReplacedElements", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kClipboardUnsanitizedContent{
+    "ClipboardUnsanitizedContent", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 9da701c6..0a7adaab 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -734,6 +734,10 @@
 BLINK_COMMON_EXPORT extern const base::FeatureParam<DelayAsyncScriptDelayType>
     kDelayAsyncScriptExecutionDelayParam;
 
+// If enabled, parser-blocking scripts are force-deferred.
+// https://crbug.com/1339112
+BLINK_COMMON_EXPORT extern const base::Feature kForceDeferScriptIntervention;
+
 // If enabled, allows MediaStreamVideoSource objects to be restarted by a
 // successful source switch. Normally, switching the source would only allowed
 // on streams that are in started state. However, changing the source also first
@@ -801,6 +805,10 @@
 // CSSOverflowForReplacedElements, if necessary, due to compat issues.
 BLINK_COMMON_EXPORT extern const base::Feature kCSSOverflowForReplacedElements;
 
+// Allows reading/writing unsanitized content from/to the clipboard. Currently,
+// it is only applicable to HTML format. See crbug.com/1268679.
+BLINK_COMMON_EXPORT extern const base::Feature kClipboardUnsanitizedContent;
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni
index 0d4ffd5..6de84b7 100644
--- a/third_party/blink/renderer/bindings/generated_in_core.gni
+++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -105,9 +105,9 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_reload_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_result.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_result.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_transition.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_transition_while_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_transition_while_options.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_transition.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_transition.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_update_current_entry_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_navigation_update_current_entry_options.h",
@@ -129,6 +129,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_composition_event_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_computed_effect_timing.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_computed_effect_timing.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_content_visibility_auto_state_changed_event_init.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_content_visibility_auto_state_changed_event_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_matrix_component_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_matrix_component_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_css_numeric_type.cc",
@@ -583,6 +585,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_composition_event.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_computed_accessible_node.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_computed_accessible_node.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_content_visibility_auto_state_changed_event.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_content_visibility_auto_state_changed_event.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_coop_access_violation_report_body.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_coop_access_violation_report_body.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_count_queuing_strategy.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_core.gni b/third_party/blink/renderer/bindings/idl_in_core.gni
index 387be90..c1a40b2 100644
--- a/third_party/blink/renderer/bindings/idl_in_core.gni
+++ b/third_party/blink/renderer/bindings/idl_in_core.gni
@@ -117,6 +117,8 @@
           "//third_party/blink/renderer/core/css/style_sheet.idl",
           "//third_party/blink/renderer/core/css/style_sheet_list.idl",
           "//third_party/blink/renderer/core/css/css_try_rule.idl",
+          "//third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.idl",
+          "//third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event_init.idl",
           "//third_party/blink/renderer/core/document_transition/document_transition.idl",
           "//third_party/blink/renderer/core/document_transition/document_transition_callback.idl",
           "//third_party/blink/renderer/core/document_transition/document_transition_supplement.idl",
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index f927473..67e1ab8 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -511,6 +511,7 @@
   sources = [
     "css/font_face_set_load_event.idl",
     "css/media_query_list_event.idl",
+    "display_lock/content_visibility_auto_state_changed_event.idl",
     "dom/events/custom_event.idl",
     "dom/events/event.idl",
     "editing/ime/character_bounds_update_event.idl",
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index e139317..35fd1bed 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -405,8 +405,7 @@
     return CSSSelector::PseudoType::kPseudoWebKitCustomElement;
   if (name.StartsWith("-internal-"))
     return CSSSelector::PseudoType::kPseudoBlinkInternalElement;
-  if (RuntimeEnabledFeatures::CustomStatePseudoClassEnabled() &&
-      name.StartsWith("--"))
+  if (name.StartsWith("--"))
     return CSSSelector::PseudoType::kPseudoState;
 
   return CSSSelector::PseudoType::kPseudoUnknown;
diff --git a/third_party/blink/renderer/core/display_lock/build.gni b/third_party/blink/renderer/core/display_lock/build.gni
index 195bc60b..7667791 100644
--- a/third_party/blink/renderer/core/display_lock/build.gni
+++ b/third_party/blink/renderer/core/display_lock/build.gni
@@ -9,6 +9,8 @@
   "display_lock_document_state.h",
   "display_lock_utilities.cc",
   "display_lock_utilities.h",
+  "content_visibility_auto_state_changed_event.cc",
+  "content_visibility_auto_state_changed_event.h",
 ]
 
 blink_core_tests_display_lock = [
diff --git a/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.cc b/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.cc
new file mode 100644
index 0000000..52e0a54
--- /dev/null
+++ b/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.cc
@@ -0,0 +1,41 @@
+// 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 "third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/v8_content_visibility_auto_state_changed_event_init.h"
+#include "third_party/blink/renderer/core/event_interface_names.h"
+
+namespace blink {
+
+ContentVisibilityAutoStateChangedEvent::
+    ContentVisibilityAutoStateChangedEvent() = default;
+
+ContentVisibilityAutoStateChangedEvent::
+    ~ContentVisibilityAutoStateChangedEvent() = default;
+
+ContentVisibilityAutoStateChangedEvent::ContentVisibilityAutoStateChangedEvent(
+    const AtomicString& type,
+    const ContentVisibilityAutoStateChangedEventInit* initializer)
+    : Event(type, initializer), skipped_(initializer->skipped()) {}
+
+ContentVisibilityAutoStateChangedEvent::ContentVisibilityAutoStateChangedEvent(
+    const AtomicString& type,
+    bool skipped)
+    : Event(type, Bubbles::kYes, Cancelable::kYes), skipped_(skipped) {}
+
+bool ContentVisibilityAutoStateChangedEvent::skipped() const {
+  return skipped_;
+}
+
+const AtomicString& ContentVisibilityAutoStateChangedEvent::InterfaceName()
+    const {
+  return event_interface_names::kContentVisibilityAutoStateChangedEvent;
+}
+
+void ContentVisibilityAutoStateChangedEvent::Trace(Visitor* visitor) const {
+  Event::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.h b/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.h
new file mode 100644
index 0000000..228d24a
--- /dev/null
+++ b/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.h
@@ -0,0 +1,53 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_CORE_DISPLAY_LOCK_CONTENT_VISIBILITY_AUTO_STATE_CHANGED_EVENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_DISPLAY_LOCK_CONTENT_VISIBILITY_AUTO_STATE_CHANGED_EVENT_H_
+
+#include "third_party/blink/renderer/core/dom/events/event.h"
+
+namespace blink {
+
+class ContentVisibilityAutoStateChangedEventInit;
+
+class ContentVisibilityAutoStateChangedEvent final : public Event {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static ContentVisibilityAutoStateChangedEvent* Create() {
+    return MakeGarbageCollected<ContentVisibilityAutoStateChangedEvent>();
+  }
+  static ContentVisibilityAutoStateChangedEvent* Create(
+      const AtomicString& type,
+      bool skipped) {
+    return MakeGarbageCollected<ContentVisibilityAutoStateChangedEvent>(
+        type, skipped);
+  }
+  static ContentVisibilityAutoStateChangedEvent* Create(
+      const AtomicString& type,
+      const ContentVisibilityAutoStateChangedEventInit* initializer) {
+    return MakeGarbageCollected<ContentVisibilityAutoStateChangedEvent>(
+        type, initializer);
+  }
+
+  ContentVisibilityAutoStateChangedEvent();
+  ContentVisibilityAutoStateChangedEvent(const AtomicString& type,
+                                         bool skipped);
+  ContentVisibilityAutoStateChangedEvent(
+      const AtomicString&,
+      const ContentVisibilityAutoStateChangedEventInit*);
+  ~ContentVisibilityAutoStateChangedEvent() override;
+
+  bool skipped() const;
+
+  const AtomicString& InterfaceName() const override;
+
+  void Trace(Visitor*) const override;
+
+ private:
+  bool skipped_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DISPLAY_LOCK_CONTENT_VISIBILITY_AUTO_STATE_CHANGED_EVENT_H_
diff --git a/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.idl b/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.idl
new file mode 100644
index 0000000..2ce9012
--- /dev/null
+++ b/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.idl
@@ -0,0 +1,11 @@
+// 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.
+
+[
+    Exposed=Window,
+    RuntimeEnabled=ContentVisibilityAutoStateChangedEvent
+] interface ContentVisibilityAutoStateChangedEvent : Event {
+  constructor(DOMString type, optional ContentVisibilityAutoStateChangedEventInit eventInitDict = {});
+  readonly attribute boolean skipped;
+};
diff --git a/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event_init.idl b/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event_init.idl
new file mode 100644
index 0000000..998a5ee
--- /dev/null
+++ b/third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event_init.idl
@@ -0,0 +1,8 @@
+// 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.
+
+dictionary ContentVisibilityAutoStateChangedEventInit : EventInit {
+    boolean skipped = false;
+};
+
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 816f4c82c..27f723b 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
+#include "third_party/blink/renderer/core/display_lock/content_visibility_auto_state_changed_event.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_document_state.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -290,6 +291,9 @@
   // layout objects, since otherwise they would be hoisted out of our subtree.
   DetachDescendantTopLayerElements();
 
+  // Schedule ContentVisibilityAutoStateChanged event if needed.
+  ScheduleStateChangeEventIfNeeded();
+
   if (!element_->GetLayoutObject())
     return;
 
@@ -518,6 +522,17 @@
   }
 }
 
+void DisplayLockContext::ScheduleStateChangeEventIfNeeded() {
+  if (state_ == EContentVisibility::kAuto &&
+      RuntimeEnabledFeatures::ContentVisibilityAutoStateChangedEventEnabled() &&
+      !IsShapingDeferred()) {
+    element_->EnqueueEvent(
+        *ContentVisibilityAutoStateChangedEvent::Create(
+            event_type_names::kContentvisibilityautostatechanged, is_locked_),
+        TaskType::kMiscPlatformAPI);
+  }
+}
+
 void DisplayLockContext::NotifyForcedUpdateScopeEnded(ForcedPhase phase) {
   forced_info_.end(phase);
 }
@@ -564,6 +579,9 @@
   if (AXObjectCache* cache = element_->GetDocument().ExistingAXObjectCache())
     cache->ChildrenChanged(element_);
 
+  // Schedule ContentVisibilityAutoStateChanged event if needed.
+  ScheduleStateChangeEventIfNeeded();
+
   auto* layout_object = element_->GetLayoutObject();
   // We might commit without connecting, so there is no layout object yet.
   if (!layout_object)
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index 9ff9bb8..be6364a 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -348,6 +348,8 @@
 
   bool SubtreeHasTopLayerElement() const;
 
+  void ScheduleStateChangeEventIfNeeded();
+
   WeakMember<Element> element_;
   WeakMember<Document> document_;
   EContentVisibility state_ = EContentVisibility::kVisible;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 13c8067..c71bd9e 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -738,6 +738,9 @@
       load_event_progress_(kLoadEventCompleted),
       is_freezing_in_progress_(false),
       script_runner_(MakeGarbageCollected<ScriptRunner>(this)),
+      script_runner_delayer_(MakeGarbageCollected<ScriptRunnerDelayer>(
+          script_runner_,
+          ScriptRunner::DelayReason::kMilestone)),
       xml_version_("1.0"),
       xml_standalone_(kStandaloneUnspecified),
       has_xml_declaration_(0),
@@ -803,6 +806,9 @@
               ? MakeGarbageCollected<RenderBlockingResourceManager>(*this)
               : nullptr),
       data_(MakeGarbageCollected<DocumentData>(GetExecutionContext())) {
+  if (base::FeatureList::IsEnabled(features::kDelayAsyncScriptExecution))
+    script_runner_delayer_->Activate();
+
   if (GetFrame()) {
     DCHECK(GetFrame()->GetPage());
     ProvideContextFeaturesToDocumentFrom(*this, *GetFrame()->GetPage());
@@ -6761,13 +6767,13 @@
       // Notify the ScriptRunner if the first paint has been recorded and
       // we're delaying async scripts until first paint or finished parsing
       // (whichever comes first).
-      script_runner_->NotifyDelayedAsyncScriptsMilestoneReached();
+      script_runner_delayer_->Deactivate();
       break;
     case features::DelayAsyncScriptDelayType::kFinishedParsing:
       // Notify the ScriptRunner if we're finished parsing and we're delaying
       // async scripts until finished parsing occurs.
       if (milestone == MilestoneForDelayedAsyncScript::kFinishedParsing)
-        script_runner_->NotifyDelayedAsyncScriptsMilestoneReached();
+        script_runner_delayer_->Deactivate();
       break;
   }
 }
@@ -8052,6 +8058,7 @@
   visitor->Trace(css_target_);
   visitor->Trace(current_script_stack_);
   visitor->Trace(script_runner_);
+  visitor->Trace(script_runner_delayer_);
   visitor->Trace(lists_invalidated_at_document_);
   visitor->Trace(node_lists_);
   visitor->Trace(top_layer_elements_);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index b50f9b92..ee6a53b 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -215,6 +215,7 @@
 class ScriptPromise;
 class ScriptRegexp;
 class ScriptRunner;
+class ScriptRunnerDelayer;
 class ScriptValue;
 class ScriptableDocumentParser;
 class ScriptedAnimationController;
@@ -2264,6 +2265,7 @@
   base::ElapsedTimer start_time_;
 
   Member<ScriptRunner> script_runner_;
+  Member<ScriptRunnerDelayer> script_runner_delayer_;
 
   HeapVector<Member<ScriptElementBase>> current_script_stack_;
 
diff --git a/third_party/blink/renderer/core/dom/global_event_handlers.h b/third_party/blink/renderer/core/dom/global_event_handlers.h
index 66594c0..9779db50 100644
--- a/third_party/blink/renderer/core/dom/global_event_handlers.h
+++ b/third_party/blink/renderer/core/dom/global_event_handlers.h
@@ -54,6 +54,8 @@
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(change, kChange)
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(click, kClick)
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(close, kClose)
+  DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(contentvisibilityautostatechanged,
+                                         kContentvisibilityautostatechanged)
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(contextmenu, kContextmenu)
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(contextlost, kContextlost)
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(contextrestored, kContextrestored)
diff --git a/third_party/blink/renderer/core/dom/global_event_handlers.idl b/third_party/blink/renderer/core/dom/global_event_handlers.idl
index d3b7570..eefaa82 100644
--- a/third_party/blink/renderer/core/dom/global_event_handlers.idl
+++ b/third_party/blink/renderer/core/dom/global_event_handlers.idl
@@ -42,6 +42,7 @@
     attribute EventHandler onchange;
     attribute EventHandler onclick;
     attribute EventHandler onclose;
+    [RuntimeEnabled=ContentVisibilityAutoStateChangedEvent] attribute EventHandler oncontentvisibilityautostatechanged;
     attribute EventHandler oncontextlost;
     attribute EventHandler oncontextmenu;
     attribute EventHandler oncontextrestored;
diff --git a/third_party/blink/renderer/core/events/event_type_names.json5 b/third_party/blink/renderer/core/events/event_type_names.json5
index d569901..141eb19 100644
--- a/third_party/blink/renderer/core/events/event_type_names.json5
+++ b/third_party/blink/renderer/core/events/event_type_names.json5
@@ -82,6 +82,7 @@
     "connecting",
     "connectionavailable",
     "connectionstatechange",
+    "contentvisibilityautostatechanged",
     "contextlost",
     "contextmenu",
     "contextrestored",
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index 72ec867..e646cc3 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -904,21 +904,19 @@
     } else {
       v8::Local<v8::Value> value = exception_.Get(state->GetIsolate());
       exception_.Reset();
-      if (RuntimeEnabledFeatures::ExceptionMetaDataForDevToolsEnabled()) {
-        ThreadDebugger* debugger = ThreadDebugger::From(state->GetIsolate());
-        if (devtools_request_id) {
-          debugger->GetV8Inspector()->associateExceptionData(
-              state->GetContext(), value,
-              V8AtomicString(state->GetIsolate(), "requestId"),
-              V8String(state->GetIsolate(), *devtools_request_id));
-        }
-        if (issue_id) {
-          debugger->GetV8Inspector()->associateExceptionData(
-              state->GetContext(), value,
-              V8AtomicString(state->GetIsolate(), "issueId"),
-              V8String(state->GetIsolate(),
-                       IdentifiersFactory::IdFromToken(*issue_id)));
-        }
+      ThreadDebugger* debugger = ThreadDebugger::From(state->GetIsolate());
+      if (devtools_request_id) {
+        debugger->GetV8Inspector()->associateExceptionData(
+            state->GetContext(), value,
+            V8AtomicString(state->GetIsolate(), "requestId"),
+            V8String(state->GetIsolate(), *devtools_request_id));
+      }
+      if (issue_id) {
+        debugger->GetV8Inspector()->associateExceptionData(
+            state->GetContext(), value,
+            V8AtomicString(state->GetIsolate(), "issueId"),
+            V8String(state->GetIsolate(),
+                     IdentifiersFactory::IdFromToken(*issue_id)));
       }
       resolver_->Reject(value);
     }
diff --git a/third_party/blink/renderer/core/html/custom/custom_state_set.idl b/third_party/blink/renderer/core/html/custom/custom_state_set.idl
index 5d9abe0..e346ba9 100644
--- a/third_party/blink/renderer/core/html/custom/custom_state_set.idl
+++ b/third_party/blink/renderer/core/html/custom/custom_state_set.idl
@@ -4,8 +4,7 @@
 
 // https://wicg.github.io/custom-state-pseudo-class/#customstateset
 [
-  Exposed=Window,
-  RuntimeEnabled=CustomStatePseudoClass
+  Exposed=Window
 ]
 interface CustomStateSet {
   setlike<DOMString>;
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.idl b/third_party/blink/renderer/core/html/custom/element_internals.idl
index 6140f30..c19d0a7 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.idl
+++ b/third_party/blink/renderer/core/html/custom/element_internals.idl
@@ -26,7 +26,7 @@
 
   // Custom state
   // https://wicg.github.io/custom-state-pseudo-class/#dom-elementinternals-states
-  [RuntimeEnabled=CustomStatePseudoClass, MeasureAs=ElementInternalsStates]
+  [MeasureAs=ElementInternalsStates]
   readonly attribute CustomStateSet states;
 
   // Access to shadowRoot from custom elements. See crbug.com/1042130 and
diff --git a/third_party/blink/renderer/core/html/html_attribute_names.json5 b/third_party/blink/renderer/core/html/html_attribute_names.json5
index 47275d1f..057207a 100644
--- a/third_party/blink/renderer/core/html/html_attribute_names.json5
+++ b/third_party/blink/renderer/core/html/html_attribute_names.json5
@@ -175,6 +175,7 @@
     "onchange",
     "onclick",
     "onclose",
+    "oncontentvisibilityautostatechanged",
     "oncontextlost",
     "oncontextmenu",
     "oncontextrestored",
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index 2a7e60a..8448665 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -638,6 +638,9 @@
   if (IsDetached())
     return;
 
+  if (script_runner_)
+    script_runner_->RecordMetricsAtParseEnd();
+
   AttemptToRunDeferredScriptsAndEnd();
 }
 
diff --git a/third_party/blink/renderer/core/page/viewport_description.cc b/third_party/blink/renderer/core/page/viewport_description.cc
index 848c2abb..3bbeee2 100644
--- a/third_party/blink/renderer/core/page/viewport_description.cc
+++ b/third_party/blink/renderer/core/page/viewport_description.cc
@@ -72,10 +72,10 @@
   if (length.IsExtendToZoom())
     return ViewportDescription::kValueExtendToZoom;
 
-  if (length.IsPercent() && direction == kHorizontal)
+  if (length.IsPercent() && direction == Direction::kHorizontal)
     return initial_viewport_size.width() * length.GetFloatValue() / 100.0f;
 
-  if (length.IsPercent() && direction == kVertical)
+  if (length.IsPercent() && direction == Direction::kVertical)
     return initial_viewport_size.height() * length.GetFloatValue() / 100.0f;
 
   if (length.IsDeviceWidth())
@@ -109,16 +109,16 @@
     }
   }
 
-  float result_max_width =
-      ResolveViewportLength(copy_max_width, initial_viewport_size, kHorizontal);
-  float result_min_width =
-      ResolveViewportLength(copy_min_width, initial_viewport_size, kHorizontal);
+  float result_max_width = ResolveViewportLength(
+      copy_max_width, initial_viewport_size, Direction::kHorizontal);
+  float result_min_width = ResolveViewportLength(
+      copy_min_width, initial_viewport_size, Direction::kHorizontal);
 
   float result_height = kValueAuto;
-  float result_max_height =
-      ResolveViewportLength(max_height, initial_viewport_size, kVertical);
-  float result_min_height =
-      ResolveViewportLength(min_height, initial_viewport_size, kVertical);
+  float result_max_height = ResolveViewportLength(
+      max_height, initial_viewport_size, Direction::kVertical);
+  float result_min_height = ResolveViewportLength(
+      min_height, initial_viewport_size, Direction::kVertical);
 
   float result_zoom = zoom;
   float result_min_zoom = min_zoom;
diff --git a/third_party/blink/renderer/core/page/viewport_description.h b/third_party/blink/renderer/core/page/viewport_description.h
index 665400d..be33cd3 100644
--- a/third_party/blink/renderer/core/page/viewport_description.h
+++ b/third_party/blink/renderer/core/page/viewport_description.h
@@ -156,7 +156,7 @@
   void ReportMobilePageStats(const LocalFrame*) const;
 
  private:
-  enum Direction { kHorizontal, kVertical };
+  enum class Direction { kHorizontal, kVertical };
   static float ResolveViewportLength(const Length&,
                                      const gfx::SizeF& initial_viewport_size,
                                      Direction);
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index c273764e..b07ecd2 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -190,7 +190,6 @@
       needs_paint_phase_float_(false),
       has_non_isolated_descendant_with_blend_mode_(false),
       has_fixed_position_descendant_(false),
-      has_sticky_position_descendant_(false),
       has_non_contained_absolute_position_descendant_(false),
       has_stacked_descendant_in_current_stacking_context_(false),
       filter_on_effect_node_dirty_(false),
@@ -472,7 +471,6 @@
     has_visible_self_painting_descendant_ = false;
     has_non_isolated_descendant_with_blend_mode_ = false;
     has_fixed_position_descendant_ = false;
-    has_sticky_position_descendant_ = false;
     has_non_contained_absolute_position_descendant_ = false;
     has_stacked_descendant_in_current_stacking_context_ = false;
     has_self_painting_layer_descendant_ = false;
@@ -507,9 +505,6 @@
       has_fixed_position_descendant_ |=
           child->HasFixedPositionDescendant() ||
           child_style.GetPosition() == EPosition::kFixed;
-      has_sticky_position_descendant_ |=
-          child->HasStickyPositionDescendant() ||
-          child_style.GetPosition() == EPosition::kSticky;
 
       if (!can_contain_abs) {
         has_non_contained_absolute_position_descendant_ |=
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index 1b4da32..be3ffe7 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -513,10 +513,6 @@
     DCHECK(!needs_descendant_dependent_flags_update_);
     return has_fixed_position_descendant_;
   }
-  bool HasStickyPositionDescendant() const {
-    DCHECK(!needs_descendant_dependent_flags_update_);
-    return has_sticky_position_descendant_;
-  }
   bool HasNonContainedAbsolutePositionDescendant() const {
     DCHECK(!needs_descendant_dependent_flags_update_);
     return has_non_contained_absolute_position_descendant_;
@@ -837,7 +833,6 @@
   // inputs.
   unsigned has_non_isolated_descendant_with_blend_mode_ : 1;
   unsigned has_fixed_position_descendant_ : 1;
-  unsigned has_sticky_position_descendant_ : 1;
   unsigned has_non_contained_absolute_position_descendant_ : 1;
   unsigned has_stacked_descendant_in_current_stacking_context_ : 1;
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_test.cc b/third_party/blink/renderer/core/paint/paint_layer_test.cc
index b9aca2c2..488945cab 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -205,26 +205,6 @@
   EXPECT_TRUE(parent->HasVisibleSelfPaintingDescendant());
 }
 
-TEST_P(PaintLayerTest, HasStickyPositionDescendant) {
-  SetBodyInnerHTML(R"HTML(
-    <div id='parent' style='isolation: isolate'>
-      <div id='child' style='position: sticky'>
-      </div>
-    </div>
-  )HTML");
-  PaintLayer* parent = GetPaintLayerByElementId("parent");
-  PaintLayer* child = GetPaintLayerByElementId("child");
-  EXPECT_TRUE(parent->HasStickyPositionDescendant());
-  EXPECT_FALSE(child->HasStickyPositionDescendant());
-
-  GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr,
-                                                      "position: relative");
-  UpdateAllLifecyclePhasesForTest();
-
-  EXPECT_FALSE(parent->HasStickyPositionDescendant());
-  EXPECT_FALSE(child->HasStickyPositionDescendant());
-}
-
 TEST_P(PaintLayerTest, HasFixedPositionDescendant) {
   SetBodyInnerHTML(R"HTML(
     <div id='parent' style='isolation: isolate'>
@@ -245,48 +225,6 @@
   EXPECT_FALSE(child->HasFixedPositionDescendant());
 }
 
-TEST_P(PaintLayerTest, HasFixedAndStickyPositionDescendant) {
-  SetBodyInnerHTML(R"HTML(
-    <div id='parent' style='isolation: isolate'>
-      <div id='child1' style='position: sticky'>
-      </div>
-      <div id='child2' style='position: fixed'>
-      </div>
-    </div>
-  )HTML");
-  PaintLayer* parent = GetPaintLayerByElementId("parent");
-  PaintLayer* child1 = GetPaintLayerByElementId("child1");
-  PaintLayer* child2 = GetPaintLayerByElementId("child2");
-  EXPECT_TRUE(parent->HasFixedPositionDescendant());
-  EXPECT_FALSE(child1->HasFixedPositionDescendant());
-  EXPECT_FALSE(child2->HasFixedPositionDescendant());
-  EXPECT_TRUE(parent->HasStickyPositionDescendant());
-  EXPECT_FALSE(child1->HasStickyPositionDescendant());
-  EXPECT_FALSE(child2->HasStickyPositionDescendant());
-
-  GetDocument().getElementById("child1")->setAttribute(html_names::kStyleAttr,
-                                                       "position: relative");
-  UpdateAllLifecyclePhasesForTest();
-
-  EXPECT_TRUE(parent->HasFixedPositionDescendant());
-  EXPECT_FALSE(child1->HasFixedPositionDescendant());
-  EXPECT_FALSE(child2->HasFixedPositionDescendant());
-  EXPECT_FALSE(parent->HasStickyPositionDescendant());
-  EXPECT_FALSE(child1->HasStickyPositionDescendant());
-  EXPECT_FALSE(child2->HasStickyPositionDescendant());
-
-  GetDocument().getElementById("child2")->setAttribute(html_names::kStyleAttr,
-                                                       "position: relative");
-  UpdateAllLifecyclePhasesForTest();
-
-  EXPECT_FALSE(parent->HasFixedPositionDescendant());
-  EXPECT_FALSE(child1->HasFixedPositionDescendant());
-  EXPECT_FALSE(child2->HasFixedPositionDescendant());
-  EXPECT_FALSE(parent->HasStickyPositionDescendant());
-  EXPECT_FALSE(child1->HasStickyPositionDescendant());
-  EXPECT_FALSE(child2->HasStickyPositionDescendant());
-}
-
 TEST_P(PaintLayerTest, HasNonContainedAbsolutePositionDescendant) {
   SetBodyInnerHTML(R"HTML(
     <div id='parent' style='isolation: isolate'>
diff --git a/third_party/blink/renderer/core/script/html_parser_script_runner.cc b/third_party/blink/renderer/core/script/html_parser_script_runner.cc
index 2006576..16f48d3 100644
--- a/third_party/blink/renderer/core/script/html_parser_script_runner.cc
+++ b/third_party/blink/renderer/core/script/html_parser_script_runner.cc
@@ -27,6 +27,7 @@
 
 #include <inttypes.h>
 #include <memory>
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/dom/document_parser_timing.h"
@@ -36,6 +37,7 @@
 #include "third_party/blink/renderer/core/html/parser/html_input_stream.h"
 #include "third_party/blink/renderer/core/script/html_parser_script_runner_host.h"
 #include "third_party/blink/renderer/core/script/script_loader.h"
+#include "third_party/blink/renderer/core/script/script_runner.h"
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
@@ -142,7 +144,12 @@
     HTMLParserReentryPermit* reentry_permit,
     Document* document,
     HTMLParserScriptRunnerHost* host)
-    : reentry_permit_(reentry_permit), document_(document), host_(host) {
+    : reentry_permit_(reentry_permit),
+      document_(document),
+      host_(host),
+      delayer_for_force_defer_(MakeGarbageCollected<ScriptRunnerDelayer>(
+          document_->GetScriptRunner(),
+          ScriptRunner::DelayReason::kForceDefer)) {
   DCHECK(host_);
 }
 
@@ -156,6 +163,14 @@
     parser_blocking_script_->Dispose();
   parser_blocking_script_ = nullptr;
 
+  while (!force_deferred_scripts_.IsEmpty()) {
+    DCHECK(
+        base::FeatureList::IsEnabled(features::kForceDeferScriptIntervention));
+    PendingScript* pending_script = force_deferred_scripts_.TakeFirst();
+    pending_script->Dispose();
+  }
+  delayer_for_force_defer_->Deactivate();
+
   while (!scripts_to_execute_after_parsing_.IsEmpty()) {
     PendingScript* pending_script =
         scripts_to_execute_after_parsing_.TakeFirst();
@@ -427,21 +442,35 @@
 // <spec step="3">If the list of scripts that will execute when the document has
 // finished parsing is not empty, run these substeps:</spec>
 //
-// This will run the developer deferred scripts.
+// This will also run any forced deferred scripts before running any developer
+// deferred scripts.
 bool HTMLParserScriptRunner::ExecuteScriptsWaitingForParsing() {
   TRACE_EVENT0("blink",
                "HTMLParserScriptRunner::executeScriptsWaitingForParsing");
 
-  while (!scripts_to_execute_after_parsing_.IsEmpty()) {
+  while (!force_deferred_scripts_.IsEmpty() ||
+         !scripts_to_execute_after_parsing_.IsEmpty()) {
     DCHECK(!IsExecutingScript());
     DCHECK(!HasParserBlockingScript());
-    DCHECK(scripts_to_execute_after_parsing_.front()->IsExternalOrModule());
+    DCHECK(scripts_to_execute_after_parsing_.IsEmpty() ||
+           scripts_to_execute_after_parsing_.front()->IsExternalOrModule());
 
     // <spec step="3.3">Remove the first script element from the list of scripts
     // that will execute when the document has finished parsing (i.e. shift out
     // the first entry in the list).</spec>
-    PendingScript* first =
-        TryTakeReadyScriptWaitingForParsing(&scripts_to_execute_after_parsing_);
+    PendingScript* first = nullptr;
+
+    // First execute the scripts that were forced-deferred. If no such scripts
+    // are present, then try executing scripts that were deferred by the web
+    // developer.
+    if (!force_deferred_scripts_.IsEmpty()) {
+      DCHECK(base::FeatureList::IsEnabled(
+          features::kForceDeferScriptIntervention));
+      first = TryTakeReadyScriptWaitingForParsing(&force_deferred_scripts_);
+    } else {
+      first = TryTakeReadyScriptWaitingForParsing(
+          &scripts_to_execute_after_parsing_);
+    }
     if (!first)
       return false;
 
@@ -457,6 +486,12 @@
     // document has finished parsing is still not empty, repeat these substeps
     // again from substep 1.</spec>
   }
+
+  // All scripts waiting for parsing have now executed (end of spec step 3),
+  // including any force deferred syncrhonous scripts. Now resume async
+  // script execution if it was suspended by force deferral.
+  DCHECK(force_deferred_scripts_.IsEmpty());
+  delayer_for_force_defer_->Deactivate();
   return true;
 }
 
@@ -487,6 +522,7 @@
   PendingScript* pending_script =
       script_loader->TakePendingScript(ScriptSchedulingType::kDefer);
 
+  DCHECK(!script_loader->IsForceDeferred());
   DCHECK(pending_script->IsExternalOrModule());
 
   // <spec href="https://html.spec.whatwg.org/C/#prepare-a-script"
@@ -496,6 +532,21 @@
   scripts_to_execute_after_parsing_.push_back(pending_script);
 }
 
+void HTMLParserScriptRunner::RequestForceDeferredScript(
+    ScriptLoader* script_loader) {
+  PendingScript* pending_script =
+      script_loader->TakePendingScript(ScriptSchedulingType::kForceDefer);
+
+  DCHECK(base::FeatureList::IsEnabled(features::kForceDeferScriptIntervention));
+  DCHECK(script_loader->IsForceDeferred());
+
+  // Add the element to the end of the list of forced deferred scripts that will
+  // execute when the document has finished parsing associated with the Document
+  // of the parser that created the element.
+  force_deferred_scripts_.push_back(pending_script);
+  delayer_for_force_defer_->Activate();
+}
+
 // The initial steps for 'An end tag whose tag name is "script"'
 // <specdef href="https://html.spec.whatwg.org/C/#scriptEndTag">
 // <specdef label="prepare-a-script"
@@ -539,6 +590,9 @@
     if (script_loader->WillExecuteWhenDocumentFinishedParsing()) {
       // Developer deferred.
       RequestDeferredScript(script_loader);
+    } else if (script_loader->IsForceDeferred()) {
+      // Force defer this otherwise parser-blocking script.
+      RequestForceDeferredScript(script_loader);
     } else if (script_loader->ReadyToBeParserExecuted()) {
       // <spec label="prepare-a-script" step="26.E">... it's an HTML parser
       // whose script nesting level is not greater than one, ...</spec>
@@ -583,12 +637,44 @@
   }
 }
 
+void HTMLParserScriptRunner::RecordMetricsAtParseEnd() const {
+  // This method is called just before starting execution of force defer
+  // scripts in order to capture the all force deferred scripts in
+  // |force_deferred_scripts_| before any are popped for execution.
+
+  if (!document_->GetFrame())
+    return;
+
+  if (base::FeatureList::IsEnabled(features::kForceDeferScriptIntervention)) {
+    uint32_t force_deferred_external_script_count = 0;
+    for (const auto& pending_script : force_deferred_scripts_) {
+      if (pending_script->IsExternal())
+        force_deferred_external_script_count++;
+    }
+    if (document_->IsInMainFrame()) {
+      UMA_HISTOGRAM_COUNTS_100("Blink.Script.ForceDeferredScripts.Mainframe",
+                               force_deferred_scripts_.size());
+      UMA_HISTOGRAM_COUNTS_100(
+          "Blink.Script.ForceDeferredScripts.Mainframe.External",
+          force_deferred_external_script_count);
+    } else {
+      UMA_HISTOGRAM_COUNTS_100("Blink.Script.ForceDeferredScripts.Subframe",
+                               force_deferred_scripts_.size());
+      UMA_HISTOGRAM_COUNTS_100(
+          "Blink.Script.ForceDeferredScripts.Subframe.External",
+          force_deferred_external_script_count);
+    }
+  }
+}
+
 void HTMLParserScriptRunner::Trace(Visitor* visitor) const {
   visitor->Trace(reentry_permit_);
   visitor->Trace(document_);
   visitor->Trace(host_);
   visitor->Trace(parser_blocking_script_);
+  visitor->Trace(force_deferred_scripts_);
   visitor->Trace(scripts_to_execute_after_parsing_);
+  visitor->Trace(delayer_for_force_defer_);
   PendingScriptClient::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/script/html_parser_script_runner.h b/third_party/blink/renderer/core/script/html_parser_script_runner.h
index e0a1d0d..2266ba5 100644
--- a/third_party/blink/renderer/core/script/html_parser_script_runner.h
+++ b/third_party/blink/renderer/core/script/html_parser_script_runner.h
@@ -42,6 +42,7 @@
 class Element;
 class HTMLParserScriptRunnerHost;
 class ScriptLoader;
+class ScriptRunnerDelayer;
 
 // HTMLParserScriptRunner is responsible for for arranging the execution of
 // script elements inserted by the parser, according to the rules for
@@ -92,7 +93,8 @@
   // to execute parsing-blocking scripts.
   void ExecuteScriptsWaitingForResources();
 
-  // Invoked when parsing is stopping, to execute any developer deferred
+  // Invoked when parsing is stopping, to execute any deferred scripts.
+  // This includes forced deferred scripts as well as developer deferred
   // scripts.
   bool ExecuteScriptsWaitingForParsing();
 
@@ -101,6 +103,10 @@
     return !!reentry_permit_->ScriptNestingLevel();
   }
 
+  // Records metrics related to the parsing phase. To be called when parsing
+  // is preparing to stop but before `ExecuteScriptsWaitingForParsing()`.
+  void RecordMetricsAtParseEnd() const;
+
   void Trace(Visitor*) const override;
   const char* NameInHeapSnapshot() const override {
     return "HTMLParserScriptRunner";
@@ -116,6 +122,7 @@
 
   void RequestParsingBlockingScript(ScriptLoader*);
   void RequestDeferredScript(ScriptLoader*);
+  void RequestForceDeferredScript(ScriptLoader*);
 
   // Processes the provided script element, but does not execute any
   // parsing-blocking scripts that may remain after execution.
@@ -143,9 +150,20 @@
   // https://html.spec.whatwg.org/C/#pending-parsing-blocking-script
   Member<PendingScript> parser_blocking_script_;
 
+  // Scripts that were force deferred by the defer all script optimization.
+  // These scripts will be executed after parsing but before
+  // |scripts_to_execute_after_parsing_|.  This is an ordered list.
+  // https://crbug.com/1339112
+  HeapDeque<Member<PendingScript>> force_deferred_scripts_;
+
   // Scripts that were deferred by the web developer. This is an ordered list.
   // https://html.spec.whatwg.org/C/#list-of-scripts-that-will-execute-when-the-document-has-finished-parsing
   HeapDeque<Member<PendingScript>> scripts_to_execute_after_parsing_;
+
+  // Suspend async script execution while |force_deferred_scripts_| is not empty
+  // in order to let the force deferred scripts execute before any async
+  // scripts.
+  Member<ScriptRunnerDelayer> delayer_for_force_defer_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script/pending_script.cc b/third_party/blink/renderer/core/script/pending_script.cc
index acf09dc..39295140 100644
--- a/third_party/blink/renderer/core/script/pending_script.cc
+++ b/third_party/blink/renderer/core/script/pending_script.cc
@@ -330,10 +330,7 @@
     case ScriptSchedulingType::kParserBlocking:
     case ScriptSchedulingType::kParserBlockingInline:
     case ScriptSchedulingType::kImmediate:
-      return false;
-    case ScriptSchedulingType::kDeprecatedForceDefer:
-      NOTREACHED()
-          << "kDeprecatedForceDefer is deprecated and should not be in use";
+    case ScriptSchedulingType::kForceDefer:
       return false;
 
     case ScriptSchedulingType::kInOrder:
diff --git a/third_party/blink/renderer/core/script/script_loader.cc b/third_party/blink/renderer/core/script/script_loader.cc
index 7a31f050..285db3f 100644
--- a/third_party/blink/renderer/core/script/script_loader.cc
+++ b/third_party/blink/renderer/core/script/script_loader.cc
@@ -216,6 +216,10 @@
   kShouldFire,
 };
 
+bool ShouldForceDeferScript() {
+  return base::FeatureList::IsEnabled(features::kForceDeferScriptIntervention);
+}
+
 }  // namespace
 
 ScriptLoader::ScriptTypeAtPrepare ScriptLoader::GetScriptTypeAtPrepare(
@@ -943,6 +947,24 @@
     return true;
   }
 
+  // Check for external script that should be force deferred.
+  if (GetScriptType() == ScriptTypeAtPrepare::kClassic &&
+      element_->HasSourceAttribute() && ShouldForceDeferScript() &&
+      IsA<HTMLDocument>(context_window->document()) && parser_inserted_ &&
+      !element_->AsyncAttributeValue()) {
+    // In terms of ScriptLoader flags, force deferred scripts behave like
+    // parser-blocking scripts, except that |force_deferred_| is set.
+    // The caller of PrepareScript()
+    // - Force-defers such scripts if the caller supports force-defer
+    //   (i.e., HTMLParserScriptRunner); or
+    // - Ignores the |force_deferred_| flag and handles such scripts as
+    //   parser-blocking scripts (e.g., XMLParserScriptRunner).
+    force_deferred_ = true;
+    will_be_parser_executed_ = true;
+
+    return true;
+  }
+
   // <spec step="26.B">If the script's type is "classic", and the element has a
   // src attribute, and the element has been flagged as "parser-inserted", and
   // the element does not have an async attribute ...</spec>
@@ -1024,6 +1046,14 @@
   DCHECK_EQ(GetScriptType(), ScriptTypeAtPrepare::kClassic);
   DCHECK(!is_external_script_);
 
+  // Check for inline script that should be force deferred.
+  if (ShouldForceDeferScript() &&
+      IsA<HTMLDocument>(context_window->document()) && parser_inserted_) {
+    force_deferred_ = true;
+    will_be_parser_executed_ = true;
+    return true;
+  }
+
   // <spec step="26.E">If the element does not have a src attribute, and the
   // element has been flagged as "parser-inserted", and either the parser that
   // created the script is an XML parser or it's an HTML parser whose script
diff --git a/third_party/blink/renderer/core/script/script_loader.h b/third_party/blink/renderer/core/script/script_loader.h
index ee98491..3a0cf57 100644
--- a/third_party/blink/renderer/core/script/script_loader.h
+++ b/third_party/blink/renderer/core/script/script_loader.h
@@ -88,6 +88,7 @@
   bool WillExecuteWhenDocumentFinishedParsing() const {
     return will_execute_when_document_finished_parsing_;
   }
+  bool IsForceDeferred() const { return force_deferred_; }
   bool IsParserInserted() const { return parser_inserted_; }
   bool AlreadyStarted() const { return already_started_; }
   bool IsNonBlocking() const { return non_blocking_; }
@@ -182,6 +183,9 @@
 
   bool will_execute_when_document_finished_parsing_ = false;
 
+  // The script will be force deferred (https://crbug.com/1339112).
+  bool force_deferred_ = false;
+
   // A PendingScript is first created in PrepareScript() and stored in
   // |prepared_pending_script_|.
   // Later, TakePendingScript() is called, and its caller holds a reference
diff --git a/third_party/blink/renderer/core/script/script_runner.cc b/third_party/blink/renderer/core/script/script_runner.cc
index 6658d7b..0277d1a 100644
--- a/third_party/blink/renderer/core/script/script_runner.cc
+++ b/third_party/blink/renderer/core/script/script_runner.cc
@@ -51,12 +51,40 @@
   DCHECK(document);
 }
 
-void ScriptRunner::QueueScriptForExecution(PendingScript* pending_script) {
+ScriptRunner::DelayReasons ScriptRunner::DetermineDelayReasonsToWait(
+    PendingScript* pending_script) {
+  DelayReasons reasons = static_cast<DelayReasons>(DelayReason::kLoad);
+
+  if (base::FeatureList::IsEnabled(features::kDelayAsyncScriptExecution)) {
+    if (pending_script->IsEligibleForDelay() &&
+        !pending_script->GetElement()->IsPotentiallyRenderBlocking() &&
+        (active_delay_reasons_ &
+         static_cast<DelayReasons>(DelayReason::kMilestone))) {
+      reasons |= static_cast<DelayReasons>(DelayReason::kMilestone);
+    }
+  }
+
+  if (base::FeatureList::IsEnabled(features::kForceDeferScriptIntervention)) {
+    if (active_delay_reasons_ &
+        static_cast<DelayReasons>(DelayReason::kForceDefer)) {
+      reasons |= static_cast<DelayReasons>(DelayReason::kForceDefer);
+    }
+  }
+
+  return reasons;
+}
+
+void ScriptRunner::QueueScriptForExecution(
+    PendingScript* pending_script,
+    absl::optional<DelayReasons> delay_reasons_override_for_test) {
   DCHECK(pending_script);
   document_->IncrementLoadEventDelayCount();
   switch (pending_script->GetSchedulingType()) {
     case ScriptSchedulingType::kAsync:
-      pending_async_scripts_.insert(pending_script);
+      pending_async_scripts_.insert(
+          pending_script, delay_reasons_override_for_test
+                              ? *delay_reasons_override_for_test
+                              : DetermineDelayReasonsToWait(pending_script));
       break;
 
     case ScriptSchedulingType::kInOrder:
@@ -72,43 +100,49 @@
   pending_script->WatchForLoad(this);
 }
 
-void ScriptRunner::NotifyDelayedAsyncScriptsMilestoneReached() {
-  delay_async_script_milestone_reached_ = true;
-  while (!pending_delayed_async_scripts_.IsEmpty()) {
-    PendingScript* pending_script = pending_delayed_async_scripts_.TakeFirst();
-    DCHECK_EQ(pending_script->GetSchedulingType(),
-              ScriptSchedulingType::kAsync);
+void ScriptRunner::AddDelayReason(DelayReason delay_reason) {
+  DCHECK(!(active_delay_reasons_ & static_cast<DelayReasons>(delay_reason)));
+  active_delay_reasons_ |= static_cast<DelayReasons>(delay_reason);
+}
 
-    task_runner_->PostTask(
-        FROM_HERE,
-        WTF::Bind(&ScriptRunner::ExecutePendingScript, WrapWeakPersistent(this),
-                  WrapPersistent(pending_script)));
+void ScriptRunner::RemoveDelayReason(DelayReason delay_reason) {
+  DCHECK(active_delay_reasons_ & static_cast<DelayReasons>(delay_reason));
+  active_delay_reasons_ &= ~static_cast<DelayReasons>(delay_reason);
+
+  HeapVector<Member<PendingScript>> pending_async_scripts;
+  CopyKeysToVector(pending_async_scripts_, pending_async_scripts);
+  for (PendingScript* pending_script : pending_async_scripts) {
+    RemoveDelayReasonFromScript(pending_script, delay_reason);
   }
 }
 
+void ScriptRunner::RemoveDelayReasonFromScript(PendingScript* pending_script,
+                                               DelayReason delay_reason) {
+  auto it = pending_async_scripts_.find(pending_script);
+
+  if (it == pending_async_scripts_.end())
+    return;
+
+  if (it->value &= ~static_cast<DelayReasons>(delay_reason)) {
+    // Still to be delayed.
+    return;
+  }
+
+  // Script is really ready to evaluate.
+  pending_async_scripts_.erase(it);
+  task_runner_->PostTask(
+      FROM_HERE,
+      WTF::Bind(&ScriptRunner::ExecutePendingScript, WrapWeakPersistent(this),
+                WrapPersistent(pending_script)));
+}
+
 void ScriptRunner::PendingScriptFinished(PendingScript* pending_script) {
   pending_script->StopWatchingForLoad();
 
   switch (pending_script->GetSchedulingType()) {
     case ScriptSchedulingType::kAsync:
       CHECK(pending_async_scripts_.Contains(pending_script));
-      pending_async_scripts_.erase(pending_script);
-
-      if (pending_script->IsEligibleForDelay() &&
-          !pending_script->GetElement()->IsPotentiallyRenderBlocking() &&
-          !delay_async_script_milestone_reached_ &&
-          base::FeatureList::IsEnabled(features::kDelayAsyncScriptExecution)) {
-        // When the ScriptRunner is notified via
-        // |NotifyDelayedAsyncScriptsMilestoneReached()|, the scripts in
-        // |pending_delayed_async_scripts_| will be scheduled for execution.
-        pending_delayed_async_scripts_.push_back(pending_script);
-        return;
-      }
-
-      task_runner_->PostTask(
-          FROM_HERE,
-          WTF::Bind(&ScriptRunner::ExecutePendingScript,
-                    WrapWeakPersistent(this), WrapPersistent(pending_script)));
+      RemoveDelayReasonFromScript(pending_script, DelayReason::kLoad);
       break;
 
     case ScriptSchedulingType::kInOrder:
@@ -143,8 +177,31 @@
   visitor->Trace(document_);
   visitor->Trace(pending_in_order_scripts_);
   visitor->Trace(pending_async_scripts_);
-  visitor->Trace(pending_delayed_async_scripts_);
   PendingScriptClient::Trace(visitor);
 }
 
+ScriptRunnerDelayer::ScriptRunnerDelayer(ScriptRunner* script_runner,
+                                         ScriptRunner::DelayReason delay_reason)
+    : script_runner_(script_runner), delay_reason_(delay_reason) {}
+
+void ScriptRunnerDelayer::Activate() {
+  if (activated_)
+    return;
+  activated_ = true;
+  if (script_runner_)
+    script_runner_->AddDelayReason(delay_reason_);
+}
+
+void ScriptRunnerDelayer::Deactivate() {
+  if (!activated_)
+    return;
+  activated_ = false;
+  if (script_runner_)
+    script_runner_->RemoveDelayReason(delay_reason_);
+}
+
+void ScriptRunnerDelayer::Trace(Visitor* visitor) const {
+  visitor->Trace(script_runner_);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script/script_runner.h b/third_party/blink/renderer/core/script/script_runner.h
index b4a7ca73..bed2ef4d 100644
--- a/third_party/blink/renderer/core/script/script_runner.h
+++ b/third_party/blink/renderer/core/script/script_runner.h
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/script/pending_script.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_deque.h"
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
@@ -48,9 +49,40 @@
   ScriptRunner(const ScriptRunner&) = delete;
   ScriptRunner& operator=(const ScriptRunner&) = delete;
 
-  void QueueScriptForExecution(PendingScript*);
+  // Delays script evaluation after `ScriptRunnerDelayer::Activate()` until
+  // `ScriptRunnerDelayer::Deactivate()`.
+  //
+  // Each `DelayReason` value represents one reason to delay, and there should
+  // be at most one active `ScriptRunnerDelayer` for each `ScriptRunnerDelayer`
+  // for each `ScriptRunner`.
+  //
+  // Each script can choose to wait or not to wait for each `DelayReason` (See
+  // `DetermineDelayReasonsToWait()`), and are evaluated after all of its
+  // relevant `ScriptRunnerDelayer`s are deactivated.
+  //
+  // This can be spec-conformant (pretending that the loading of async scripts
+  // are not completed until `ScriptRunnerDelayer`s are deactivated), but be
+  // careful to avoid deadlocks and infinite delays.
+  //
+  // Currently this only affects async scripts and not in-order scripts.
+  enum class DelayReason : uint8_t {
+    // Script is loaded. Should be enabled for all scripts.
+    kLoad = 1 << 0,
+    // Milestone is reached as defined by https://crbug.com/1340837.
+    kMilestone = 1 << 1,
+    // Delay async scripts until force-deferred scripts are evaluated
+    // https://crbug.com/1339112.
+    kForceDefer = 1 << 2,
 
-  void NotifyDelayedAsyncScriptsMilestoneReached();
+    kTest1 = 1 << 6,
+    kTest2 = 1 << 7,
+  };
+  using DelayReasons = std::underlying_type<DelayReason>::type;
+
+  void QueueScriptForExecution(
+      PendingScript*,
+      absl::optional<DelayReasons> delay_reason_override_for_test =
+          absl::nullopt);
 
   void SetTaskRunnerForTesting(base::SingleThreadTaskRunner* task_runner) {
     task_runner_ = task_runner;
@@ -66,23 +98,43 @@
   // Execute the given pending script.
   void ExecutePendingScript(PendingScript*);
 
+  friend class ScriptRunnerDelayer;
+  void AddDelayReason(DelayReason);
+  void RemoveDelayReason(DelayReason);
+  DelayReasons DetermineDelayReasonsToWait(PendingScript*);
+  void RemoveDelayReasonFromScript(PendingScript*, DelayReason);
+
   Member<Document> document_;
 
   // https://html.spec.whatwg.org/C/#list-of-scripts-that-will-execute-in-order-as-soon-as-possible
   HeapDeque<Member<PendingScript>> pending_in_order_scripts_;
   // https://html.spec.whatwg.org/C/#set-of-scripts-that-will-execute-as-soon-as-possible
-  HeapHashSet<Member<PendingScript>> pending_async_scripts_;
-  HeapDeque<Member<PendingScript>> pending_delayed_async_scripts_;
+  // The value represents the `DelayReason`s that the script is waiting for
+  // before its evaluation.
+  HeapHashMap<Member<PendingScript>, DelayReasons> pending_async_scripts_;
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
-  // Scripts in |pending_delayed_async_scripts_| are delayed until the
-  // |NotifyDelayedAsyncScriptsMilestoneReached()| is called. After this point,
-  // the ScriptRunner no longer delays async scripts. This bool is used to
-  // ensure we don't continue delaying async scripts after this point. See the
-  // design doc:
-  // https://docs.google.com/document/u/1/d/1G-IUrT4enARZlsIrFQ4d4cRVe9MRTJASfWwolV09JZE/edit.
-  bool delay_async_script_milestone_reached_ = false;
+  DelayReasons active_delay_reasons_ = 0;
+};
+
+class CORE_EXPORT ScriptRunnerDelayer final
+    : public GarbageCollected<ScriptRunnerDelayer> {
+ public:
+  ScriptRunnerDelayer(ScriptRunner*, ScriptRunner::DelayReason);
+  ~ScriptRunnerDelayer() = default;
+  void Activate();
+  void Deactivate();
+
+  ScriptRunnerDelayer(const ScriptRunnerDelayer&) = delete;
+  ScriptRunnerDelayer& operator=(const ScriptRunnerDelayer&) = delete;
+
+  void Trace(Visitor*) const;
+
+ private:
+  WeakMember<ScriptRunner> script_runner_;
+  const ScriptRunner::DelayReason delay_reason_;
+  bool activated_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script/script_runner_test.cc b/third_party/blink/renderer/core/script/script_runner_test.cc
index 21479dd7..fd0bc544a 100644
--- a/third_party/blink/renderer/core/script/script_runner_test.cc
+++ b/third_party/blink/renderer/core/script/script_runner_test.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/script/script_runner.h"
 
+#include "base/test/scoped_feature_list.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink.h"
@@ -386,6 +387,34 @@
   EXPECT_THAT(order_, WhenSorted(ElementsAre(1, 2, 3)));
 }
 
+TEST_F(ScriptRunnerTest, SetForceDeferredWithAddedAsyncScript) {
+  base::test::ScopedFeatureList feature_list(
+      features::kForceDeferScriptIntervention);
+
+  auto* pending_script1 = MockPendingScript::CreateAsync(document_);
+
+  QueueScriptForExecution(pending_script1);
+  NotifyScriptReady(pending_script1);
+  EXPECT_CALL(*pending_script1, ExecuteScriptBlock(_))
+      .WillOnce(InvokeWithoutArgs([this] { order_.push_back(1); }));
+  auto* delayer = MakeGarbageCollected<ScriptRunnerDelayer>(
+      script_runner_, ScriptRunner::DelayReason::kForceDefer);
+  delayer->Activate();
+
+  // Adding new async script while deferred will cause another task to be
+  // posted for it when execution is unblocked.
+  auto* pending_script2 = MockPendingScript::CreateAsync(document_);
+  QueueScriptForExecution(pending_script2);
+  NotifyScriptReady(pending_script2);
+  EXPECT_CALL(*pending_script2, ExecuteScriptBlock(_))
+      .WillOnce(InvokeWithoutArgs([this] { order_.push_back(2); }));
+  // Unblock async scripts before the tasks posted in NotifyScriptReady() is
+  // executed, i.e. no RunUntilIdle() etc. in between.
+  delayer->Deactivate();
+  platform_->RunUntilIdle();
+  ASSERT_EQ(2u, order_.size());
+}
+
 TEST_F(ScriptRunnerTest, LateNotifications) {
   auto* pending_script1 = MockPendingScript::CreateInOrder(document_);
   auto* pending_script2 = MockPendingScript::CreateInOrder(document_);
@@ -439,4 +468,88 @@
   QueueScriptForExecution(pending_script1);
 }
 
+TEST_F(ScriptRunnerTest, DelayReasons) {
+  // Script waiting only for loading.
+  MockPendingScript* pending_script1 =
+      MockPendingScript::CreateAsync(document_);
+
+  // Script waiting for one additional delay reason.
+  MockPendingScript* pending_script2 =
+      MockPendingScript::CreateAsync(document_);
+
+  // Script waiting for two additional delay reason.
+  MockPendingScript* pending_script3 =
+      MockPendingScript::CreateAsync(document_);
+
+  // Script waiting for an additional delay reason that is removed before load
+  // completion.
+  MockPendingScript* pending_script4 =
+      MockPendingScript::CreateAsync(document_);
+
+  using Checkpoint = testing::StrictMock<testing::MockFunction<void(int)>>;
+  Checkpoint checkpoint;
+  ::testing::InSequence s;
+
+  EXPECT_CALL(checkpoint, Call(1));
+  EXPECT_CALL(*pending_script1, ExecuteScriptBlock(_));
+  EXPECT_CALL(checkpoint, Call(2));
+  EXPECT_CALL(checkpoint, Call(3));
+  EXPECT_CALL(*pending_script2, ExecuteScriptBlock(_));
+  EXPECT_CALL(checkpoint, Call(4));
+  EXPECT_CALL(checkpoint, Call(5));
+  EXPECT_CALL(*pending_script3, ExecuteScriptBlock(_));
+  EXPECT_CALL(checkpoint, Call(6));
+  EXPECT_CALL(checkpoint, Call(7));
+  EXPECT_CALL(*pending_script4, ExecuteScriptBlock(_));
+  EXPECT_CALL(checkpoint, Call(8));
+  EXPECT_CALL(checkpoint, Call(9));
+
+  auto* delayer1 = MakeGarbageCollected<ScriptRunnerDelayer>(
+      script_runner_, ScriptRunner::DelayReason::kTest1);
+  auto* delayer2 = MakeGarbageCollected<ScriptRunnerDelayer>(
+      script_runner_, ScriptRunner::DelayReason::kTest2);
+  delayer1->Activate();
+  delayer1->Activate();
+  delayer2->Activate();
+
+  script_runner_->QueueScriptForExecution(
+      pending_script1, static_cast<int>(ScriptRunner::DelayReason::kLoad));
+  script_runner_->QueueScriptForExecution(
+      pending_script2, static_cast<int>(ScriptRunner::DelayReason::kLoad) |
+                           static_cast<int>(ScriptRunner::DelayReason::kTest1));
+  script_runner_->QueueScriptForExecution(
+      pending_script3, static_cast<int>(ScriptRunner::DelayReason::kLoad) |
+                           static_cast<int>(ScriptRunner::DelayReason::kTest1) |
+                           static_cast<int>(ScriptRunner::DelayReason::kTest2));
+  script_runner_->QueueScriptForExecution(
+      pending_script4, static_cast<int>(ScriptRunner::DelayReason::kLoad) |
+                           static_cast<int>(ScriptRunner::DelayReason::kTest1));
+
+  NotifyScriptReady(pending_script1);
+  NotifyScriptReady(pending_script2);
+  NotifyScriptReady(pending_script3);
+
+  checkpoint.Call(1);
+  platform_->RunUntilIdle();
+  checkpoint.Call(2);
+  delayer1->Deactivate();
+  checkpoint.Call(3);
+  platform_->RunUntilIdle();
+
+  checkpoint.Call(4);
+  delayer2->Deactivate();
+  checkpoint.Call(5);
+  platform_->RunUntilIdle();
+
+  checkpoint.Call(6);
+  NotifyScriptReady(pending_script4);
+  checkpoint.Call(7);
+  platform_->RunUntilIdle();
+
+  checkpoint.Call(8);
+  delayer2->Deactivate();
+  checkpoint.Call(9);
+  platform_->RunUntilIdle();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script/script_scheduling_type.h b/third_party/blink/renderer/core/script/script_scheduling_type.h
index e233e94..6a5a95a 100644
--- a/third_party/blink/renderer/core/script/script_scheduling_type.h
+++ b/third_party/blink/renderer/core/script/script_scheduling_type.h
@@ -69,11 +69,9 @@
   // execute after parsing completes (due to ForceDeferScriptIntervention).
   //
   // Spec: not yet spec'ed. https://crbug.com/976061
-  // kDeprecatedForceDefer is deprecated, but kept here to ensure metrics are
-  // recorded correctly in-order.
-  kDeprecatedForceDefer,
+  kForceDefer,
 
-  kMaxValue = kDeprecatedForceDefer,
+  kMaxValue = kForceDefer,
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc b/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
index 2e331f1..a1c86e14 100644
--- a/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
+++ b/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/platform/geometry/calculation_expression_node.h"
+#include "third_party/blink/renderer/platform/geometry/length_functions.h"
 
 #include "base/notreached.h"
 
@@ -434,8 +435,8 @@
         }
         break;
     }
+    return FloatValueForLength(fallback_, max_value, anchor_evaluator);
   }
-  // TODO(crbug.com/1309178): Support fallback.
   return 0;
 }
 
diff --git a/third_party/blink/renderer/platform/geometry/length_functions.cc b/third_party/blink/renderer/platform/geometry/length_functions.cc
index 66068df..0c937bed 100644
--- a/third_party/blink/renderer/platform/geometry/length_functions.cc
+++ b/third_party/blink/renderer/platform/geometry/length_functions.cc
@@ -35,7 +35,9 @@
   return ValueForLength(length, LayoutUnit(maximum_value)).ToInt();
 }
 
-float FloatValueForLength(const Length& length, float maximum_value) {
+float FloatValueForLength(const Length& length,
+                          float maximum_value,
+                          const Length::AnchorEvaluator* anchor_evaluator) {
   switch (length.GetType()) {
     case Length::kFixed:
       return length.GetFloatValue();
@@ -45,7 +47,8 @@
     case Length::kAuto:
       return static_cast<float>(maximum_value);
     case Length::kCalculated:
-      return length.NonNanCalculatedValue(LayoutUnit(maximum_value));
+      return length.NonNanCalculatedValue(LayoutUnit(maximum_value),
+                                          anchor_evaluator);
     case Length::kMinContent:
     case Length::kMaxContent:
     case Length::kMinIntrinsic:
diff --git a/third_party/blink/renderer/platform/geometry/length_functions.h b/third_party/blink/renderer/platform/geometry/length_functions.h
index 7351f93..4d74019 100644
--- a/third_party/blink/renderer/platform/geometry/length_functions.h
+++ b/third_party/blink/renderer/platform/geometry/length_functions.h
@@ -42,7 +42,10 @@
 struct LengthPoint;
 
 PLATFORM_EXPORT int IntValueForLength(const Length&, int maximum_value);
-PLATFORM_EXPORT float FloatValueForLength(const Length&, float maximum_value);
+PLATFORM_EXPORT float FloatValueForLength(
+    const Length&,
+    float maximum_value,
+    const Length::AnchorEvaluator* anchor_evaluator = nullptr);
 PLATFORM_EXPORT LayoutUnit
 MinimumValueForLengthInternal(const Length&,
                               LayoutUnit maximum_value,
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
index 6bc060a..5d103e7 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
@@ -221,8 +221,7 @@
                                  const PropertyTreeState& guest_state,
                                  bool prefers_lcd_text,
                                  bool dry_run) {
-  if (&Chunks().GetPaintArtifact() != &guest.Chunks().GetPaintArtifact())
-    return false;
+  DCHECK_EQ(&Chunks().GetPaintArtifact(), &guest.Chunks().GetPaintArtifact());
   if (ChunkRequiresOwnLayer() || guest.ChunkRequiresOwnLayer())
     return false;
   if (&GetPropertyTreeState().Effect() != &guest_state.Effect())
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer_test.cc b/third_party/blink/renderer/platform/graphics/compositing/pending_layer_test.cc
index a8f8bea..c9d965d 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer_test.cc
@@ -256,20 +256,6 @@
             pending_layer.RectKnownToBeOpaque());
 }
 
-TEST(PendingLayerTest, CanNotMergeAcrossPaintArtifacts) {
-  TestPaintArtifact test_artifact_a;
-  test_artifact_a.Chunk().RectDrawing(gfx::Rect(0, 0, 100, 100), Color::kWhite);
-  PaintChunkSubset chunks_a(test_artifact_a.Build());
-  PendingLayer layer_a(chunks_a, chunks_a.begin());
-
-  TestPaintArtifact test_artifact_b;
-  test_artifact_b.Chunk().RectDrawing(gfx::Rect(0, 0, 100, 100), Color::kGray);
-  PaintChunkSubset chunks_b(test_artifact_b.Build());
-  PendingLayer layer_b(chunks_b, chunks_b.begin());
-
-  EXPECT_FALSE(layer_a.Merge(layer_b));
-}
-
 class PendingLayerTextOpaquenessTest
     : public testing::Test,
       public testing::WithParamInterface<bool> {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a46138d..84b30a1 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -388,6 +388,10 @@
       status: "experimental",
     },
     {
+      name: "ClipboardUnsanitizedContent",
+      status: "experimental",
+    },
+    {
       name: "CloseWatcher",
       status: "experimental",
     },
@@ -462,6 +466,10 @@
       status:  {"Android": "stable", "default": "experimental"},
     },
     {
+      name: "ContentVisibilityAutoStateChangedEvent",
+      status: "experimental",
+    },
+    {
       name: "ContextMenu",
       status: "experimental",
     },
@@ -782,10 +790,6 @@
       status: "experimental",
     },
     {
-      name: "CustomStatePseudoClass",
-      status: "stable",
-    },
-    {
       name: "Database",
       status: "stable",
     },
@@ -953,10 +957,6 @@
       status: "stable",
     },
     {
-      name: "ExceptionMetaDataForDevTools",
-      status: "stable",
-    },
-    {
       name: "ExperimentalContentSecurityPolicyFeatures",
       status: "experimental",
     },
@@ -1205,6 +1205,7 @@
     },
     {
       name: "HighlightOverlayPainting",
+      status: "stable",
     },
     {
       name: "HrefTranslate",
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 0d77a7a..7da3fcb7 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -650,7 +650,6 @@
 crbug.com/1108830 external/wpt/css/css-pseudo/marker-word-break.html [ Failure ]
 crbug.com/1012289 external/wpt/css/css-pseudo/marker-unicode-bidi-default.html [ Failure ]
 crbug.com/1012289 external/wpt/css/css-pseudo/marker-unicode-bidi-normal.html [ Failure ]
-crbug.com/1136817 external/wpt/css/css-pseudo/target-text-004.html [ Pass ]
 crbug.com/1136817 external/wpt/css/css-pseudo/target-text-007.html [ Failure ]
 crbug.com/1035708 wpt_internal/css/css-pseudo/spelling-error-color-001.html [ Failure ]
 crbug.com/1035708 wpt_internal/css/css-pseudo/spelling-error-color-002.html [ Failure ]
@@ -1801,6 +1800,120 @@
 
 # HighlightOverlayPainting is only implemented in ng
 crbug.com/1147859 virtual/css-highlight-overlay-painting/* [ Skip ]
+crbug.com/1147859 dark-mode/colors/selection.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-001.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-002.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-003.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-005.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-006.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-007.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-008.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-009.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-010.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-011.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-012.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-013.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-014.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-015.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-016.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-017.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-018.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-below-selection.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-001.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-002.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-003.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-004.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-005.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-inheritance-001.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-inheritance-002.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-001.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-003.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-004.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-005.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-006.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-007.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-overlapping-highlights-001.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-overlapping-highlights-002.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-prioritization-001.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-prioritization-002.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-002.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-003.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-shadow.tentative.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-003.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-currentcolor-004.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/highlight-paired-cascade-004.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/target-text-001.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/target-text-002.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/target-text-003.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/target-text-004.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/target-text-005.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/target-text-006.html [ Failure ]
+crbug.com/1147859 external/wpt/css/css-pseudo/target-text-text-decoration-001.html [ Failure ]
+crbug.com/1147859 paint/markers/document-markers-font-8px.html [ Failure ]
+crbug.com/1147859 paint/markers/document-markers-zoom-125.html [ Failure ]
+crbug.com/1147859 paint/markers/document-markers-zoom-150.html [ Failure ]
+crbug.com/1147859 paint/markers/document-markers-zoom-175.html [ Failure ]
+crbug.com/1147859 paint/markers/document-markers-zoom-200.html [ Failure ]
+crbug.com/1147859 paint/markers/document-markers-zoom-2000.html [ Failure ]
+crbug.com/1147859 paint/markers/document-markers-zoom-250.html [ Failure ]
+crbug.com/1147859 paint/markers/document-markers.html [ Failure ]
+crbug.com/1147859 paint/markers/first-letter.html [ Failure ]
+crbug.com/1147859 paint/markers/grammar-markers-hidpi.html [ Failure ]
+crbug.com/1147859 paint/markers/grammar-markers.html [ Failure ]
+crbug.com/1147859 paint/markers/inline-spelling-markers-hidpi-composited.html [ Failure ]
+crbug.com/1147859 paint/markers/inline-spelling-markers-hidpi.html [ Failure ]
+crbug.com/1147859 paint/markers/marker-early-break-bug.html [ Failure ]
+crbug.com/1147859 paint/markers/markers-zoomed.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-001.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-002.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-003.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-005.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-006.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-007.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-008.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-009.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-010.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-011.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-012.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-013.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-014.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-015.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-016.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-017.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-018.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-below-selection.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-001.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-002.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-003.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-004.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-005.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-001.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-003.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-004.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-005.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-006.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-007.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-overlapping-highlights-001.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-overlapping-highlights-002.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-prioritization-001.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-prioritization-002.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-002.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-003.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-shadow.tentative.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-painting-003.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-painting-currentcolor-004.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-paired-cascade-004.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/target-text-001.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/target-text-002.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/target-text-003.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/target-text-004.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/target-text-005.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/target-text-006.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/target-text-text-decoration-001.html [ Failure ]
+crbug.com/1147859 virtual/css-highlight-inheritance/wpt_internal/css/css-pseudo/target-text-002.html [ Failure ]
+crbug.com/1147859 virtual/dark-mode-default/dark-mode/colors/selection.html [ Failure ]
+crbug.com/1147859 virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection.html [ Failure ]
+crbug.com/1147859 wpt_internal/css/css-pseudo/target-text-002.html [ Failure ]
 
 # CSS Anchor Positioning is only implemented in ng.
 crbug.com/1309178 wpt_internal/css/css-anchor-position/* [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index f19f90a7..b1a3941 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1975,14 +1975,10 @@
 crbug.com/1163437 external/wpt/css/css-pseudo/grammar-spelling-errors-002.html [ Failure ]
 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-001.html [ Failure ]
 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-002.html [ Failure ]
-crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-003.html [ Failure ]
 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-004.html [ Failure ]
 crbug.com/1295264 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-currentcolor-001.html [ Failure ]
 crbug.com/1295264 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-currentcolor-002.html [ Failure ]
-crbug.com/1295264 external/wpt/css/css-pseudo/highlight-pseudos-currentcolor-visited-computed-001.html [ Failure ]
-crbug.com/1179938 external/wpt/css/css-pseudo/highlight-painting-currentcolor-003.html [ Failure ]
-crbug.com/1179938 external/wpt/css/css-pseudo/highlight-painting-currentcolor-004.html [ Failure ]
-crbug.com/1179938 external/wpt/css/css-pseudo/highlight-painting-currentcolor-005.html [ Failure ]
+crbug.com/1341621 external/wpt/css/css-pseudo/highlight-pseudos-currentcolor-visited-computed-001.html [ Failure ]
 crbug.com/1024156 external/wpt/css/css-pseudo/highlight-pseudos-inheritance-computed-001.html [ Failure ]
 crbug.com/1024156 external/wpt/css/css-pseudo/highlight-pseudos-currentcolor-inheritance-computed-001.html [ Failure ]
 crbug.com/1024156 external/wpt/css/css-pseudo/highlight-pseudos-currentcolor-inheritance-computed-002.html [ Failure ]
@@ -2003,12 +1999,15 @@
 crbug.com/1035708 wpt_internal/css/css-pseudo/grammar-error-color-dynamic-003.html [ Failure ]
 crbug.com/1035708 wpt_internal/css/css-pseudo/grammar-error-color-dynamic-004.html [ Failure ]
 crbug.com/1147859 external/wpt/css/css-pseudo/target-text-004.html [ Failure ]
-crbug.com/1179938 external/wpt/css/css-pseudo/target-text-006.html [ Failure ]
 crbug.com/1035708 external/wpt/css/css-pseudo/target-text-dynamic-001.html [ Failure ]
 crbug.com/1035708 external/wpt/css/css-pseudo/target-text-dynamic-002.html [ Failure ]
 crbug.com/1035708 external/wpt/css/css-pseudo/target-text-dynamic-003.html [ Failure ]
 crbug.com/1035708 external/wpt/css/css-pseudo/target-text-dynamic-004.html [ Failure ]
-crbug.com/1179938 external/wpt/css/css-pseudo/target-text-text-decoration-001.html [ Failure ]
+
+# CSS highlight painting (HighlightOverlayPainting)
+crbug.com/1147859 [ Mac ] external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001.html [ Failure ]
+crbug.com/1147859 [ Mac ] external/wpt/css/css-pseudo/highlight-painting-003.html [ Failure ]
+crbug.com/1321114 paint/markers/suggestion-marker-basic.html [ Failure ]
 
 crbug.com/1205953 external/wpt/css/css-will-change/will-change-fixpos-cb-position-1.html [ Failure ]
 crbug.com/1207788 external/wpt/css/css-will-change/will-change-stacking-context-mask-1.html [ Failure ]
@@ -3311,73 +3310,6 @@
 crbug.com/1035708 virtual/css-highlight-inheritance/wpt_internal/css/css-pseudo/spelling-error-color-dynamic-003.html [ Pass ]
 crbug.com/1035708 virtual/css-highlight-inheritance/wpt_internal/css/css-pseudo/spelling-error-color-dynamic-004.html [ Pass ]
 
-# virtual/css-highlight-overlay-painting/
-crbug.com/1295264 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-overlapping-highlights-001.html [ Failure ]
-crbug.com/1295264 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-overlapping-highlights-002.html [ Failure ]
-crbug.com/1147859 [ Mac ] virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001.html [ Failure ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/grammar-error-color-001.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/grammar-error-color-002.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/spelling-error-color-001.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/spelling-error-color-002.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-001.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-002.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-003.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-005.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-006.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-007.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-008.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-009.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-010.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-011.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-012.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-013.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-014.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-015.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-016.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-017.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-018.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-below-selection.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-001.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-002.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-003.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-004.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-005.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-iframe-006.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-inheritance-001.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-inheritance-002.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-inheritance-003.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-001.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-002.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-003.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-004.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-005.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-006.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-invalidation-007.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-prioritization-001.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-prioritization-002.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-001.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-002.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-003.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-004.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-005.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-006.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-staticrange-007.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-shadow.tentative.html [ Pass ]
-crbug.com/1147859 [ Mac11 ] virtual/css-highlight-overlay-painting/external/wpt/css/css-pseudo/highlight-painting-003.html [ Failure ]
-crbug.com/1147859 [ Win ] virtual/css-highlight-overlay-painting/external/wpt/css/css-pseudo/highlight-painting-003.html [ Pass ]
-crbug.com/1147859 [ Linux ] virtual/css-highlight-overlay-painting/external/wpt/css/css-pseudo/highlight-painting-003.html [ Pass ]
-crbug.com/1147859 [ Fuchsia ] virtual/css-highlight-overlay-painting/external/wpt/css/css-pseudo/highlight-painting-003.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-pseudo/highlight-painting-currentcolor-003.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-pseudo/highlight-painting-currentcolor-004.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-pseudo/target-text-006.html [ Pass ]
-crbug.com/1147859 virtual/css-highlight-overlay-painting/external/wpt/css/css-pseudo/target-text-text-decoration-001.html [ Pass ]
-crbug.com/1251193 crbug.com/1024156 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/grammar-error-color-dynamic-001.html [ Failure ]
-crbug.com/1251193 crbug.com/1024156 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/grammar-error-color-dynamic-003.html [ Failure ]
-crbug.com/1251193 crbug.com/1024156 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/grammar-error-color-dynamic-004.html [ Failure ]
-crbug.com/1251193 crbug.com/1024156 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/spelling-error-color-dynamic-001.html [ Failure ]
-crbug.com/1251193 crbug.com/1024156 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/spelling-error-color-dynamic-003.html [ Failure ]
-crbug.com/1251193 crbug.com/1024156 virtual/css-highlight-overlay-painting/wpt_internal/css/css-pseudo/spelling-error-color-dynamic-004.html [ Failure ]
-
 crbug.com/825270 external/wpt/payment-request/delegate-request.https.sub.html [ Failure ]
 crbug.com/1105958 external/wpt/payment-request/payment-is-showing.https.html [ Failure Timeout ]
 
@@ -3478,6 +3410,7 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/mediaqueries/prefers-color-scheme-svg-image.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-images/gradient/gradient-eval-001.tentative.html [ Failure ]
 crbug.com/626703 [ Mac12 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/paint2d-composite.https.html [ Failure ]
 crbug.com/626703 [ Mac11 ] external/wpt/html/browsers/origin/cross-origin-objects/location-properties-smoke-test.html [ Timeout ]
@@ -6104,10 +6037,7 @@
 crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-004.html [ Failure ]
 crbug.com/1163437 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-below-grammar.html [ Failure ]
 crbug.com/1147859 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-below-target-text.html [ Failure ]
-crbug.com/1274174 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-001.html [ Pass ]
-crbug.com/1274174 external/wpt/css/css-highlight-api/painting/custom-highlight-painting-text-decoration-dynamic-001.html [ Pass ]
-crbug.com/1147859 [ Linux ] external/wpt/css/css-highlight-api/painting/* [ Failure ]
-crbug.com/1147859 [ Linux ] virtual/css-highlight-inheritance/external/wpt/css/css-highlight-api/painting/* [ Failure ]
+crbug.com/1147859 [ Linux ] external/wpt/css/css-highlight-api/painting/custom-highlight-painting-004-2.html [ Failure ]
 crbug.com/1147859 [ Linux ] paint/custom-highlight-only-inheritance.html [ Failure ]
 crbug.com/1147859 [ Linux ] virtual/forced-high-contrast-colors/external/wpt/forced-colors-mode/forced-colors-mode-53.html [ Failure ]
 crbug.com/1147859 [ Mac ] virtual/forced-high-contrast-colors/external/wpt/forced-colors-mode/forced-colors-mode-53.html [ Failure ]
@@ -6325,7 +6255,6 @@
 # Failures that surfaced on the mac12-arm64 waterfall
 crbug.com/1249176 [ Mac12-arm64 ] media/color-profile-video-seek.html [ Failure ]
 crbug.com/1249176 [ Mac12-arm64 ] media/video-zoom.html [ Failure ]
-crbug.com/1249176 [ Mac12-arm64 ] paint/markers/suggestion-marker-basic.html [ Failure ]
 crbug.com/1249176 [ Mac12-arm64 ] virtual/exotic-color-space/images/color-profile-background-image-cover.html [ Failure ]
 crbug.com/1249176 [ Mac12-arm64 ] virtual/exotic-color-space/images/color-profile-border-image.html [ Failure ]
 crbug.com/1249176 [ Mac12-arm64 ] virtual/exotic-color-space/images/color-profile-image-canvas-svg.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 34da545..eddd5e1 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1286,6 +1286,12 @@
     ]
   },
   {
+    "prefix": "force-defer-script",
+    "platforms": ["Linux", "Mac", "Win"],
+    "bases": ["external/wpt/html/semantics/scripting-1/the-script-element/defer-script/"],
+    "args": ["--enable-features=ForceDeferScriptIntervention"]
+  },
+  {
     "prefix": "threaded-preload-scanner",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 983f617..e208cc0 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 961c5a18f3550b830df68c87221f5200bbebe821
+Version: 926d343abc5c60def07579e476694b87f0ce7dd6
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 da81015..cac0196 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
@@ -227653,6 +227653,19 @@
        {}
       ]
      ],
+     "prefers-color-scheme-svg-image.html": [
+      "ce7babe6318f4456709dae0925064c6b8e55296d",
+      [
+       null,
+       [
+        [
+         "/css/mediaqueries/prefers-color-scheme-svg-image-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "relative-units-001.html": [
       "4e526fbf2ef7e4c3eca48862cd692bff99acd88d",
       [
@@ -297191,6 +297204,10 @@
       "f277097263e7acf6f2e7b40fa810231c61b00e63",
       []
      ],
+     "prefers-color-scheme-svg-image-ref.html": [
+      "7da496cf8831819c0e75d73f07004e8d96e5b65a",
+      []
+     ],
      "reference": {
       "ref-green-body.xht": [
        "f79255de9cb1af511c23247de02918844a9afcc4",
@@ -297201,6 +297218,18 @@
       "matchmedia-utils.js": [
        "49ce0bb42a30eda2d1debcacaaaa9342588a83c3",
        []
+      ],
+      "prefers-color-scheme-dark.svg": [
+       "f65fce76ecbd949bd53e64e17ab2c41ded828857",
+       []
+      ],
+      "prefers-color-scheme-light.svg": [
+       "23ac2ad949605ee03bcb92265ffafae1803c357f",
+       []
+      ],
+      "prefers-color-scheme.svg": [
+       "5523ff39fa4983e4cf42d0c6f4cb1ac8cef3fe9d",
+       []
       ]
      },
      "support": {
@@ -306924,6 +306953,10 @@
           []
          ]
         },
+        "serialize-worker.js": [
+         "a76537cc9e225cb220d07fe5b88477b17a38d763",
+         []
+        ],
         "transfer-worker.js": [
          "55465a899ca27d5202dfa796ec5200bde845892b",
          []
@@ -366526,7 +366559,7 @@
       ]
      ],
      "iframe-srcdoc-history-inheritance.html": [
-      "91592252e2efc64614d8a1b90290d3ec07a6e49a",
+      "52d460dc92cd0f9fd58cd727449706d0962a9930",
       [
        null,
        {}
@@ -375251,6 +375284,13 @@
         null,
         {}
        ]
+      ],
+      "supports-conditionText.html": [
+       "ea74077dfd0070edd9e8ae4f2ab092bf369ce268",
+       [
+        null,
+        {}
+       ]
       ]
      }
     },
@@ -385426,7 +385466,16 @@
         "timeout": "long"
        }
       ]
-     ]
+     ],
+     "parsing": {
+      "paint-function-valid.https.html": [
+       "187bd9e94c5a69f32a84cdd0f6d759f34e313a3c",
+       [
+        null,
+        {}
+       ]
+      ]
+     }
     },
     "css-parser-api": {
      "idlharness.html": [
@@ -446315,6 +446364,13 @@
           null,
           {}
          ]
+        ],
+        "imagebitmap-replication-exif-orientation.html": [
+         "b04a76121effe42a2166eed421d17102913d816d",
+         [
+          null,
+          {}
+         ]
         ]
        },
        "line-styles": {
@@ -526206,6 +526262,15 @@
        }
       ]
      ],
+     "focus-click-on-shadow-host.html": [
+      "7a318a0200e7405b4793f2705434bef948aea61f",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
      "focus-method-delegatesFocus-nested-browsing-context.html": [
       "d2724f17d52b93912544cda8a44aae7017918ac9",
       [
@@ -526239,7 +526304,7 @@
       ]
      ],
      "focus-tab-on-shadow-host.html": [
-      "354ed0ed941d96219dc54e3d16c276662723ac64",
+      "0dffc0157f53d7178ebb0ff04b3b09977642b280",
       [
        null,
        {
@@ -535233,6 +535298,13 @@
         {}
        ]
       ],
+      "shapes-clip-path.svg": [
+       "a8277226a5ba542ce408d64a50e319b7d44a7dcd",
+       [
+        null,
+        {}
+       ]
+      ],
       "stroke-dashes-hit-at-high-scale.svg": [
        "b57a9e0aa7714e7a821c74d24f6578ab7d6286b3",
        [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-auto-state-changed.html b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-auto-state-changed.html
new file mode 100644
index 0000000..d1076459
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-auto-state-changed.html
@@ -0,0 +1,63 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Content Visibility: ContentVisibilityAutoStateChanged event.</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility">
+<meta name="timeout" content="long">
+<meta name="assert" content="ContentVisibilityAutoStateChanged fires when things enter/exit viewport">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/scroll-to-text-fragment/stash.js"></script>
+
+<style>
+.spacer {
+  height: 3000px;
+}
+.auto { content-visibility: auto; }
+</style>
+
+<div id=top class=auto></div>
+<div class=spacer></div>
+<div id=middle></div>
+<div class=spacer></div>
+<div id=bottom class=auto></div>
+
+<script>
+promise_test(t => new Promise(async (resolve, reject) => {
+  await new Promise((waited, _) => {
+    requestAnimationFrame(() => requestAnimationFrame(waited));
+  });
+
+  top.addEventListener("contentvisibilityautostatechanged", (e) => {
+    if (e.skipped)
+      resolve();
+    else
+      reject();
+  });
+  requestAnimationFrame(() => requestAnimationFrame(() => {
+    middle.scrollIntoView();
+  }));
+}), "ContentVisibilityAutoStateChanged fires when skipped");
+
+promise_test(t => new Promise(async (resolve, reject) => {
+  await new Promise((waited, _) => {
+    requestAnimationFrame(() => requestAnimationFrame(waited));
+  });
+
+  bottom.addEventListener("contentvisibilityautostatechanged", (e) => {
+    if (!e.skipped)
+      resolve();
+    else
+      reject();
+  });
+  requestAnimationFrame(() => requestAnimationFrame(() => {
+    bottom.scrollIntoView();
+  }));
+}), "ContentVisibilityAutoStateChanged fires when not skipped");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001-ref.html
index b15f74c..0ee37b1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001-ref.html
@@ -4,6 +4,8 @@
 <style>
   div {
     text-decoration: solid underline magenta;
+    color: initial;
+    background: transparent;
   }
 </style>
 <p>The test passes if the following line has a magenta underline.</p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001.html
index 1709ce70..d90d5d9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-text-decoration-001.html
@@ -7,6 +7,8 @@
 <style>
   ::target-text {
     text-decoration: solid underline magenta;
+    color: initial;
+    background: transparent;
   }
 </style>
 <p>The test passes if the following line has a magenta underline.</p>
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/prefers-color-scheme-svg-image-ref.html b/third_party/blink/web_tests/external/wpt/css/mediaqueries/prefers-color-scheme-svg-image-ref.html
new file mode 100644
index 0000000..7da496c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/mediaqueries/prefers-color-scheme-svg-image-ref.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test Reference</title>
+<style>
+  .background {
+    width: 32px;
+    height: 32px;
+  }
+</style>
+<div style="color-scheme: light">
+  <img src="resources/prefers-color-scheme-light.svg">
+  <div class="background" style="background-image: url(resources/prefers-color-scheme-light.svg)"></div>
+</div>
+<div style="color-scheme: dark">
+  <img src="resources/prefers-color-scheme-dark.svg">
+  <div class="background" style="background-image: url(resources/prefers-color-scheme-dark.svg)"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/prefers-color-scheme-svg-image.html b/third_party/blink/web_tests/external/wpt/css/mediaqueries/prefers-color-scheme-svg-image.html
new file mode 100644
index 0000000..ce7babe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/mediaqueries/prefers-color-scheme-svg-image.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS prefers-color-scheme affects SVG images</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7213">
+<link rel="match" href="prefers-color-scheme-svg-image-ref.html">
+<style>
+  .background {
+    width: 32px;
+    height: 32px;
+    background-image: url(resources/prefers-color-scheme.svg);
+  }
+</style>
+<div style="color-scheme: light">
+  <img src="resources/prefers-color-scheme.svg">
+  <div class="background"></div>
+</div>
+<div style="color-scheme: dark">
+  <img src="resources/prefers-color-scheme.svg">
+  <div class="background"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/resources/prefers-color-scheme-dark.svg b/third_party/blink/web_tests/external/wpt/css/mediaqueries/resources/prefers-color-scheme-dark.svg
new file mode 100644
index 0000000..f65fce7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/mediaqueries/resources/prefers-color-scheme-dark.svg
@@ -0,0 +1,6 @@
+<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    :root { color: purple }
+  </style>
+  <rect fill="currentColor" width="32" height="32"/>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/resources/prefers-color-scheme-light.svg b/third_party/blink/web_tests/external/wpt/css/mediaqueries/resources/prefers-color-scheme-light.svg
new file mode 100644
index 0000000..23ac2ad9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/mediaqueries/resources/prefers-color-scheme-light.svg
@@ -0,0 +1,6 @@
+<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    :root { color: blue }
+  </style>
+  <rect fill="currentColor" width="32" height="32"/>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/resources/prefers-color-scheme.svg b/third_party/blink/web_tests/external/wpt/css/mediaqueries/resources/prefers-color-scheme.svg
new file mode 100644
index 0000000..5523ff39
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/mediaqueries/resources/prefers-color-scheme.svg
@@ -0,0 +1,9 @@
+<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    :root { color: blue }
+    @media (prefers-color-scheme: dark) {
+      :root { color: purple }
+    }
+  </style>
+  <rect fill="currentColor" width="32" height="32"/>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html
new file mode 100644
index 0000000..f74bbfd7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+async_test(t => {
+  const crossOriginUrl = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+  crossOriginUrl.pathname = "/common/blank.html";
+  const i = document.createElement("iframe");
+  i.src = crossOriginUrl;
+  document.body.appendChild(i);
+
+  let wasLoadEventFired = false;
+  i.onload = t.step_func(() => {
+    // Though iframe is cross-origin and changing hash leads soft reload, the
+    // load event should be fired to protect sensitive information.
+    // See: https://crbug.com/1248444
+    crossOriginUrl.hash = "#foo";
+    i.onload = () => {
+      assert_false(wasLoadEventFired)
+      wasLoadEventFired = true;
+      // Wait for a while to ensure other onload events are never fired.
+      t.step_timeout(() => t.done(), 100);
+    };
+    i.src = crossOriginUrl;
+  });
+
+}, "Changing the URL hash of a cross-origin iframe should fire a load event");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/README.md b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/README.md
new file mode 100644
index 0000000..ac5c91c9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/README.md
@@ -0,0 +1,7 @@
+The tests in this directory is intended for Chromium's DeferAllScript
+experiment https://crbug.com/1339112, containing scenarios that would be
+affected by DeferAllScript, to monitor the behavior on Chromium and other
+browsers.
+
+The same set of expectations (when async/defer scripts are evaluated) should
+already be covered somewhere in WPT.
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-2.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-2.html
new file mode 100644
index 0000000..f7377d8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-2.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>Async Script Execution Order</title>
+<html>
+<head>
+    <meta charset="utf-8">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/helper.js"></script>
+</head>
+<body>
+  <script>
+  setup({single_test: true});
+  function finish() {
+    assert_array_equals(
+        window.result,
+        ["Inline1", "Sync1", "Async1", "Sync2", "EndOfBody",
+         "DOMContentLoaded"],
+        "Execution order");
+    // Chromium's force-defer order would be:
+    //  ["EndOfBody", "Inline1", "Sync1", "Sync2",
+    //   "DOMContentLoaded", "Async1"]
+    //
+    // If we delay async script e.g. after DOMContentLoaded,
+    // the order would be:
+    // ["Inline1", "Sync1", "Sync2", "EndOfBody",
+    //  "DOMContentLoaded", "Async1"]
+    done();
+  }
+  logScript("Inline1");
+  window.addEventListener("load", finish);
+  </script>
+  <script src="resources/sync-script-1.js"></script>
+  <!-- To test the async script loaded before force-deferred scripts
+       should be evaluated after the force-deferred scripts
+       in Chromium's force-defer order. -->
+  <script src="resources/async-script-1.js?pipe=trickle(d1)" async></script>
+  <script src="resources/sync-script-2.js?pipe=trickle(d2)"></script>
+  <pre id="bodyend">End</pre>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script.html
new file mode 100644
index 0000000..ea6c546
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>Async Script Execution Order</title>
+<html>
+<head>
+    <meta charset="utf-8">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/helper.js"></script>
+</head>
+<body>
+  <script>
+  setup({single_test: true});
+  function finish() {
+    assert_array_equals(
+        window.result,
+        ["Inline1", "Sync1", "EndOfBody", "DOMContentLoaded", "Async1"],
+        "Execution order");
+    // Chromium's force-defer order would be:
+    //  ["EndOfBody", "Inline1", "Sync1", "DOMContentLoaded", "Async1"]
+    done();
+  }
+  logScript("Inline1");
+  window.addEventListener("load", finish);
+  </script>
+  <script src="resources/sync-script-1.js"></script>
+  <!-- Delays are added to make DOMContentLoaded be fired before
+       async script load completion -->
+  <script src="resources/async-script-1.js?pipe=trickle(d1)" async></script>
+  <pre id="bodyend">End</pre>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml.xhtml b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml.xhtml
new file mode 100644
index 0000000..9d02ff3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml.xhtml
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Defer Script Execution Order</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="resources/helper.js"></script>
+</head>
+<body>
+  <div id="scriptlog"/>
+  <input id="testElement"/>
+  <script>
+  setup({single_test: true});
+  function finish() {
+    assert_array_equals(
+        window.result,
+        ["Inline1", "Sync1", "Inline2", "Sync2", "EndOfBody",
+         "Defer1", "Defer2", "DOMContentLoaded"],
+        "Execution order");
+    // Chromium order is (due to https://crbug.com/874749):
+    //  ["Inline1", "Sync1", "Defer1", "Inline2", "Defer2", "Sync2",
+    //   "EndOfBody", "DOMContentLoaded"]
+    done();
+  }
+  logScript("Inline1");
+  window.addEventListener("load", finish);
+  </script>
+
+  <script src="resources/sync-script-1.js"></script>
+  <script src="resources/defer-script-1.js" defer="defer"></script>
+  <script>
+  logScript("Inline2");
+  </script>
+  <script src="resources/defer-script-2.js" defer="defer"></script>
+  <script src="resources/sync-script-2.js"></script>
+  <pre id="bodyend">End</pre>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script.html
new file mode 100644
index 0000000..62c3a74
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>Defer Script Execution Order</title>
+<html>
+<head>
+    <meta charset="utf-8">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/helper.js"></script>
+</head>
+<body>
+  <script>
+  setup({single_test: true});
+  function finish() {
+    assert_array_equals(
+        window.result,
+        ["Inline1", "Sync1", "Inline2", "Sync2", "EndOfBody",
+         "Defer1", "Defer2", "DOMContentLoaded"],
+        "Execution order");
+    // Chromium's force defer order would be:
+    //  ["EndOfBody", "Inline1", "Sync1", "Inline2", "Sync2",
+    //   "Defer1", "Defer2", "DOMContentLoaded"]
+    done();
+  }
+  logScript("Inline1");
+  window.addEventListener("load", finish);
+  </script>
+
+  <script src="resources/sync-script-1.js"></script>
+  <script src="resources/defer-script-1.js" defer></script>
+  <script>
+  logScript("Inline2");
+  </script>
+  <script src="resources/defer-script-2.js" defer></script>
+  <script src="resources/sync-script-2.js"></script>
+  <pre id="bodyend">End</pre>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write.html
new file mode 100644
index 0000000..63e251bae
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<title>DeferAllScript: document.write()</title>
+<html>
+<head>
+    <meta charset="utf-8">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script>
+    const t1 = async_test("document.write()");
+    const t2 = async_test("document.write(),close()");
+    const t3 = async_test("document.open(),write()");
+    const t4 = async_test("document.open(),write(),close()");
+    function finish() {
+      const expected = ["Inline1", "Sync2", "Async1", "Sync1",
+                        "EndOfBody", "DOMContentLoaded", "WindowLoad"];
+      t1.step_func_done(() => {
+        assert_array_equals(
+          document.getElementById("document-write").contentWindow.result,
+          expected,
+          "Execution order");
+      })();
+
+      t2.step_func_done(() => {
+        assert_array_equals(
+          document.getElementById("document-write-close").contentWindow.result,
+          expected,
+          "Execution order");
+      })();
+
+      t3.step_func_done(() => {
+        assert_array_equals(
+          document.getElementById("document-open-write").contentWindow.result,
+          expected,
+          "Execution order");
+      })();
+
+      t4.step_func_done(() => {
+        assert_array_equals(
+          document.getElementById(
+              "document-open-write-close").contentWindow.result,
+          expected,
+          "Execution order");
+      })();
+      // For cases where documents are kept open, call `document.close()` here
+      // to finish the test harness.
+      for (const iframe of document.querySelectorAll("iframe")) {
+        iframe.contentDocument.close();
+      }
+    }
+
+    // For cases where documents are kept open (that should never occur in
+    // non-intervention cases), schedule `finish()` because Window load events
+    // might be not fired.
+    setTimeout(finish, 5000);
+    </script>
+</head>
+<body onload="finish()">
+<iframe id="document-write"
+        src="resources/document-write-iframe.sub.html?script=document-write.js"></iframe>
+<iframe id="document-write-close"
+        src="resources/document-write-iframe.sub.html?script=document-write-close.js"></iframe>
+<iframe id="document-open-write"
+        src="resources/document-write-iframe.sub.html?script=document-open-write.js"></iframe>
+<iframe id="document-open-write-close"
+        src="resources/document-write-iframe.sub.html?script=document-open-write-close.js"></iframe>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/async-script-1.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/async-script-1.js
new file mode 100644
index 0000000..267f324
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/async-script-1.js
@@ -0,0 +1 @@
+logScript("Async1");
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-1.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-1.js
new file mode 100644
index 0000000..1a0524f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-1.js
@@ -0,0 +1 @@
+logScript("Defer1");
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-2.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-2.js
new file mode 100644
index 0000000..d644e37
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/defer-script-2.js
@@ -0,0 +1 @@
+logScript("Defer2");
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write-close.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write-close.js
new file mode 100644
index 0000000..80703d5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write-close.js
@@ -0,0 +1,3 @@
+document.open();
+document.write(`<script src="sync-script-2.js"></script>`);
+document.close();
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write.js
new file mode 100644
index 0000000..178c374
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-open-write.js
@@ -0,0 +1,2 @@
+document.open();
+document.write(`<script src="sync-script-2.js"></script>`);
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-close.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-close.js
new file mode 100644
index 0000000..7cdde0d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-close.js
@@ -0,0 +1,2 @@
+document.write(`<script src="sync-script-2.js"></script>`);
+document.close();
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-iframe.sub.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-iframe.sub.html
new file mode 100644
index 0000000..e3022e3b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write-iframe.sub.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<body>
+<script src="helper.js"></script>
+<script>
+logScript("Inline1");
+window.addEventListener("load", () => logScript("WindowLoad"));
+</script>
+<script src="{{GET[script]}}?pipe=trickle(d1)"></script>
+<script src="async-script-1.js" async></script>
+<script src="sync-script-1.js?pipe=trickle(d2)"></script>
+<pre id="bodyend">End</pre>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write.js
new file mode 100644
index 0000000..413a9bc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/document-write.js
@@ -0,0 +1 @@
+document.write(`<script src="sync-script-2.js"></script>`);
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/helper.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/helper.js
new file mode 100644
index 0000000..89c6d1e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/helper.js
@@ -0,0 +1,17 @@
+window.result = [];
+function log(msg) {
+  window.result.push(msg);
+}
+function checkIfReachedBodyEnd() {
+  const endelement = document.getElementById("bodyend");
+  // `<pre id="bodyend">End</pre>` is needed at the end of HTML.
+  if (endelement && endelement.textContent === "End") {
+    log("EndOfBody");
+    endelement.textContent = "Detected";
+  }
+}
+function logScript(msg) {
+  checkIfReachedBodyEnd();
+  log(msg);
+}
+document.addEventListener("DOMContentLoaded", function() { logScript("DOMContentLoaded"); });
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-1.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-1.js
new file mode 100644
index 0000000..726b563
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-1.js
@@ -0,0 +1 @@
+logScript("Sync1");
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-2.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-2.js
new file mode 100644
index 0000000..ba2edfb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/resources/sync-script-2.js
@@ -0,0 +1 @@
+logScript("Sync2");
diff --git a/third_party/blink/web_tests/external/wpt/svg/shapes/scripted/shapes-clip-path.svg b/third_party/blink/web_tests/external/wpt/svg/shapes/scripted/shapes-clip-path.svg
new file mode 100644
index 0000000..a827722
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/shapes/scripted/shapes-clip-path.svg
@@ -0,0 +1,78 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:h="http://www.w3.org/1999/xhtml">
+  <title>Clipped shapes can be hit when rendered</title>
+  <h:script src="/resources/testharness.js"/>
+  <h:script src="/resources/testharnessreport.js"/>
+  <metadata>
+    <h:link rel="help" href="https://svgwg.org/svg2-draft/shapes.html#RectElement"/>
+    <h:link rel="help" href="https://svgwg.org/svg2-draft/shapes.html#CircleElement"/>
+    <h:link rel="help" href="https://svgwg.org/svg2-draft/shapes.html#EllipseElement"/>
+  </metadata>
+  <defs>
+    <clipPath id="clip1">
+      <circle cx="100" cy="100" r="25"/>
+    </clipPath>
+    <clipPath id="clip2">
+      <circle cx="150" cy="150" r="25"/>
+    </clipPath>
+    <clipPath id="clip3">
+      <circle cx="250" cy="150" r="25"/>
+    </clipPath>
+    <clipPath id="clip4">
+      <circle cx="350" cy="150" r="25"/>
+    </clipPath>
+  </defs>
+  <g>
+    <circle id="circle1" cx="100" cy="100" r="25"/>
+    <circle id="circle2" cx="150" cy="100" r="25" clip-path="url(#unknown)"/>
+    <circle id="circle3" cx="100" cy="150" r="25" clip-path="url(#clip1)"/>
+    <circle id="circle4" cx="150" cy="150" r="25" clip-path="url(#clip2)"/>
+  </g>
+  <g>
+    <ellipse id="ellipse1" cx="200" cy="100" rx="25" ry="15"/>
+    <ellipse id="ellipse2" cx="250" cy="100" rx="25" ry="15" clip-path="url(#unknown)"/>
+    <ellipse id="ellipse3" cx="200" cy="150" rx="25" ry="15" clip-path="url(#clip1)"/>
+    <ellipse id="ellipse4" cx="250" cy="150" rx="25" ry="15" clip-path="url(#clip3)"/>
+  </g>
+  <g>
+    <rect id="rect1" x="285" y="85" width="30" height="30"/>
+    <rect id="rect2" x="335" y="85" width="30" height="30" clip-path="url(#unknown)"/>
+    <rect id="rect3" x="285" y="135" width="30" height="30" clip-path="url(#clip1)"/>
+    <rect id="rect4" x="335" y="135" width="30" height="30" clip-path="url(#clip4)"/>
+  </g>
+  <script><![CDATA[
+    class Expectation {
+      constructor(x, y, id = null) {
+        this.x = x;
+        this.y = y;
+        this.element = id ? document.getElementById(id) : document.documentElement;
+      }
+    }
+    class Shape {
+      constructor(name, offset) {
+        this.name = name;
+        this.offset = offset;
+      }
+    }
+    let shapes = [];
+    shapes.push(new Shape("circle", 0));
+    shapes.push(new Shape("ellipse", 100));
+    shapes.push(new Shape("rect", 200));
+
+    shapes.forEach(shape => {
+      test(function() {
+        let expectations = [];
+
+        expectations.push(new Expectation(100 + shape.offset, 100, shape.name + "1"));
+        expectations.push(new Expectation(150 + shape.offset, 100, shape.name + "2"));
+        expectations.push(new Expectation(100 + shape.offset, 150));
+        expectations.push(new Expectation(150 + shape.offset, 150, shape.name + "4"));
+
+        expectations.forEach(expectation => {
+          let element = document.elementFromPoint(expectation.x, expectation.y);
+          assert_equals(element, expectation.element, "finds " + shape.name);
+        });
+      }, document.title + ": " + shape.name);
+    });
+  ]]>
+  </script>
+</svg>
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/dark-mode-default/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
deleted file mode 100644
index 14bb7bc..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
deleted file mode 100644
index b495bc3..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-site-isolation-trials/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-2-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-2-expected.txt
new file mode 100644
index 0000000..5218a54c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-2-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Async Script Execution Order Uncaught Error: assert_array_equals: Execution order expected property 2 to be "Async1" but got "Sync2" (expected array ["Inline1", "Sync1", "Async1", "Sync2", "EndOfBody", "DOMContentLoaded"] got ["Inline1", "Sync1", "Sync2", "EndOfBody", "DOMContentLoaded", "Async1"])
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml-expected.txt
new file mode 100644
index 0000000..6be2d2e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script-xml-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Defer Script Execution Order Uncaught Error: assert_array_equals: Execution order expected property 2 to be "Inline2" but got "Defer1" (expected array ["Inline1", "Sync1", "Inline2", "Sync2", "EndOfBody", "Defer1", "Defer2", "DOMContentLoaded"] got ["Inline1", "Sync1", "Defer1", "Inline2", "Defer2", "Sync2", "EndOfBody", "DOMContentLoaded"])
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write-expected.txt
new file mode 100644
index 0000000..58626a49
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL document.write() assert_array_equals: Execution order expected property 2 to be "Async1" but got "Sync1" (expected array ["Inline1", "Sync2", "Async1", "Sync1", "EndOfBody", "DOMContentLoaded", "WindowLoad"] got ["Inline1", "Sync2", "Sync1", "EndOfBody", "DOMContentLoaded", "Async1", "WindowLoad"])
+FAIL document.write(),close() assert_array_equals: Execution order expected property 2 to be "Async1" but got "Sync1" (expected array ["Inline1", "Sync2", "Async1", "Sync1", "EndOfBody", "DOMContentLoaded", "WindowLoad"] got ["Inline1", "Sync2", "Sync1", "EndOfBody", "DOMContentLoaded", "Async1", "WindowLoad"])
+FAIL document.open(),write() assert_array_equals: Execution order expected property 2 to be "Async1" but got "Sync1" (expected array ["Inline1", "Sync2", "Async1", "Sync1", "EndOfBody", "DOMContentLoaded", "WindowLoad"] got ["Inline1", "Sync2", "Sync1", "EndOfBody", "DOMContentLoaded", "Async1", "WindowLoad"])
+FAIL document.open(),write(),close() assert_array_equals: Execution order expected property 2 to be "Async1" but got "Sync1" (expected array ["Inline1", "Sync2", "Async1", "Sync1", "EndOfBody", "DOMContentLoaded", "WindowLoad"] got ["Inline1", "Sync2", "Sync1", "EndOfBody", "DOMContentLoaded", "Async1", "WindowLoad"])
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index b6b82ca..b299738 100644
--- a/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -119,6 +119,7 @@
 PASS oldChildWindow.onchange is newChildWindow.onchange
 PASS oldChildWindow.onclick is newChildWindow.onclick
 PASS oldChildWindow.onclose is newChildWindow.onclose
+PASS oldChildWindow.oncontentvisibilityautostatechanged is newChildWindow.oncontentvisibilityautostatechanged
 PASS oldChildWindow.oncontextlost is newChildWindow.oncontextlost
 PASS oldChildWindow.oncontextmenu is newChildWindow.oncontextmenu
 PASS oldChildWindow.oncontextrestored is newChildWindow.oncontextrestored
diff --git a/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
index 1983cad..61bec34 100644
--- a/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -64,6 +64,7 @@
 PASS childWindow.onchange is null
 PASS childWindow.onclick is null
 PASS childWindow.onclose is null
+PASS childWindow.oncontentvisibilityautostatechanged is null
 PASS childWindow.oncontextlost is null
 PASS childWindow.oncontextmenu is null
 PASS childWindow.oncontextrestored is null
diff --git a/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
index 1dcb603a..e12194cc 100644
--- a/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -64,6 +64,7 @@
 PASS childWindow.onchange is null
 PASS childWindow.onclick is null
 PASS childWindow.onclose is null
+PASS childWindow.oncontentvisibilityautostatechanged is null
 PASS childWindow.oncontextlost is null
 PASS childWindow.oncontextmenu is null
 PASS childWindow.oncontextrestored is null
diff --git a/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-2-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-2-expected.txt
new file mode 100644
index 0000000..5ec35cc
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-2-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Async Script Execution Order Uncaught Error: assert_array_equals: Execution order expected property 0 to be "Inline1" but got "EndOfBody" (expected array ["Inline1", "Sync1", "Async1", "Sync2", "EndOfBody", "DOMContentLoaded"] got ["EndOfBody", "Inline1", "Sync1", "Sync2", "DOMContentLoaded", "Async1"])
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-expected.txt
new file mode 100644
index 0000000..3c6326d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/async-script-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Async Script Execution Order Uncaught Error: assert_array_equals: Execution order expected property 0 to be "Inline1" but got "EndOfBody" (expected array ["Inline1", "Sync1", "EndOfBody", "DOMContentLoaded", "Async1"] got ["EndOfBody", "Inline1", "Sync1", "DOMContentLoaded", "Async1"])
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script-expected.txt
new file mode 100644
index 0000000..df343f8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/defer-script-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Defer Script Execution Order Uncaught Error: assert_array_equals: Execution order expected property 0 to be "Inline1" but got "EndOfBody" (expected array ["Inline1", "Sync1", "Inline2", "Sync2", "EndOfBody", "Defer1", "Defer2", "DOMContentLoaded"] got ["EndOfBody", "Inline1", "Sync1", "Inline2", "Sync2", "Defer1", "Defer2", "DOMContentLoaded"])
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write-expected.txt
new file mode 100644
index 0000000..5e365aaa
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL document.write() assert_array_equals: Execution order lengths differ, expected array ["Inline1", "Sync2", "Async1", "Sync1", "EndOfBody", "DOMContentLoaded", "WindowLoad"] length 7, got ["EndOfBody", "Inline1", "Sync1", "DOMContentLoaded", "Async1", "WindowLoad"] length 6
+FAIL document.write(),close() assert_array_equals: Execution order lengths differ, expected array ["Inline1", "Sync2", "Async1", "Sync1", "EndOfBody", "DOMContentLoaded", "WindowLoad"] length 7, got ["EndOfBody", "Inline1", "Sync1", "DOMContentLoaded", "Async1", "WindowLoad"] length 6
+FAIL document.open(),write() assert_array_equals: Execution order lengths differ, expected array ["Inline1", "Sync2", "Async1", "Sync1", "EndOfBody", "DOMContentLoaded", "WindowLoad"] length 7, got ["EndOfBody", "Inline1", "Async1"] length 3
+FAIL document.open(),write(),close() assert_array_equals: Execution order lengths differ, expected array ["Inline1", "Sync2", "Async1", "Sync1", "EndOfBody", "DOMContentLoaded", "WindowLoad"] length 7, got ["EndOfBody", "Inline1", "Async1", "Sync2"] length 4
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/platform/generic/webexposed/element-instance-property-listing-expected.txt
index f3cd01f..5214e4ab 100644
--- a/third_party/blink/web_tests/platform/generic/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/webexposed/element-instance-property-listing-expected.txt
@@ -191,6 +191,7 @@
     property onchange
     property onclick
     property onclose
+    property oncontentvisibilityautostatechanged
     property oncontextlost
     property oncontextmenu
     property oncontextrestored
@@ -1411,6 +1412,7 @@
     property onchange
     property onclick
     property onclose
+    property oncontentvisibilityautostatechanged
     property oncontextlost
     property oncontextmenu
     property oncontextrestored
diff --git a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
index ab34163..bb1f710 100644
--- a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
@@ -1324,6 +1324,10 @@
     method constructor
     method delete
     method getAll
+interface ContentVisibilityAutoStateChangedEvent : Event
+    attribute @@toStringTag
+    getter skipped
+    method constructor
 interface ConvolverNode : AudioNode
     attribute @@toStringTag
     getter buffer
@@ -1804,6 +1808,7 @@
     getter onchange
     getter onclick
     getter onclose
+    getter oncontentvisibilityautostatechanged
     getter oncontextlost
     getter oncontextmenu
     getter oncontextrestored
@@ -2012,6 +2017,7 @@
     setter onchange
     setter onclick
     setter onclose
+    setter oncontentvisibilityautostatechanged
     setter oncontextlost
     setter oncontextmenu
     setter oncontextrestored
@@ -3207,6 +3213,7 @@
     getter onchange
     getter onclick
     getter onclose
+    getter oncontentvisibilityautostatechanged
     getter oncontextlost
     getter oncontextmenu
     getter oncontextrestored
@@ -3335,6 +3342,7 @@
     setter onchange
     setter onclick
     setter onclose
+    setter oncontentvisibilityautostatechanged
     setter oncontextlost
     setter oncontextmenu
     setter oncontextrestored
@@ -5144,6 +5152,7 @@
     getter onchange
     getter onclick
     getter onclose
+    getter oncontentvisibilityautostatechanged
     getter oncontextlost
     getter oncontextmenu
     getter oncontextrestored
@@ -5253,6 +5262,7 @@
     setter onchange
     setter onclick
     setter onclose
+    setter oncontentvisibilityautostatechanged
     setter oncontextlost
     setter oncontextmenu
     setter oncontextrestored
@@ -7358,6 +7368,7 @@
     getter onchange
     getter onclick
     getter onclose
+    getter oncontentvisibilityautostatechanged
     getter oncontextlost
     getter oncontextmenu
     getter oncontextrestored
@@ -7469,6 +7480,7 @@
     setter onchange
     setter onclick
     setter onclose
+    setter oncontentvisibilityautostatechanged
     setter oncontextlost
     setter oncontextmenu
     setter oncontextrestored
@@ -11624,6 +11636,7 @@
     getter onchange
     getter onclick
     getter onclose
+    getter oncontentvisibilityautostatechanged
     getter oncontextlost
     getter oncontextmenu
     getter oncontextrestored
@@ -11846,6 +11859,7 @@
     setter onchange
     setter onclick
     setter onclose
+    setter oncontentvisibilityautostatechanged
     setter oncontextlost
     setter oncontextmenu
     setter oncontextrestored
diff --git a/third_party/blink/web_tests/platform/linux/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/linux/dark-mode/colors/selection-expected.png
index 644446fa..231145c4 100644
--- a/third_party/blink/web_tests/platform/linux/dark-mode/colors/selection-expected.png
+++ b/third_party/blink/web_tests/platform/linux/dark-mode/colors/selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/line-wrap-2-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/line-wrap-2-expected.png
index ea678ec..a9106581 100644
--- a/third_party/blink/web_tests/platform/linux/editing/selection/line-wrap-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/selection/line-wrap-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/markers/document-markers-zoom-2000-expected.png b/third_party/blink/web_tests/platform/linux/paint/markers/document-markers-zoom-2000-expected.png
index 81a9e407..fc548fb 100644
--- a/third_party/blink/web_tests/platform/linux/paint/markers/document-markers-zoom-2000-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/markers/document-markers-zoom-2000-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/text/text-selection-deco-01-b-expected.png b/third_party/blink/web_tests/platform/linux/svg/text/text-selection-deco-01-b-expected.png
index 22db64d..6bfd44f 100644
--- a/third_party/blink/web_tests/platform/linux/svg/text/text-selection-deco-01-b-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/text/text-selection-deco-01-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/text/text-selection-text-03-b-expected.png b/third_party/blink/web_tests/platform/linux/svg/text/text-selection-text-03-b-expected.png
index f7fdebd2..6c8d1f9 100644
--- a/third_party/blink/web_tests/platform/linux/svg/text/text-selection-text-03-b-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/text/text-selection-text-03-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-mode-default/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
index 14bb7bc..1e60df3 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
index b495bc3..aaefc53 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/khmer-selection-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/khmer-selection-expected.png
index 4dae189..4ba52e7 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/khmer-selection-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/khmer-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/khmer-selection-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/khmer-selection-expected.png
new file mode 100644
index 0000000..d34eed0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/khmer-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/editing/selection/line-wrap-2-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/editing/selection/line-wrap-2-expected.png
index b0e106b..6468907 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/editing/selection/line-wrap-2-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/editing/selection/line-wrap-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/paint/markers/document-markers-font-8px-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/paint/markers/document-markers-font-8px-expected.png
index 1547a596..422df4c 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/paint/markers/document-markers-font-8px-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/paint/markers/document-markers-font-8px-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/paint/markers/document-markers-zoom-2000-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/paint/markers/document-markers-zoom-2000-expected.png
index 52f9143..b723e3e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/paint/markers/document-markers-zoom-2000-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/paint/markers/document-markers-zoom-2000-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/text-selection-deco-01-b-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/text-selection-deco-01-b-expected.png
index eead15c..8005621 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/text-selection-deco-01-b-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/text-selection-deco-01-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/text-selection-text-03-b-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/text-selection-text-03-b-expected.png
index 416870c..9ec97f6 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/text-selection-text-03-b-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/text-selection-text-03-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/khmer-selection-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/khmer-selection-expected.png
index 89a1690e..43c4731 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/khmer-selection-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/khmer-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-mode-default/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
deleted file mode 100644
index a931abf..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
deleted file mode 100644
index 3678f88..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-mode-default/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
deleted file mode 100644
index a931abf..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
deleted file mode 100644
index 3678f88..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11/external/wpt/webmessaging/with-ports/020-expected.txt b/third_party/blink/web_tests/platform/mac-mac11/external/wpt/webmessaging/with-ports/020-expected.txt
new file mode 100644
index 0000000..44de48e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac11/external/wpt/webmessaging/with-ports/020-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+PASS cross-origin test
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac11/virtual/dark-mode-default/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/mac-mac11/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
deleted file mode 100644
index a931abf..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/mac-mac11/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
deleted file mode 100644
index 3678f88..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/external/wpt/webmessaging/with-ports/020-expected.txt b/third_party/blink/web_tests/platform/mac-mac12-arm64/external/wpt/webmessaging/with-ports/020-expected.txt
new file mode 100644
index 0000000..44de48e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/external/wpt/webmessaging/with-ports/020-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+PASS cross-origin test
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-mode-default/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
deleted file mode 100644
index a931abf..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
deleted file mode 100644
index 3678f88..0000000
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/selection/line-wrap-2-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/line-wrap-2-expected.png
index f79c9b86..9d089a7 100644
--- a/third_party/blink/web_tests/platform/mac/editing/selection/line-wrap-2-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/selection/line-wrap-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/markers/document-markers-font-8px-expected.png b/third_party/blink/web_tests/platform/mac/paint/markers/document-markers-font-8px-expected.png
index 1e0b12d..fe00a90 100644
--- a/third_party/blink/web_tests/platform/mac/paint/markers/document-markers-font-8px-expected.png
+++ b/third_party/blink/web_tests/platform/mac/paint/markers/document-markers-font-8px-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/markers/document-markers-zoom-2000-expected.png b/third_party/blink/web_tests/platform/mac/paint/markers/document-markers-zoom-2000-expected.png
index f0b7adb1..9627203b 100644
--- a/third_party/blink/web_tests/platform/mac/paint/markers/document-markers-zoom-2000-expected.png
+++ b/third_party/blink/web_tests/platform/mac/paint/markers/document-markers-zoom-2000-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/text/text-selection-deco-01-b-expected.png b/third_party/blink/web_tests/platform/mac/svg/text/text-selection-deco-01-b-expected.png
index c1b9339..ee87b3b 100644
--- a/third_party/blink/web_tests/platform/mac/svg/text/text-selection-deco-01-b-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/text/text-selection-deco-01-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/text/text-selection-text-03-b-expected.png b/third_party/blink/web_tests/platform/mac/svg/text/text-selection-text-03-b-expected.png
index 6d3823b4..17c6e79 100644
--- a/third_party/blink/web_tests/platform/mac/svg/text/text-selection-text-03-b-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/text/text-selection-text-03-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/khmer-selection-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/khmer-selection-expected.png
index ce97e889..343126b 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/khmer-selection-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/khmer-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/line-wrap-2-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/line-wrap-2-expected.png
index 450abb2..46e3bdb 100644
--- a/third_party/blink/web_tests/platform/win/editing/selection/line-wrap-2-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/selection/line-wrap-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/markers/document-markers-zoom-2000-expected.png b/third_party/blink/web_tests/platform/win/paint/markers/document-markers-zoom-2000-expected.png
index 7dba21f1..cb1fa3f 100644
--- a/third_party/blink/web_tests/platform/win/paint/markers/document-markers-zoom-2000-expected.png
+++ b/third_party/blink/web_tests/platform/win/paint/markers/document-markers-zoom-2000-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/text/text-selection-deco-01-b-expected.png b/third_party/blink/web_tests/platform/win/svg/text/text-selection-deco-01-b-expected.png
index a498ad2..15b6be2b 100644
--- a/third_party/blink/web_tests/platform/win/svg/text/text-selection-deco-01-b-expected.png
+++ b/third_party/blink/web_tests/platform/win/svg/text/text-selection-deco-01-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/text/text-selection-text-03-b-expected.png b/third_party/blink/web_tests/platform/win/svg/text/text-selection-text-03-b-expected.png
index d236f98..3fe7ec3 100644
--- a/third_party/blink/web_tests/platform/win/svg/text/text-selection-text-03-b-expected.png
+++ b/third_party/blink/web_tests/platform/win/svg/text/text-selection-text-03-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/khmer-selection-expected.png b/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/khmer-selection-expected.png
index 884ccb4..4c5de446 100644
--- a/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/khmer-selection-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/khmer-selection-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win10/virtual/dark-mode-default/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/win10/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
deleted file mode 100644
index 77442b4f..0000000
--- a/third_party/blink/web_tests/platform/win10/virtual/dark-mode-default/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win10/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png b/third_party/blink/web_tests/platform/win10/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
deleted file mode 100644
index ad31d17..0000000
--- a/third_party/blink/web_tests/platform/win10/virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/force-defer-script/README.md b/third_party/blink/web_tests/virtual/force-defer-script/README.md
new file mode 100644
index 0000000..4c793215
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/force-defer-script/README.md
@@ -0,0 +1,4 @@
+This suite runs the tests in defer-script/ with
+`--enable-features=ForceDeferScriptIntervention`
+to force the deferral of synchronous script.
+https://crbug.com/1339112
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-query-fallback.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-query-fallback.html
new file mode 100644
index 0000000..974131a3
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-query-fallback.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<title>Tests the fallback value in anchor queries</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#anchor-pos">
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#anchor-size">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<style>
+#container {
+  position: relative;
+  display: flex;
+  flex-wrap: wrap;
+  width: 300px;
+}
+
+.flex-item {
+  width: 100px;
+  height: 50px;
+  flex: auto;
+}
+
+#a1 {
+  anchor-name: --a1;
+  background: orange;
+}
+#a2 {
+  anchor-name: --a2;
+  background: purple;
+}
+
+.target {
+  position: absolute;
+}
+</style>
+
+<body id="container" onload="checkLayout('.target')">
+  <div class="flex-item" id="a1"></div>
+  <div class="flex-item"></div>
+  <div class="flex-item"></div>
+  <div class="flex-item"></div>
+  <div class="flex-item"></div>
+  <div class="flex-item"></div>
+  <div class="flex-item"></div>
+  <div class="flex-item"></div>
+  <div class="flex-item" id="a2"></div>
+
+  <!-- Fallback due to no valid anchor -->
+  <div class="target" style="left: anchor(--inexist-anchor left, 50px)" data-offset-x="50"></div>
+  <div class="target" style="width: anchor-size(--inexist-anchor width, 50px)" data-expected-width="50"></div>
+
+  <!-- Fallback due to wrong axis for anchor() -->
+  <div class="target" style="left: anchor(--a1 top, 50px)" data-offset-x="50"></div>
+  <div class="target" style="left: anchor(--a1 bottom, 50px)" data-offset-x="50"></div>
+  <div class="target" style="top: anchor(--a1 left, 50px)" data-offset-y="50"></div>
+  <div class="target" style="top: anchor(--a1 right, 50px)" data-offset-y="50"></div>
+
+  <!-- More complicated fallback values -->
+  <div class="target" style="left: anchor(--inexist-anchor left, 50%)" data-offset-x="150"></div>
+  <div class="target" style="left: anchor(--inexist-anchor left, calc(20% + 20px))" data-offset-x="80"></div>
+  <div class="target" style="top: anchor(--a1 left, anchor(--a2 top))" data-offset-y="100"></div>
+  <div class="target" style="top: anchor(--a1 left, calc((anchor(--a1 bottom) + anchor(--a2 top)) / 2)" data-offset-y="75"></div>
+  <div class="target" style="width: anchor-size(--inexist-anchor width, 50%)" data-expected-width="150"></div>
+  <div class="target" style="width: anchor-size(--inexist-anchor width, calc(20% + 20px))" data-expected-width="80"></div>
+  <div class="target" style="height: anchor-size(--inexist-anchor height, anchor-size(--a1 width))" data-expected-height="100"></div>
+  <div class="target" style="height: anchor-size(--inexist-anchor height, calc((anchor-size(--a1 width) + anchor-size(--a2 height)) / 2)" data-expected-height="75"></div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-001.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-001.html
index 8421ba62..58fb6fc 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-001.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#propdef-anchor-name">
 <link rel="author" href="mailto:kojii@chromium.org">
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/check-layout-th.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
 <style>
 .container {
   position: relative;
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-replaced-001.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-replaced-001.html
index 2a6baee..2e0c641 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-replaced-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-replaced-001.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#propdef-anchor-name">
 <link rel="author" href="mailto:kojii@chromium.org">
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/check-layout-th.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
 <style>
 .container {
   position: relative;
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-writing-modes-001.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-writing-modes-001.html
index a606e463..c9e1134 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-writing-modes-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-writing-modes-001.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#propdef-anchor-name">
 <link rel="author" href="mailto:kojii@chromium.org">
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/check-layout-th.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
 <style>
 .container {
   position: relative;
diff --git a/third_party/crubit/BUILD.gn b/third_party/crubit/BUILD.gn
new file mode 100644
index 0000000..4cb4fc7
--- /dev/null
+++ b/third_party/crubit/BUILD.gn
@@ -0,0 +1,77 @@
+# 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("//build/rust/rust_macro.gni")
+import("//build/rust/rust_static_library.gni")
+
+# Common constants.
+_support_dir = "src/rs_bindings_from_cc/support"
+
+# Dependencies of ..._rs_api.rs files generated by Crubit's
+# `bin/rs_bindings_from_cc` tool.  See also `deps_for_generated_rs_file` in
+# Crubit's `src/rs_bindings_from_cc/BUILD`.
+group("deps_of_rs_api") {
+  public_deps = [
+    ":ctor",
+    ":forward_declare",
+    ":oops",
+    "//third_party/rust/memoffset/v0_6:lib",
+    "//third_party/rust/static_assertions/v1:lib",
+  ]
+}
+
+# Dependencies of ..._rs_api_impl.cc files generated by Crubit's
+# `bin/rs_bindings_from_cc` tool.  See also `deps_for_generated_cc_file` in
+# Crubit's `src/rs_bindings_from_cc/BUILD`.
+source_set("deps_of_rs_api_impl") {
+  sources = [
+    "${_support_dir}/cxx20_backports.h",
+    "${_support_dir}/offsetof.h",
+  ]
+}
+
+rust_static_library("ctor") {
+  visibility = [ ":*" ]
+  crate_root = "${_support_dir}/ctor.rs"
+  sources = [ "${_support_dir}/ctor.rs" ]
+  build_native_rust_unit_tests = true
+  deps = [ ":ctor_proc_macros" ]
+}
+
+rust_macro("ctor_proc_macros") {
+  visibility = [ ":*" ]
+  crate_root = "${_support_dir}/ctor_proc_macros.rs"
+  sources = [ "${_support_dir}/ctor_proc_macros.rs" ]
+  deps = [
+    "//third_party/rust/proc_macro2/v1:lib",
+    "//third_party/rust/quote/v1:lib",
+    "//third_party/rust/syn/v1:lib",
+  ]
+}
+
+rust_static_library("forward_declare") {
+  visibility = [ ":*" ]
+  crate_root = "${_support_dir}/forward_declare.rs"
+  sources = [ "${_support_dir}/forward_declare.rs" ]
+  build_native_rust_unit_tests = true
+  deps = [ ":forward_declare_proc_macros" ]
+}
+
+rust_macro("forward_declare_proc_macros") {
+  visibility = [ ":*" ]
+  crate_root = "${_support_dir}/forward_declare_proc_macros.rs"
+  sources = [ "${_support_dir}/forward_declare_proc_macros.rs" ]
+  deps = [
+    "//third_party/rust/proc_macro2/v1:lib",
+    "//third_party/rust/quote/v1:lib",
+    "//third_party/rust/syn/v1:lib",
+  ]
+}
+
+rust_static_library("oops") {
+  visibility = [ ":*" ]
+  crate_root = "${_support_dir}/oops.rs"
+  sources = [ "${_support_dir}/oops.rs" ]
+  build_native_rust_unit_tests = true
+}
diff --git a/third_party/crubit/README.chromium b/third_party/crubit/README.chromium
index e6534b8..44e3d66 100644
--- a/third_party/crubit/README.chromium
+++ b/third_party/crubit/README.chromium
@@ -25,4 +25,5 @@
   packaging and distribution via `gclient sync`)
 
 Local Modifications:
-- TODO(lukasza): BUILD.gn that covers `src/rs_bindings_from_cc/support'
+- Added BUILD.gn that covers `src/rs_bindings_from_cc/support'
+  (see https://crrev.com/c/3756719)
diff --git a/third_party/js_code_coverage/README.chromium b/third_party/js_code_coverage/README.chromium
index 1a68279e..3fdfcce3 100644
--- a/third_party/js_code_coverage/README.chromium
+++ b/third_party/js_code_coverage/README.chromium
@@ -1,7 +1,7 @@
 Name: V8 to Istanbul Coverage Converter
 Short Name: v8-to-istanbul
 URL: https://www.npmjs.com/package/v8-to-istanbul
-Version: 7.1.2
+Version: 9.0.1
 License: ISC
 License File: NOT_SHIPPED
 Security Critical: no
@@ -15,7 +15,7 @@
 Name: Source map
 Short Name: source-map
 URL: https://www.npmjs.com/package/source-map
-Version: 0.7.3
+Version: 0.7.4
 License: BSD-3-Clause
 License File: NOT_SHIPPED
 Security Critical: No. Not shipped in Chrome, used to generate source maps for build steps.
@@ -56,3 +56,14 @@
 Description:
 Argparse is used to take, validate and parse CLI arguments for JavaScript scripts
 which are run as CLI scripts.
+
+Name: istanbul-lib-coverage
+Short Name: istanbul-lib-coverage
+URL: https://www.npmjs.com/package/istanbul-lib-coverage
+Version: 3.2.0
+License: BSD-3-Clause
+License File: NOT_SHIPPED
+Security Critical: No. Not shipped in Chrome, used to merge coverage reports.
+
+Description:
+istanbul-lib-coverage is used to merge coverage reports by the NYC library.
\ No newline at end of file
diff --git a/third_party/js_code_coverage/node_modules.tar.gz.sha1 b/third_party/js_code_coverage/node_modules.tar.gz.sha1
index 8b1dbb00..3bf338c 100644
--- a/third_party/js_code_coverage/node_modules.tar.gz.sha1
+++ b/third_party/js_code_coverage/node_modules.tar.gz.sha1
@@ -1 +1 @@
-0871d22e51e0e48b8bc98adce4b3cfade3939f97
+d538975c93eefc7bafd599b50f867e90c1ef17f3
diff --git a/third_party/js_code_coverage/package-lock.json b/third_party/js_code_coverage/package-lock.json
index d50a984..efc18b1 100644
--- a/third_party/js_code_coverage/package-lock.json
+++ b/third_party/js_code_coverage/package-lock.json
@@ -9,9 +9,10 @@
       "version": "1.0.0",
       "dependencies": {
         "argparse": "2.0.1",
+        "istanbul-lib-coverage": "3.2.0",
         "nyc": "15.1.0",
-        "source-map": "0.7.3",
-        "v8-to-istanbul": "7.1.2"
+        "source-map": "0.7.4",
+        "v8-to-istanbul": "9.0.1"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -278,6 +279,28 @@
         "node": ">=8"
       }
     },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+      "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.4.14",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+      "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.14",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
+      "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.0.3",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      }
+    },
     "node_modules/@types/istanbul-lib-coverage": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
@@ -773,9 +796,9 @@
       "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
     },
     "node_modules/istanbul-lib-coverage": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
-      "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+      "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
       "engines": {
         "node": ">=8"
       }
@@ -1245,9 +1268,9 @@
       "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
     },
     "node_modules/source-map": {
-      "version": "0.7.3",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-      "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
       "engines": {
         "node": ">= 8"
       }
@@ -1363,16 +1386,16 @@
       }
     },
     "node_modules/v8-to-istanbul": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz",
-      "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==",
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz",
+      "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==",
       "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.12",
         "@types/istanbul-lib-coverage": "^2.0.1",
-        "convert-source-map": "^1.6.0",
-        "source-map": "^0.7.3"
+        "convert-source-map": "^1.6.0"
       },
       "engines": {
-        "node": ">=10.10.0"
+        "node": ">=10.12.0"
       }
     },
     "node_modules/which": {
@@ -1733,6 +1756,25 @@
       "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
       "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA=="
     },
+    "@jridgewell/resolve-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+      "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
+    },
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.4.14",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+      "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+    },
+    "@jridgewell/trace-mapping": {
+      "version": "0.3.14",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
+      "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
+      "requires": {
+        "@jridgewell/resolve-uri": "^3.0.3",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      }
+    },
     "@types/istanbul-lib-coverage": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
@@ -2092,9 +2134,9 @@
       "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
     },
     "istanbul-lib-coverage": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
-      "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg=="
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
+      "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw=="
     },
     "istanbul-lib-hook": {
       "version": "3.0.0",
@@ -2447,9 +2489,9 @@
       "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
     },
     "source-map": {
-      "version": "0.7.3",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-      "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="
     },
     "spawn-wrap": {
       "version": "2.0.0",
@@ -2534,13 +2576,13 @@
       "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
     },
     "v8-to-istanbul": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz",
-      "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==",
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz",
+      "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==",
       "requires": {
+        "@jridgewell/trace-mapping": "^0.3.12",
         "@types/istanbul-lib-coverage": "^2.0.1",
-        "convert-source-map": "^1.6.0",
-        "source-map": "^0.7.3"
+        "convert-source-map": "^1.6.0"
       }
     },
     "which": {
diff --git a/third_party/js_code_coverage/package.json b/third_party/js_code_coverage/package.json
index 8aef75f4..102fdd4 100644
--- a/third_party/js_code_coverage/package.json
+++ b/third_party/js_code_coverage/package.json
@@ -4,8 +4,9 @@
   "author": "benreich@chromium.org",
   "dependencies": {
     "argparse": "2.0.1",
+    "istanbul-lib-coverage": "3.2.0",
     "nyc": "15.1.0",
-    "source-map": "0.7.3",
-    "v8-to-istanbul": "7.1.2"
+    "source-map": "0.7.4",
+    "v8-to-istanbul": "9.0.1"
   }
 }
diff --git a/third_party/rust/memoffset/v0_6/BUILD.gn b/third_party/rust/memoffset/v0_6/BUILD.gn
index 3ad496b..edd735f 100644
--- a/third_party/rust/memoffset/v0_6/BUILD.gn
+++ b/third_party/rust/memoffset/v0_6/BUILD.gn
@@ -5,7 +5,17 @@
 import("//build/rust/cargo_crate.gni")
 
 cargo_crate("lib") {
-  crate_name = "memoffset"
+  # `crate_name` has been manually changed from `memoffset` to
+  # `memoffset_unstable_const` to match Crubit's expectations.  It is not 100%
+  # clear what a long-term solution should look like, but this particular
+  # problem will probably go away on its own once related Rust features get
+  # stabilized and there is no need to opt into "unstable_const" in `features`
+  # below.
+  #
+  # TODO(https://crbug.com/1344314): Automate renaming crates under
+  # //third_party/rust and then remove the comment above.
+  crate_name = "memoffset_unstable_const"
+
   epoch = "0.6"
   crate_type = "rlib"
   crate_root = "crate/src/lib.rs"
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index a7ac49d..9cf764a7 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -3340,11 +3340,11 @@
     ],
 
     'release_bot_fuchsia_arm64_cast': [
-      'release_bot', 'fuchsia', 'arm64', 'cast', 'no_symbols',
+      'release_bot', 'fuchsia', 'arm64', 'cast_receiver', 'no_symbols',
     ],
 
     'release_bot_fuchsia_cast': [
-      'release_bot', 'fuchsia', 'cast', 'cast', 'no_symbols',
+      'release_bot', 'fuchsia', 'cast_receiver', 'no_symbols',
     ],
 
     'release_bot_fuchsia_cfv2_script': [
@@ -3484,11 +3484,11 @@
     ],
 
     'release_trybot_fuchsia_arm64_cast': [
-      'release_trybot', 'fuchsia', 'arm64', 'cast',
+      'release_trybot', 'fuchsia', 'arm64', 'cast_receiver',
     ],
 
     'release_trybot_fuchsia_cast': [
-      'release_trybot', 'fuchsia', 'cast',
+      'release_trybot', 'fuchsia', 'cast_receiver',
     ],
 
     'release_trybot_minimal_symbols_reclient': [
@@ -3737,6 +3737,10 @@
       'gn_args': 'is_cast_audio_only=true'
     },
 
+    'cast_receiver': {
+      'gn_args': 'enable_cast_receiver=true'
+    },
+
     'cfi': {
       'gn_args': 'is_cfi=true',
     },
diff --git a/tools/mb/mb_config_expectations/chromium.fuchsia.json b/tools/mb/mb_config_expectations/chromium.fuchsia.json
index 4599f8f..bff65f6 100644
--- a/tools/mb/mb_config_expectations/chromium.fuchsia.json
+++ b/tools/mb/mb_config_expectations/chromium.fuchsia.json
@@ -31,7 +31,7 @@
   "fuchsia-arm64-cast": {
     "gn_args": {
       "dcheck_always_on": false,
-      "is_chromecast": true,
+      "enable_cast_receiver": true,
       "is_component_build": false,
       "is_debug": false,
       "symbol_level": 0,
@@ -43,7 +43,7 @@
   "fuchsia-x64-cast": {
     "gn_args": {
       "dcheck_always_on": false,
-      "is_chromecast": true,
+      "enable_cast_receiver": true,
       "is_component_build": false,
       "is_debug": false,
       "symbol_level": 0,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.fuchsia.json b/tools/mb/mb_config_expectations/tryserver.chromium.fuchsia.json
index 00cdc7f..1eee440 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.fuchsia.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.fuchsia.json
@@ -2,7 +2,7 @@
   "fuchsia-arm64-cast": {
     "gn_args": {
       "dcheck_always_on": true,
-      "is_chromecast": true,
+      "enable_cast_receiver": true,
       "is_component_build": false,
       "is_debug": false,
       "symbol_level": 0,
@@ -62,7 +62,7 @@
   "fuchsia-x64-cast": {
     "gn_args": {
       "dcheck_always_on": true,
-      "is_chromecast": true,
+      "enable_cast_receiver": true,
       "is_component_build": false,
       "is_debug": false,
       "symbol_level": 0,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 98463d6..06a64de 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -56198,6 +56198,7 @@
   <int value="-1603026968"
       label="AlignFontDisplayAutoTimeoutWithLCPGoal:disabled"/>
   <int value="-1601374850" label="DialMediaRouteProvider:disabled"/>
+  <int value="-1600187674" label="ClipboardUnsanitizedContent:enabled"/>
   <int value="-1599538279" label="enable-md-policy-page"/>
   <int value="-1598785167" label="RequestDesktopSiteAdditions:disabled"/>
   <int value="-1597088741" label="RequestDesktopSiteAdditions:enabled"/>
@@ -56788,6 +56789,7 @@
   <int value="-1250222445" label="WebMidi:enabled"/>
   <int value="-1248478422" label="enable-zip-archiver-packer"/>
   <int value="-1246840031" label="OptInImeMenu:disabled"/>
+  <int value="-1246477455" label="ClipboardUnsanitizedContent:disabled"/>
   <int value="-1245617305" label="NewProfilePicker:enabled"/>
   <int value="-1244772303" label="NtpRealboxMatchOmniboxTheme:enabled"/>
   <int value="-1243694853" label="DeferAllScript:disabled"/>
@@ -80915,6 +80917,11 @@
   <int value="21" label="QUIC_CHROMIUM_CLIENT_SESSION_CLOSE_SESSION_ON_ERROR"/>
 </enum>
 
+<enum name="QuickAnswersDictionaryIntentSource">
+  <int value="0" label="kTextClassifier"/>
+  <int value="1" label="kHunspell"/>
+</enum>
+
 <enum name="QuickAnswersExitPoint">
   <int value="0" label="kUnspecified"/>
   <int value="1" label="KContextMenuDismiss"/>
@@ -85226,7 +85233,7 @@
   <int value="4" label="InOrder"/>
   <int value="5" label="Async"/>
   <int value="6" label="Immediate"/>
-  <int value="7" label="ForceDefer (Deprecated Feb 2021)"/>
+  <int value="7" label="ForceDefer"/>
 </enum>
 
 <enum name="ScrollAnchorRestorationStatus">
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 4ad3299c..46381c7 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -859,6 +859,18 @@
   </token>
 </histogram>
 
+<histogram name="Ash.Calendar.TimeToSeeTodaysEventDots" units="ms"
+    expires_after="2022-11-10">
+  <owner>newcomer@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
+  <summary>
+    Measures the time from open of CalendarView, to CalendarDateCellView
+    representing today receives events. Recorded once per lifetime of
+    CalendarViewController (once per show), when todays CalendarDateCellView
+    receives event data.
+  </summary>
+</histogram>
+
 <histogram name="Ash.Calendar.{CalendarChildView}.Activated"
     enum="CalendarEventSource" expires_after="2022-11-10">
   <owner>newcomer@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index e1fbd41..f639a27 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -2350,6 +2350,56 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Script.ForceDeferredScripts.Mainframe" units="count"
+    expires_after="2023-06-01">
+  <owner>chikamune@chromium.org</owner>
+  <owner>hiroshige@chromium.org</owner>
+  <owner>sisidovski@chromium.org</owner>
+  <summary>
+    Number of synchronous scripts that were deferred for a mainframe by the
+    ForceDeferScriptIntervention. This includes both inline scripts and external
+    scripts that had their execution deferred until after parsing completes.
+    Recorded at the end of parsing when ForceDeferScriptIntervention is enabled.
+  </summary>
+</histogram>
+
+<histogram name="Blink.Script.ForceDeferredScripts.Mainframe.External"
+    units="count" expires_after="2023-06-01">
+  <owner>chikamune@chromium.org</owner>
+  <owner>hiroshige@chromium.org</owner>
+  <owner>sisidovski@chromium.org</owner>
+  <summary>
+    Number of external synchronous scripts that were deferred for a mainframe by
+    the ForceDeferScriptIntervention. Recorded at the end of parsing when
+    ForceDeferScriptIntervention is enabled.
+  </summary>
+</histogram>
+
+<histogram name="Blink.Script.ForceDeferredScripts.Subframe" units="count"
+    expires_after="2023-06-01">
+  <owner>chikamune@chromium.org</owner>
+  <owner>hiroshige@chromium.org</owner>
+  <owner>sisidovski@chromium.org</owner>
+  <summary>
+    Number of synchronous scripts that were deferred for a subframe by the
+    ForceDeferScriptIntervention. This includes both inline scripts and external
+    scripts that had their execution deferred until after parsing completes.
+    Recorded at the end of parsing when ForceDeferScriptIntervention is enabled.
+  </summary>
+</histogram>
+
+<histogram name="Blink.Script.ForceDeferredScripts.Subframe.External"
+    units="count" expires_after="2023-06-01">
+  <owner>chikamune@chromium.org</owner>
+  <owner>hiroshige@chromium.org</owner>
+  <owner>sisidovski@chromium.org</owner>
+  <summary>
+    Number of external synchronous scripts that were deferred for a subframe by
+    the ForceDeferScriptIntervention. Recorded at the end of parsing when
+    ForceDeferScriptIntervention is enabled.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Script.SchedulingType" enum="ScriptSchedulingType"
     expires_after="2022-11-25">
   <owner>kouhei@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
index 71440d9..d3fae54 100644
--- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml
+++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -864,6 +864,17 @@
   </summary>
 </histogram>
 
+<histogram name="Bluetooth.ChromeOS.FastPair.SavedDevices.DeviceCount"
+    units="devices" expires_after="2022-09-20">
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the number of Fast Pair devices saved to a user's account. Emitted
+    following parsing the data fetched from the Footprints server to determine
+    the number of active devices, if any.
+  </summary>
+</histogram>
+
 <histogram
     name="Bluetooth.ChromeOS.FastPair.SavedDevices.GetSavedDevices.Result"
     enum="BooleanSuccess" expires_after="2022-09-20">
@@ -889,6 +900,24 @@
   </summary>
 </histogram>
 
+<histogram name="Bluetooth.ChromeOS.FastPair.SavedDevices.TotalUxLoadTime"
+    units="ms" expires_after="2022-09-20">
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the time necessary to load the Saved Devices Bluetooth Settings
+    sub-age to a terminal state with a list of devices, from the user's
+    perspective. Time is calculated as the difference between when the user
+    opens the Saved Devices sub-page and a load request is immediately sent to
+    the WebUI layer, and when the WebUI receives a response back from the server
+    with a list of devices (even if it is empty). The metric is emitted after
+    the response has been transformed into the appropriate data to be displayed
+    on the settings page, which involves decoding image URLs and parsing bytes
+    to transform them into string keys, and sent to the Settings UI layer for
+    immediate display.
+  </summary>
+</histogram>
+
 <histogram
     name="Bluetooth.ChromeOS.FastPair.SavedDevices.UpdateOptInStatus.Result.{FastPairPairingProtocol}"
     enum="BooleanSuccess" expires_after="2022-09-20">
diff --git a/tools/metrics/histograms/metadata/content/histograms.xml b/tools/metrics/histograms/metadata/content/histograms.xml
index e709675..66abaf07 100644
--- a/tools/metrics/histograms/metadata/content/histograms.xml
+++ b/tools/metrics/histograms/metadata/content/histograms.xml
@@ -1317,6 +1317,21 @@
   </summary>
 </histogram>
 
+<histogram name="ContentSuggestions.Feed.WebFeed.FollowCount.Engaged"
+    units="follows" expires_after="2023-01-15">
+  <owner>harringtond@chromium.org</owner>
+  <owner>feed@chromium.org</owner>
+  <summary>
+    Replaced with the ContentSuggestions.{FeedType}.FollowCount.Engaged2
+    histogram pattern because this one had a misleading name. It will be
+    effectively obsoleted in the future.
+
+    Android: The number of web feeds the user is following. Reported at most
+    once per feed visit, when the user engages (FeedEngagementType.Engaged) with
+    either the For-You or Following Feeds.
+  </summary>
+</histogram>
+
 <histogram name="ContentSuggestions.Feed.WebFeed.FollowCount.{Event}"
     units="follows" expires_after="2023-03-01">
   <owner>harringtond@chromium.org</owner>
@@ -1325,19 +1340,14 @@
     Android: The number of web feeds the user is following. Reported {Event}.
   </summary>
   <token key="Event">
-    <variant name="AfterFollow" summary="after a successful follow action."/>
-    <variant name="AfterUnfollow"
-        summary="after a successful unfollow action."/>
+    <variant name="AfterFollow" summary="after a successful follow action"/>
+    <variant name="AfterUnfollow" summary="after a successful unfollow action"/>
     <variant name="ContentShown"
         summary="after the Following feed is shown, and some content is
-                 displayed."/>
-    <variant name="Engaged"
-        summary="after the user has been recorded as FeedEngagementType
-                 'Engaged', reported at most once per day when the user
-                 interacts with either the For-You or Following Feeds."/>
+                 displayed"/>
     <variant name="NoContentShown"
         summary="after the Following Feed is shown, and no content is
-                 available for display."/>
+                 available for display"/>
   </token>
 </histogram>
 
@@ -1581,6 +1591,23 @@
   </token>
 </histogram>
 
+<histogram name="ContentSuggestions.{FeedType}.FollowCount.Engaged2"
+    units="follows" expires_after="2023-03-01">
+  <owner>harringtond@chromium.org</owner>
+  <owner>feed@chromium.org</owner>
+  <summary>
+    Android: The number of web feeds the user is following. Reported at most
+    once per feed visit, when the user engages (FeedEngagementType.Engaged,
+    non-Simple) with {FeedType}.
+  </summary>
+  <token key="FeedType">
+    <variant name="Feed" summary="the For-You feed"/>
+    <variant name="Feed.AllFeeds"
+        summary="either the For-You or Following/Web feed"/>
+    <variant name="Feed.WebFeed" summary="the Following/Web Feed"/>
+  </token>
+</histogram>
+
 <histogram name="ContentSuggestions.{FeedType}.InfoCard.{Action}"
     enum="FeedInfoCardType" expires_after="2023-05-01">
   <owner>jianli@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/cras/histograms.xml b/tools/metrics/histograms/metadata/cras/histograms.xml
index 12cef95..43ddfbc 100644
--- a/tools/metrics/histograms/metadata/cras/histograms.xml
+++ b/tools/metrics/histograms/metadata/cras/histograms.xml
@@ -80,6 +80,64 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Cras.DeviceConfigureTime{Direction}{Device}{HostCapability}{PeripheralCapability}"
+    units="ms" expires_after="2023-07-11">
+  <owner>hychao@google.com</owner>
+  <owner>chromeos-audio@google.com</owner>
+  <summary>
+    The configure time of {Direction} device {Device} in CRAS(ChromeOS audio
+    server). The configure time is the time taken for the device's own configure
+    function to execute and when it returns signaling that device is ready to
+    send/receive audio data. For wired audio devices this is expected to range
+    from 10ms to 100ms, while wireless headsets this could take longer than
+    500ms. We collect this configure time to gather stats and track the
+    improvement on shortening this configure time.
+  </summary>
+  <token key="Direction">
+    <variant name="" summary="aggregated across all breakdowns"/>
+    <variant name=".Input"/>
+    <variant name=".Output"/>
+  </token>
+  <token key="Device">
+    <variant name="" summary="aggregated across all breakdowns"/>
+    <variant name=".A2DP"/>
+    <variant name=".AbnormalFallback"/>
+    <variant name=".AlsaLoopback"/>
+    <variant name=".Bluetooth"/>
+    <variant name=".FrontMic"/>
+    <variant name=".Haptic"/>
+    <variant name=".HDMI"/>
+    <variant name=".Headphone"/>
+    <variant name=".HFP"/>
+    <variant name=".Hotword"/>
+    <variant name=".InternalMic"/>
+    <variant name=".InternalSpeaker"/>
+    <variant name=".InvalidType"/>
+    <variant name=".KeyboardMic"/>
+    <variant name=".Lineout"/>
+    <variant name=".Mic"/>
+    <variant name=".NoDevice"/>
+    <variant name=".NormalFallback"/>
+    <variant name=".PostDspLoopback"/>
+    <variant name=".PostMixLoopback"/>
+    <variant name=".RearMic"/>
+    <variant name=".SilentHotword"/>
+    <variant name=".Unknown"/>
+    <variant name=".USB"/>
+  </token>
+  <token key="HostCapability">
+    <variant name="" summary="aggregated across all breakdowns"/>
+    <variant name=".NonOffloading"/>
+    <variant name=".Offloading"/>
+  </token>
+  <token key="PeripheralCapability">
+    <variant name="" summary="aggregated across all breakdowns"/>
+    <variant name=".NarrowBandMic"/>
+    <variant name=".WideBandMic"/>
+  </token>
+</histogram>
+
 <histogram name="Cras.DeviceGain" units="level" expires_after="2022-11-20">
 <!-- Name completed by histogram_suffixes
      name="Cras.DeviceType" -->
diff --git a/tools/metrics/histograms/metadata/geolocation/histograms.xml b/tools/metrics/histograms/metadata/geolocation/histograms.xml
index abfe948..df98234 100644
--- a/tools/metrics/histograms/metadata/geolocation/histograms.xml
+++ b/tools/metrics/histograms/metadata/geolocation/histograms.xml
@@ -208,6 +208,16 @@
   </summary>
 </histogram>
 
+<histogram name="Geolocation.WifiDataProviderCommon.WifiScanTaskTime"
+    units="ms" expires_after="2023-07-15">
+  <owner>reillyg@chromium.org</owner>
+  <owner>deviceapi-team@google.com</owner>
+  <summary>
+    Records the time taken on Windows, macOS and Linux to query the operating
+    system for nearby access points.
+  </summary>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index e9d7b99..927ef1a 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -3019,6 +3019,16 @@
   </summary>
 </histogram>
 
+<histogram name="NetworkService.MemoryCache.FreshnessAtStore" units="seconds"
+    expires_after="2022-10-05">
+  <owner>bashi@chromium.org</owner>
+  <owner>blink-network-stack@google.com</owner>
+  <summary>
+    Records freshness of a response when it is going to be stored into the
+    in-memory cache. The range is from 1 seconds to 864000 seconds (10 days).
+  </summary>
+</histogram>
+
 <histogram name="NetworkService.NetworkLoaderCompletionTime.{Source}"
     units="ms" expires_after="2022-10-05">
   <owner>bashi@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/quick_answers/histograms.xml b/tools/metrics/histograms/metadata/quick_answers/histograms.xml
index 6201881..e96d7c1 100644
--- a/tools/metrics/histograms/metadata/quick_answers/histograms.xml
+++ b/tools/metrics/histograms/metadata/quick_answers/histograms.xml
@@ -116,6 +116,16 @@
   </token>
 </histogram>
 
+<histogram name="QuickAnswers.DictionaryIntent.Source"
+    enum="QuickAnswersDictionaryIntentSource" expires_after="2023-07-01">
+  <owner>updowndota@chromium.org</owner>
+  <owner>croissant-eng@chromium.org</owner>
+  <summary>
+    For Quick answers fetch, records the source type of dictionary intent
+    generated on device. ChromeOS only.
+  </summary>
+</histogram>
+
 <histogram name="QuickAnswers.ExitPoint" enum="QuickAnswersExitPoint"
     expires_after="2022-09-01">
   <owner>updowndota@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index a70ee9dc..0fcdadb 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -13,8 +13,8 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "efa73c7dd47ad2f78e240471101ac3aa4fbdc665",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/5771b652471bcc60464cce7605d2066aba8f0a9d/trace_processor_shell"
+            "hash": "ae8a798b3c3d93af16bdc077ce2b35bbcaacb8a4",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/da6e55d8b227cff3a88c26db23781790fad56c49/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
diff --git a/tools/win/IdleWakeups/IdleWakeups.vcxproj b/tools/win/IdleWakeups/IdleWakeups.vcxproj
index 773afee..fc765641 100644
--- a/tools/win/IdleWakeups/IdleWakeups.vcxproj
+++ b/tools/win/IdleWakeups/IdleWakeups.vcxproj
@@ -28,26 +28,26 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">

     <ConfigurationType>Application</ConfigurationType>

     <UseDebugLibraries>true</UseDebugLibraries>

-    <PlatformToolset>v142</PlatformToolset>

+    <PlatformToolset>v143</PlatformToolset>

     <CharacterSet>Unicode</CharacterSet>

   </PropertyGroup>

   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

     <ConfigurationType>Application</ConfigurationType>

     <UseDebugLibraries>false</UseDebugLibraries>

-    <PlatformToolset>v142</PlatformToolset>

+    <PlatformToolset>v143</PlatformToolset>

     <WholeProgramOptimization>true</WholeProgramOptimization>

     <CharacterSet>Unicode</CharacterSet>

   </PropertyGroup>

   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">

     <ConfigurationType>Application</ConfigurationType>

     <UseDebugLibraries>true</UseDebugLibraries>

-    <PlatformToolset>v142</PlatformToolset>

+    <PlatformToolset>v143</PlatformToolset>

     <CharacterSet>Unicode</CharacterSet>

   </PropertyGroup>

   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">

     <ConfigurationType>Application</ConfigurationType>

     <UseDebugLibraries>false</UseDebugLibraries>

-    <PlatformToolset>v142</PlatformToolset>

+    <PlatformToolset>v143</PlatformToolset>

     <WholeProgramOptimization>true</WholeProgramOptimization>

     <CharacterSet>Unicode</CharacterSet>

   </PropertyGroup>

diff --git a/tools/win/IdleWakeups/system_information_sampler.cpp b/tools/win/IdleWakeups/system_information_sampler.cpp
index 8d67a1e..854936d 100644
--- a/tools/win/IdleWakeups/system_information_sampler.cpp
+++ b/tools/win/IdleWakeups/system_information_sampler.cpp
@@ -330,8 +330,15 @@
         // If |pi| has the targeted process name, add its data to the snapshot.
         if (wcsncmp(target_process_name_filter(), pi->ImageName.Buffer,
                     lstrlen(target_process_name_filter())) == 0) {
-          snapshot->processes.insert(
-              std::make_pair(pi->ProcessId, GetProcessData(pi)));
+          // Special case System so that it must be an exact match instead of a
+          // prefix match, since otherwise there is no way to get reports for
+          // the system process without also recording SystemSettings.exe. For
+          // most processes you can solve this by adding .exe to the filter name
+          // but the System process doesn't have that suffix.
+          if (wcscmp(target_process_name_filter(), L"System") != 0 ||
+              wcslen(pi->ImageName.Buffer) == 6)
+            snapshot->processes.insert(
+                std::make_pair(pi->ProcessId, GetProcessData(pi)));
         }
       }
     }
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index 357dc18..bcb01bd 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -225,6 +225,13 @@
 bool IsPdfOcrEnabled() {
   return base::FeatureList::IsEnabled(::features::kPdfOcr);
 }
+
+const base::Feature kTextBasedAudioDescription{
+    "TextBasedAudioDescription", base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsTextBasedAudioDescriptionEnabled() {
+  return base::FeatureList::IsEnabled(::features::kTextBasedAudioDescription);
+}
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace features
diff --git a/ui/accessibility/accessibility_features.h b/ui/accessibility/accessibility_features.h
index f97cf7e..513859c 100644
--- a/ui/accessibility/accessibility_features.h
+++ b/ui/accessibility/accessibility_features.h
@@ -198,6 +198,14 @@
 // and the resulting text, together with its layout information, will be added
 // to the accessibility tree.
 AX_BASE_EXPORT bool IsPdfOcrEnabled();
+
+// Enables a setting that can turn on/off browser vocalization of 'descriptions'
+// tracks.
+AX_BASE_EXPORT extern const base::Feature kTextBasedAudioDescription;
+
+// Returns true if the setting to turn on text based audio descriptions is
+// enabled.
+AX_BASE_EXPORT bool IsTextBasedAudioDescriptionEnabled();
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace features
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index dec9de7..226bc9c 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -373,7 +373,6 @@
     "//base:jni_java",
     "//build/android:build_java",
     "//components/url_formatter/android:url_formatter_java",
-    "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
     "//third_party/androidx:androidx_activity_activity_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/HardwareDraw.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/HardwareDraw.java
index d2548d2..fe79047d 100644
--- a/ui/android/java/src/org/chromium/ui/resources/dynamics/HardwareDraw.java
+++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/HardwareDraw.java
@@ -20,17 +20,18 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.RequiresApi;
 
-import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.SequencedTaskRunner;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.ui.resources.dynamics.ViewResourceAdapter.CaptureMechanism;
+import org.chromium.ui.resources.dynamics.ViewResourceAdapter.CaptureResult;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Uses a {@link RenderNode} to perform bitmap capture of a java View. This should typically walk
@@ -55,28 +56,17 @@
         int RUNNING = 3;
     }
 
-    private static class RequestState {
-        // Track the last BitmapRequestId so we only return one image per request (in case of
-        // animations during that draw).
-        public final int requestId;
+    private ThreadUtils.ThreadChecker mThreadChecker = new ThreadUtils.ThreadChecker();
+    // When using Hardware Acceleration the conversion from canvas to Bitmap occurs on a different
+    // thread.
+    private static Handler sHandler;
 
-        public final View view;
-        public final float scale;
-        public final Callback<Bitmap> onBitmapCapture;
-
-        private RequestState(
-                int requestId, View view, float scale, Callback<Bitmap> onBitmapCapture) {
-            this.requestId = requestId;
-            this.view = view;
-            this.scale = scale;
-            this.onBitmapCapture = onBitmapCapture;
-        }
-        public static RequestState next(
-                View view, float scale, Callback<Bitmap> onBitmapCapture, RequestState previous) {
-            int nextId = previous == null ? 1 : previous.requestId + 1;
-            return new RequestState(nextId, view, scale, onBitmapCapture);
-        }
-    }
+    private AcceleratedImageReader mReader;
+    private boolean mDebugViewAttachedToWindowListenerAdded;
+    // Incremented each time we enqueue a Hardware drawn Bitmap. Only used if
+    // |mUseHardwareBitmapDraw| is true.
+    private AtomicInteger mCurrentBitmapRequestId;
+    private Bitmap mBitmap;
 
     // RenderNode was added in API level 29 (Android 10). So restrict AcceleratedImageReader as
     // well.
@@ -118,7 +108,7 @@
             init(width, height);
         }
 
-        // This needs PixelFormat.RGBA_8888, because the bitmap uses Bitmap.Config.ARGB_888
+        // This needs PixelFormat.RGBA_8888, because the |mBitmap| uses Bitmap.Config.ARGB_888
         // this is supported by the android docs which states " This must be one of the
         // ImageFormat or PixelFormat constants.". It does state that not all formats are
         // supported, but this seems to work and has worked for quite awhile. This comment
@@ -164,8 +154,7 @@
         // Should only be called directly after calling currentState() and seeing |requestNeeded|
         // being true. By requiring this we can avoid taking a lock to check the state before
         // posting the renderNode.
-        private void requestDraw(
-                RenderNode renderNode, View view, float scale, Callback<Bitmap> onBitmapCapture) {
+        private void requestDraw(RenderNode renderNode) {
             mThreadChecker.assertOnValidThread();
             switch (mImageReaderStatus) {
                 case ImageReaderStatus.NEW:
@@ -183,8 +172,7 @@
             mTaskRunner.postTask(() -> {
                 HardwareRenderer mRenderer = new HardwareRenderer();
                 try (TraceEvent e = TraceEvent.scoped("AcceleratedImageReader::requestDraw")) {
-                    mCurrentRequestState =
-                            RequestState.next(view, scale, onBitmapCapture, mCurrentRequestState);
+                    mCurrentBitmapRequestId.incrementAndGet();
                     Surface s = mReaderDelegate.getSurface();
                     mRenderer.setContentRoot(renderNode);
                     mRenderer.setSurface(s);
@@ -194,7 +182,6 @@
             });
         }
 
-        // This method is run on the sHandler.
         @Override
         public void onImageAvailable(ImageReader reader) {
             try (TraceEvent e = TraceEvent.scoped("AcceleratedImageReader::onImageAvailable")) {
@@ -205,8 +192,8 @@
                     return;
                 }
 
-                final RequestState requestState = mCurrentRequestState;
-                if (requestState.requestId == mLastBitmapRequestId) {
+                int request = mCurrentBitmapRequestId.get();
+                if (request == mLastBitmapRequestId) {
                     // If there was an animation when we requested a draw, we will receive each
                     // frame of the animation. For now we just take the first one (though the last
                     // would be better there is no good way to know when its the last of the frame).
@@ -218,7 +205,7 @@
                     image.close();
                     return;
                 }
-                mLastBitmapRequestId = requestState.requestId;
+                mLastBitmapRequestId = request;
 
                 android.media.Image.Plane[] planes = image.getPlanes();
                 assert planes.length != 0;
@@ -240,21 +227,8 @@
                                 CaptureUtils.createBitmap(width + rowPaddingInPixels, height);
                         snapshot.copyPixelsFromBuffer(buffer);
                         image.close();
-
-                        final State currentState =
-                                new State(width, height, rowPaddingInPixels, snapshot);
-                        mState = currentState;
-                        requestState.view.getHandler().post(() -> {
-                            int scaledWidth =
-                                    (int) (requestState.view.getWidth() * requestState.scale);
-                            int scaledHeight =
-                                    (int) (requestState.view.getHeight() * requestState.scale);
-                            if (mReader.validateCurrentBitmap(
-                                        currentState, scaledWidth, scaledHeight)
-                                    && currentState.mHardwareBitmap != null) {
-                                requestState.onBitmapCapture.onResult(currentState.mHardwareBitmap);
-                            }
-                        });
+                        // Update the bitmap the UI reads.
+                        mState = new State(width, height, rowPaddingInPixels, snapshot);
                     }
                 });
             }
@@ -285,30 +259,21 @@
         }
     }
 
-    // When using Hardware Acceleration the conversion from canvas to Bitmap occurs on a different
-    // thread.
-    private static Handler sHandler;
-
-    private final ThreadUtils.ThreadChecker mThreadChecker = new ThreadUtils.ThreadChecker();
-
-    private AcceleratedImageReader mReader;
-    private boolean mDebugViewAttachedToWindowListenerAdded;
-    // Set each time we enqueue a Hardware drawn Bitmap.
-    private RequestState mCurrentRequestState;
-
     /**
      * Each instance should be called by external clients only on the thread it is created. The
      * first instance created will also create a thread to do the actual rendering on.
      */
     public HardwareDraw() {
         mThreadChecker.assertOnValidThread();
+
         if (sHandler == null) {
             HandlerThread thread = new HandlerThread("HardwareDrawThread");
             thread.start();
             sHandler = new Handler(thread.getLooper());
         }
-        // We couldn't set up |mReader| even if we had a View, because the view might not have had
-        // its first layout yet and image reader needs to know the width and the height.
+        mCurrentBitmapRequestId = new AtomicInteger(0);
+        // We can't set up |mReader| here because |mView| might not have had its first layout
+        // yet and image reader needs to know the width and the height.
     }
 
     private boolean captureHardware(Canvas canvas, View view, Rect dirtyRect, float scale,
@@ -317,7 +282,8 @@
                     /*drawWhileDetached*/ drawWhileDetached, observer)) {
             return true;
         }
-        // TODO(https://crbug/1318009): Remove this code or promote it to default once we determine
+        // TODO(crbug/1318009): remove this code or promote it to default once we determine if this
+        // is the proper fix.
         TraceEvent.instant("HardwareDraw::DrawAttemptedWhileDetached");
         if (!mDebugViewAttachedToWindowListenerAdded) {
             mDebugViewAttachedToWindowListenerAdded = true;
@@ -338,14 +304,11 @@
         return false;
     }
 
-    /**
-     * This uses a RecordingNode to store all the required draw instructions without doing them
-     * upfront. And then on a threadpool task we grab a hardware canvas (required to use a
-     * RenderNode) and draw it using the hardware accelerated canvas.
-     * @return If draw instructions were recorded and the dirty rect can be reset.
-     */
-    private boolean captureWithHardwareDraw(View view, Rect dirtyRect, float scale,
-            CaptureObserver observer, Callback<Bitmap> onBitmapCapture) {
+    // This uses a RecordingNode to store all the required draw instructions without doing
+    // them upfront. And then on a threadpool task we grab a hardware canvas (required to use a
+    // RenderNode) and draw it using the hardware accelerated canvas.
+    private boolean captureWithHardwareDraw(
+            View view, Rect dirtyRect, float scale, CaptureObserver observer) {
         try (TraceEvent e = TraceEvent.scoped("HardwareDraw:captureWithHardwareDraw")) {
             if (view.getWidth() == 0 || view.getHeight() == 0) {
                 // We haven't actually laid out this view yet no point in requesting a screenshot.
@@ -358,6 +321,18 @@
             // modify this state object is the UI thread. So grab it all up front.
             AcceleratedImageReader.State currentState = mReader.currentState();
 
+            // If we have a new Bitmap update our |mBitmap| to the newest version. Since we update
+            // one check late we might serve a slightly stale result but not for long. If the bitmap
+            // is not there we'll end up just showing a blank toolbar without any icons or text.
+            // However this is preferred over blocking the main thread waiting for the image
+            // potentially during user input.
+            int scaledWidth = (int) (view.getWidth() * scale);
+            int scaledHeight = (int) (view.getHeight() * scale);
+            if (mReader.validateCurrentBitmap(currentState, scaledWidth, scaledHeight)
+                    && currentState.mHardwareBitmap != null) {
+                mBitmap = currentState.mHardwareBitmap;
+            }
+
             // If we didn't have a bitmap to return and there isn't an ongoing request already we
             // will start a bitmap copy which will be done Async on a different thread.
             RenderNode renderNode = null;
@@ -377,8 +352,7 @@
                         canvas, view, dirtyRect, scale, /*drawWhileDetached*/ false, observer);
                 renderNode.endRecording();
                 if (captureSuccess) {
-                    onDrawInstructionsAvailable(
-                            renderNode, currentState, view, scale, onBitmapCapture);
+                    onDrawInstructionsAvailable(renderNode, currentState);
                 }
                 return captureSuccess;
             }
@@ -387,11 +361,10 @@
     }
 
     @SuppressWarnings("NewApi")
-    private void onDrawInstructionsAvailable(RenderNode renderNode,
-            AcceleratedImageReader.State currentState, View view, float scale,
-            Callback<Bitmap> onBitmapCapture) {
+    private void onDrawInstructionsAvailable(
+            RenderNode renderNode, AcceleratedImageReader.State currentState) {
         currentState.mRequestNewDraw = false;
-        mReader.requestDraw(renderNode, view, scale, onBitmapCapture);
+        mReader.requestDraw(renderNode);
     }
 
     @Override
@@ -400,7 +373,19 @@
     }
 
     @Override
+    public boolean shouldPretendIsDirty() {
+        // The bitmap is dirty if we're waiting for the results of a previous request (null mBitmap
+        // or RUNNING), so continue to trigger captures.
+        return mBitmap == null || mReader.currentStatus() == ImageReaderStatus.RUNNING;
+    }
+
+    @Override
     public void onViewSizeChange(View view, float scale) {
+        // Wait for a new image before returning anything.
+        if (mBitmap != null) {
+            mBitmap.recycle();
+            mBitmap = null;
+        }
         int scaledWidth = (int) (view.getWidth() * scale);
         int scaledHeight = (int) (view.getHeight() * scale);
         if (mReader == null) {
@@ -412,17 +397,15 @@
 
     @Override
     public void dropCachedBitmap() {
-        AcceleratedImageReader.State state = mReader.mState;
-        if (state != null) {
-            state.mHardwareBitmap = null;
-        }
+        mBitmap = null;
     }
 
     @Override
-    public boolean startBitmapCapture(View view, Rect dirtyRect, float scale,
-            CaptureObserver observer, Callback<Bitmap> onBitmapCapture) {
+    public CaptureResult syncCaptureBitmap(
+            View view, Rect dirtyRect, float scale, CaptureObserver observer) {
         try (TraceEvent e = TraceEvent.scoped("HardwareDraw:syncCaptureBitmap")) {
-            return captureWithHardwareDraw(view, dirtyRect, scale, observer, onBitmapCapture);
+            boolean captureSuccess = captureWithHardwareDraw(view, dirtyRect, scale, observer);
+            return new CaptureResult(mBitmap, captureSuccess);
         }
     }
 }
\ No newline at end of file
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/SoftwareDraw.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/SoftwareDraw.java
index e5d951c..20878b627 100644
--- a/ui/android/java/src/org/chromium/ui/resources/dynamics/SoftwareDraw.java
+++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/SoftwareDraw.java
@@ -10,8 +10,8 @@
 import android.graphics.Rect;
 import android.view.View;
 
-import org.chromium.base.Callback;
 import org.chromium.base.TraceEvent;
+import org.chromium.ui.resources.dynamics.ViewResourceAdapter.CaptureResult;
 
 /** Simple bitmap capture approach simply calling {@link View#draw(Canvas)}. */
 public class SoftwareDraw implements ViewResourceAdapter.CaptureMechanism {
@@ -23,6 +23,11 @@
     }
 
     @Override
+    public boolean shouldPretendIsDirty() {
+        return false;
+    }
+
+    @Override
     public void onViewSizeChange(View view, float scale) {}
 
     @Override
@@ -31,8 +36,8 @@
     }
 
     @Override
-    public boolean startBitmapCapture(View view, Rect dirtyRect, float scale,
-            CaptureObserver observer, Callback<Bitmap> onBitmapCapture) {
+    public CaptureResult syncCaptureBitmap(
+            View view, Rect dirtyRect, float scale, CaptureObserver observer) {
         try (TraceEvent e = TraceEvent.scoped("SoftwareDraw:syncCaptureBitmap")) {
             int scaledWidth = (int) (view.getWidth() * scale);
             int scaledHeight = (int) (view.getHeight() * scale);
@@ -60,9 +65,7 @@
                 assert mBitmap.getWidth() == 1 && mBitmap.getHeight() == 1;
                 mBitmap.setPixel(0, 0, Color.TRANSPARENT);
             }
-
-            onBitmapCapture.onResult(mBitmap);
-            return !isEmpty;
+            return new CaptureResult(mBitmap, !isEmpty);
         }
     }
 }
\ No newline at end of file
diff --git a/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java b/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java
index b05eb48..9ce2ea9 100644
--- a/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java
+++ b/ui/android/java/src/org/chromium/ui/resources/dynamics/ViewResourceAdapter.java
@@ -29,26 +29,41 @@
  */
 public class ViewResourceAdapter
         implements DynamicResource, OnLayoutChangeListener, CaptureObserver {
+    /** A plain object to hold the return values from {@link CaptureMechanism#syncCaptureBitmap}. */
+    public static class CaptureResult {
+        public final Bitmap bitmap;
+        public final boolean clearDirtyRect;
+
+        /**
+         * @param bitmap The drawn pixels for the view being captured.
+         * @param clearDirtyRect If the dirty rect can be cleared, because a capture happened.
+         */
+        public CaptureResult(Bitmap bitmap, boolean clearDirtyRect) {
+            this.bitmap = bitmap;
+            this.clearDirtyRect = clearDirtyRect;
+        }
+    }
+
     /** Abstraction around the mechanism for actually capturing bitmaps.  */
     public interface CaptureMechanism {
         /** See {@link Resource#shouldRemoveResourceOnNullBitmap()}. */
         boolean shouldRemoveResourceOnNullBitmap();
+        /** If a capture should be taken based on state of the capture mechanism. */
+        boolean shouldPretendIsDirty();
         /** Called when the size of the view changes. */
-        default void onViewSizeChange(View view, float scale) {}
+        void onViewSizeChange(View view, float scale);
         /** Called to drop any cached bitmaps to free up memory. */
         void dropCachedBitmap();
-
         /**
          * Called to trigger the actual bitmap capture.
          * @param view The view being captured.
          * @param dirtyRect The area that has changed since last capture.
          * @param scale Scalar to apply to width and height when capturing a bitmap.
          * @param observer To be notified before and after the capture happens.
-         * @param onBitmapCapture The callback to return the recorded image.
-         * @return If the dirty rect can be cleared on a successful capture.
+         * @return The result of the capture.
          */
-        boolean startBitmapCapture(View view, Rect dirtyRect, float scale, CaptureObserver observer,
-                Callback<Bitmap> onBitmapCapture);
+        CaptureResult syncCaptureBitmap(
+                View view, Rect dirtyRect, float scale, CaptureObserver observer);
     }
 
     private final View mView;
@@ -97,28 +112,25 @@
     }
 
     /**
-     * Triggers a bitmap capture. Depending on this mechanism, it may do some or all of the work,
-     * and may be sync or async.
+     * Typically called when ({@link #isDirty()} returned {@code true}), to return a new
+     * {@link Bitmap} and clear out the dirty rect, resulting in a non-dirty view. Depending on the
+     * draw mechanism, this may return a null bitmap. In such a case, on the next frame, isDirty()
+     * should still be used to decide whether to call this.
+     * @return A {@link Bitmap} representing the {@link View}.
      */
     @SuppressWarnings("NewApi")
-    public void triggerBitmapCapture() {
+    public Bitmap getBitmap() {
         mThreadChecker.assertOnValidThread();
         try (TraceEvent e = TraceEvent.scoped("ViewResourceAdapter:getBitmap")) {
-            if (mCaptureMechanism.startBitmapCapture(
-                        mView, new Rect(mDirtyRect), mScale, this, this::onCapture)) {
+            CaptureResult result =
+                    mCaptureMechanism.syncCaptureBitmap(mView, new Rect(mDirtyRect), mScale, this);
+            if (result.clearDirtyRect) {
                 mDirtyRect.setEmpty();
             }
+            return result.bitmap;
         }
     }
 
-    private void onCapture(Bitmap bitmap) {
-        mThreadChecker.assertOnValidThread();
-        Resource resource = new DynamicResourceSnapshot(bitmap,
-                mCaptureMechanism.shouldRemoveResourceOnNullBitmap(), mViewSize,
-                createNativeResource());
-        mOnResourceReady.onResult(resource);
-    }
-
     /**
      * Set the downsampling scale. The rendered size is not affected.
      * @param scale The scale to use. <1 means the Bitmap is smaller than the View.
@@ -144,18 +156,30 @@
     /**
      * On every render frame, this resources will check to see if it is dirty. If so, it will take
      * a bitmap capture and push it to the renderer. Subclasses can override this method to suppress
-     * when captures are/can be taken.
+     * when captures are/can be taken. Although this will likely run into problems with hardware
+     * draws because the bitmap they return lags behind by a capture. Should ultimately be fixed as
+     * part of https://crbug.com/1338202.
      * @return If a bitmap capture should be taken/started.
      */
     @CallSuper
     protected boolean isDirty() {
-        return !mDirtyRect.isEmpty();
+        // The bitmap is dirty if some part of it has changed, or the capture mode wants to return a
+        // new bitmap.
+        return !mDirtyRect.isEmpty() || mCaptureMechanism.shouldPretendIsDirty();
     }
 
     @Override
     public void onResourceRequested() {
+        // TODO(skym): The hardware capture approach should be pushing bitmaps when they're ready,
+        // and avoid relying on isDirty and/or onResourceRequested signals. However this is an
+        // intermediate state during refactoring, and is intentionally keeping the old behavior for
+        // now.
         if (mOnResourceReady != null && isDirty()) {
-            triggerBitmapCapture();
+            Bitmap bitmap = getBitmap();
+            boolean removeOnNull = mCaptureMechanism.shouldRemoveResourceOnNullBitmap();
+            Resource resource = new DynamicResourceSnapshot(
+                    bitmap, removeOnNull, mViewSize, createNativeResource());
+            mOnResourceReady.onResult(resource);
         }
     }
 
diff --git a/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java b/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java
index 0a863dc..2c7d064 100644
--- a/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java
+++ b/ui/android/junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java
@@ -87,15 +87,9 @@
         return DynamicResourceTestUtils.getBitmapSizeSync(mAdapter);
     }
 
-    private Bitmap getBitmap() {
-        // Need to mark dirty before requesting, otherwise it will no-op.
-        mAdapter.invalidate(null);
-        return DynamicResourceTestUtils.getBitmapSync(mAdapter);
-    }
-
     @Test
     public void testGetBitmap() {
-        Bitmap bitmap = getBitmap();
+        Bitmap bitmap = mAdapter.getBitmap();
         assertNotNull(bitmap);
         assertEquals(mViewWidth, bitmap.getWidth());
         assertEquals(mViewHeight, bitmap.getHeight());
@@ -103,9 +97,8 @@
 
     @Test
     public void testGetBitmapSize() {
-        Bitmap bitmap = getBitmap();
+        Bitmap bitmap = mAdapter.getBitmap();
         Rect rect = getBitmapSize();
-
         assertEquals(bitmap.getWidth(), rect.width());
         assertEquals(bitmap.getHeight(), rect.height());
     }
@@ -114,7 +107,7 @@
     public void testSetDownsamplingSize() {
         float scale = 0.5f;
         mAdapter.setDownsamplingScale(scale);
-        Bitmap bitmap = getBitmap();
+        Bitmap bitmap = mAdapter.getBitmap();
         assertEquals(mViewWidth * scale, bitmap.getWidth(), 1);
         assertEquals(mViewHeight * scale, bitmap.getHeight(), 1);
 
@@ -127,13 +120,13 @@
     public void testIsDirty() {
         assertTrue(mAdapter.isDirty());
 
-        getBitmap();
+        mAdapter.getBitmap();
         assertFalse(mAdapter.isDirty());
     }
 
     @Test
     public void testOnLayoutChange() {
-        getBitmap();
+        mAdapter.getBitmap();
         assertFalse(mAdapter.isDirty());
 
         mAdapter.onLayoutChange(mView, 0, 0, 1, 2, 0, 0, mViewWidth, mViewHeight);
@@ -148,7 +141,7 @@
     public void testOnLayoutChangeDownsampled() {
         mAdapter.setDownsamplingScale(0.5f);
 
-        getBitmap();
+        mAdapter.getBitmap();
         assertFalse(mAdapter.isDirty());
 
         mAdapter.onLayoutChange(mView, 0, 0, 1, 2, 0, 0, mViewWidth, mViewHeight);
@@ -161,7 +154,7 @@
 
     @Test
     public void testInvalidate() {
-        getBitmap();
+        mAdapter.getBitmap();
         assertFalse(mAdapter.isDirty());
 
         mAdapter.invalidate(null);
@@ -174,7 +167,7 @@
 
     @Test
     public void testInvalidateRect() {
-        getBitmap();
+        mAdapter.getBitmap();
         assertFalse(mAdapter.isDirty());
 
         Rect dirtyRect = new Rect(1, 2, 3, 4);
@@ -187,7 +180,7 @@
     public void testInvalidateRectDownsampled() {
         mAdapter.setDownsamplingScale(0.5f);
 
-        getBitmap();
+        mAdapter.getBitmap();
         assertFalse(mAdapter.isDirty());
 
         Rect dirtyRect = new Rect(1, 2, 3, 4);
@@ -198,7 +191,7 @@
 
     @Test
     public void testInvalidateRectUnion() {
-        getBitmap();
+        mAdapter.getBitmap();
         assertFalse(mAdapter.isDirty());
 
         mAdapter.invalidate(new Rect(1, 2, 3, 4));
@@ -210,7 +203,7 @@
 
     @Test
     public void testGetBitmapResized() {
-        Bitmap bitmap = getBitmap();
+        Bitmap bitmap = mAdapter.getBitmap();
         assertNotNull(bitmap);
         assertEquals(mViewWidth, bitmap.getWidth());
         assertEquals(mViewHeight, bitmap.getHeight());
@@ -218,7 +211,7 @@
         mViewWidth = 10;
         mViewHeight = 20;
         mAdapter.invalidate(null);
-        Bitmap bitmap2 = getBitmap();
+        Bitmap bitmap2 = mAdapter.getBitmap();
         assertNotNull(bitmap2);
         assertEquals(mViewWidth, bitmap2.getWidth());
         assertEquals(mViewHeight, bitmap2.getHeight());
@@ -227,39 +220,39 @@
 
     @Test
     public void testBitmapReused() {
-        Bitmap bitmap = getBitmap();
+        Bitmap bitmap = mAdapter.getBitmap();
         assertNotNull(bitmap);
 
         mAdapter.invalidate(null);
         assertTrue(mAdapter.isDirty());
-        assertEquals(bitmap, getBitmap());
+        assertEquals(bitmap, mAdapter.getBitmap());
     }
 
     @Test
     public void testDropCachedBitmap() {
-        Bitmap bitmap = getBitmap();
+        Bitmap bitmap = mAdapter.getBitmap();
         assertNotNull(bitmap);
 
         mAdapter.invalidate(null);
         assertTrue(mAdapter.isDirty());
-        assertEquals(bitmap, getBitmap());
+        assertEquals(bitmap, mAdapter.getBitmap());
 
         mAdapter.dropCachedBitmap();
         mAdapter.invalidate(null);
         assertTrue(mAdapter.isDirty());
-        assertNotEquals(bitmap, getBitmap());
+        assertNotEquals(bitmap, mAdapter.getBitmap());
     }
 
     @Test
     public void testDropCachedBitmapNotDirty() {
-        getBitmap();
+        mAdapter.getBitmap();
         mAdapter.dropCachedBitmap();
         assertFalse(mAdapter.isDirty());
     }
 
     @Test
     public void testDropCachedBitmapGCed() {
-        WeakReference<Bitmap> bitmapWeakReference = new WeakReference<>(getBitmap());
+        WeakReference<Bitmap> bitmapWeakReference = new WeakReference<>(mAdapter.getBitmap());
         assertNotNull(bitmapWeakReference.get());
         assertFalse(canBeGarbageCollected(bitmapWeakReference));
 
@@ -269,19 +262,19 @@
 
     @Test
     public void testResizeGCed() {
-        WeakReference<Bitmap> bitmapWeakReference = new WeakReference<>(getBitmap());
+        WeakReference<Bitmap> bitmapWeakReference = new WeakReference<>(mAdapter.getBitmap());
         assertNotNull(bitmapWeakReference.get());
         assertFalse(canBeGarbageCollected(bitmapWeakReference));
 
         mViewWidth += 10;
         mAdapter.invalidate(null);
-        getBitmap();
+        mAdapter.getBitmap();
         assertTrue(canBeGarbageCollected(bitmapWeakReference));
     }
 
     @Test
     public void testGetDirtyRect() {
-        getBitmap();
+        mAdapter.getBitmap();
         Rect rect = mAdapter.getDirtyRect();
         assertTrue(rect.isEmpty());
 
@@ -295,7 +288,7 @@
     public void testGetDirtyRectDownsampled() {
         mAdapter.setDownsamplingScale(0.5f);
 
-        getBitmap();
+        mAdapter.getBitmap();
         Rect rect = mAdapter.getDirtyRect();
         assertTrue(rect.isEmpty());
 
diff --git a/ui/base/cursor/BUILD.gn b/ui/base/cursor/BUILD.gn
index 18e4f4a..50b7ecb 100644
--- a/ui/base/cursor/BUILD.gn
+++ b/ui/base/cursor/BUILD.gn
@@ -24,17 +24,6 @@
   deps = [ "//ui/gfx:gfx_skia" ]
 }
 
-component("theme_manager") {
-  output_name = "ui_base_cursor_theme_manager"
-  sources = [
-    "cursor_theme_manager.cc",
-    "cursor_theme_manager.h",
-    "cursor_theme_manager_observer.h",
-  ]
-  defines = [ "IS_UI_BASE_CURSOR_THEME_MANAGER_IMPL" ]
-  public_deps = [ "//base" ]
-}
-
 source_set("unittests") {
   testonly = true
   sources = []
diff --git a/ui/base/cursor/cursor_theme_manager.cc b/ui/base/cursor/cursor_theme_manager.cc
deleted file mode 100644
index 28552fb..0000000
--- a/ui/base/cursor/cursor_theme_manager.cc
+++ /dev/null
@@ -1,45 +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.
-
-#include "ui/base/cursor/cursor_theme_manager.h"
-
-#include "base/observer_list.h"
-
-namespace ui {
-
-namespace {
-
-CursorThemeManager* g_instance = nullptr;
-
-}
-
-CursorThemeManager::~CursorThemeManager() = default;
-
-// static
-CursorThemeManager* CursorThemeManager::GetInstance() {
-  return g_instance;
-}
-
-// static
-void CursorThemeManager::SetInstance(CursorThemeManager* instance) {
-  g_instance = instance;
-}
-
-void CursorThemeManager::AddObserver(CursorThemeManagerObserver* observer) {
-  cursor_theme_observers_.AddObserver(observer);
-  std::string name = GetCursorThemeName();
-  if (!name.empty())
-    observer->OnCursorThemeNameChanged(name);
-  int size = GetCursorThemeSize();
-  if (size)
-    observer->OnCursorThemeSizeChanged(size);
-}
-
-void CursorThemeManager::RemoveObserver(CursorThemeManagerObserver* observer) {
-  cursor_theme_observers_.RemoveObserver(observer);
-}
-
-CursorThemeManager::CursorThemeManager() = default;
-
-}  // namespace ui
diff --git a/ui/base/cursor/cursor_theme_manager.h b/ui/base/cursor/cursor_theme_manager.h
deleted file mode 100644
index 40f96b9..0000000
--- a/ui/base/cursor/cursor_theme_manager.h
+++ /dev/null
@@ -1,49 +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.
-
-#ifndef UI_BASE_CURSOR_CURSOR_THEME_MANAGER_H_
-#define UI_BASE_CURSOR_CURSOR_THEME_MANAGER_H_
-
-#include <string>
-
-#include "base/component_export.h"
-#include "base/observer_list.h"
-#include "ui/base/cursor/cursor_theme_manager_observer.h"
-
-namespace ui {
-
-class COMPONENT_EXPORT(UI_BASE_CURSOR_THEME_MANAGER) CursorThemeManager {
- public:
-  CursorThemeManager(const CursorThemeManager&) = delete;
-  CursorThemeManager& operator=(const CursorThemeManager&) = delete;
-  virtual ~CursorThemeManager();
-
-  static CursorThemeManager* GetInstance();
-
-  static void SetInstance(CursorThemeManager* instance);
-
-  // Adds |observer| and makes initial OnCursorThemNameChanged() and/or
-  // OnCursorThemeSizeChanged() calls if the respective settings were set.
-  void AddObserver(CursorThemeManagerObserver* observer);
-
-  void RemoveObserver(CursorThemeManagerObserver* observer);
-
-  virtual std::string GetCursorThemeName() = 0;
-  virtual int GetCursorThemeSize() = 0;
-
- protected:
-  CursorThemeManager();
-
-  const base::ObserverList<CursorThemeManagerObserver>&
-  cursor_theme_observers() {
-    return cursor_theme_observers_;
-  }
-
- private:
-  base::ObserverList<CursorThemeManagerObserver> cursor_theme_observers_;
-};
-
-}  // namespace ui
-
-#endif  // UI_BASE_CURSOR_CURSOR_THEME_MANAGER_H_
diff --git a/ui/base/x/BUILD.gn b/ui/base/x/BUILD.gn
index 0815385d..f3377bec 100644
--- a/ui/base/x/BUILD.gn
+++ b/ui/base/x/BUILD.gn
@@ -64,7 +64,6 @@
 
   public_deps = [
     "//ui/base/cursor",
-    "//ui/base/cursor:theme_manager",
     "//ui/base/cursor/mojom:cursor_type_shared",
   ]
   deps = [
@@ -93,6 +92,9 @@
     "//ui/strings:ui_strings_grit",
   ]
 
+  if (is_linux) {
+    public_deps += [ "//ui/linux:linux_ui" ]
+  }
   if (is_linux || is_chromeos) {
     sources += [
       "selection_owner.cc",
diff --git a/ui/base/x/DEPS b/ui/base/x/DEPS
index 4a0553f..f8670ca 100644
--- a/ui/base/x/DEPS
+++ b/ui/base/x/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
-  "+ui/platform_window/common/platform_window_defaults.h",
-  "+ui/gl/gl_surface_egl.h",
   "+third_party/khronos/EGL/egl.h",
+  "+ui/gl/gl_surface_egl.h",
+  "+ui/linux",
+  "+ui/platform_window/common/platform_window_defaults.h",
 ]
diff --git a/ui/base/x/x11_cursor_factory.cc b/ui/base/x/x11_cursor_factory.cc
index 46c0560..80f36b0 100644
--- a/ui/base/x/x11_cursor_factory.cc
+++ b/ui/base/x/x11_cursor_factory.cc
@@ -56,9 +56,11 @@
 }
 
 void X11CursorFactory::ObserveThemeChanges() {
-  auto* cursor_theme_manager = CursorThemeManager::GetInstance();
-  DCHECK(cursor_theme_manager);
-  cursor_theme_observation_.Observe(cursor_theme_manager);
+#if BUILDFLAG(IS_LINUX)
+  auto* linux_ui = LinuxUi::instance();
+  DCHECK(linux_ui);
+  cursor_theme_observation_.Observe(linux_ui);
+#endif
 }
 
 void X11CursorFactory::OnCursorThemeNameChanged(
diff --git a/ui/base/x/x11_cursor_factory.h b/ui/base/x/x11_cursor_factory.h
index bf93252..4ee960b 100644
--- a/ui/base/x/x11_cursor_factory.h
+++ b/ui/base/x/x11_cursor_factory.h
@@ -13,9 +13,12 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/scoped_observation.h"
 #include "ui/base/cursor/cursor_factory.h"
-#include "ui/base/cursor/cursor_theme_manager.h"
-#include "ui/base/cursor/cursor_theme_manager_observer.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
+#include "ui/linux/cursor_theme_manager_observer.h"
+
+#if BUILDFLAG(IS_LINUX)
+#include "ui/linux/linux_ui.h"
+#endif
 
 namespace ui {
 class X11Cursor;
@@ -56,8 +59,13 @@
 
   std::map<mojom::CursorType, scoped_refptr<X11Cursor>> default_cursors_;
 
-  base::ScopedObservation<CursorThemeManager, CursorThemeManagerObserver>
+#if BUILDFLAG(IS_LINUX)
+  base::ScopedObservation<LinuxUi,
+                          CursorThemeManagerObserver,
+                          &LinuxUi::AddCursorThemeObserver,
+                          &LinuxUi::RemoveCursorThemeObserver>
       cursor_theme_observation_{this};
+#endif
 };
 
 }  // namespace ui
diff --git a/ui/base/x/x11_cursor_loader.cc b/ui/base/x/x11_cursor_loader.cc
index abdae888..f6acf60 100644
--- a/ui/base/x/x11_cursor_loader.cc
+++ b/ui/base/x/x11_cursor_loader.cc
@@ -27,13 +27,16 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
-#include "ui/base/cursor/cursor_theme_manager.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/gfx/x/connection.h"
 #include "ui/gfx/x/x11_atom_cache.h"
 #include "ui/gfx/x/xproto.h"
 #include "ui/gfx/x/xproto_util.h"
 
+#if BUILDFLAG(IS_LINUX)
+#include "ui/linux/linux_ui.h"
+#endif
+
 extern "C" {
 const char* XcursorLibraryPath(void);
 }
@@ -247,16 +250,17 @@
     const std::string& rm_xcursor_theme) {
   constexpr const char kDefaultTheme[] = "default";
   std::string themes[] = {
-      // The toolkit theme has the highest priority.
-      CursorThemeManager::GetInstance()
-          ? CursorThemeManager::GetInstance()->GetCursorThemeName()
-          : std::string(),
+#if BUILDFLAG(IS_LINUX)
+    // The toolkit theme has the highest priority.
+    LinuxUi::instance() ? LinuxUi::instance()->GetCursorThemeName()
+                        : std::string(),
+#endif
 
-      // Next try Xcursor.theme.
-      rm_xcursor_theme,
+    // Next try Xcursor.theme.
+    rm_xcursor_theme,
 
-      // As a last resort, use the default theme.
-      kDefaultTheme,
+    // As a last resort, use the default theme.
+    kDefaultTheme,
   };
 
   for (const std::string& theme : themes) {
@@ -440,11 +444,13 @@
   if (base::StringToInt(GetEnv(kXcursorSizeEnv), &size) && size > 0)
     return size;
 
+#if BUILDFLAG(IS_LINUX)
   // Let the toolkit have the next say.
-  auto* manager = CursorThemeManager::GetInstance();
-  size = manager ? manager->GetCursorThemeSize() : 0;
+  auto* linux_ui = LinuxUi::instance();
+  size = linux_ui ? linux_ui->GetCursorThemeSize() : 0;
   if (size > 0)
     return size;
+#endif
 
   // Use Xcursor.size from RESOURCE_MANAGER if available.
   if (rm_xcursor_size_)
diff --git a/ui/file_manager/.eslintrc.js b/ui/file_manager/.eslintrc.js
index 7b1f71f..6c48f22a 100644
--- a/ui/file_manager/.eslintrc.js
+++ b/ui/file_manager/.eslintrc.js
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 module.exports = {
-  'rules': {
-    'no-console': 'off',
+  'rules' : {
+    'comma-dangle' : ['error', 'always-multiline'],
+    'no-console' : 'off',
   },
 };
diff --git a/ui/file_manager/audio_player/elements/audio_player.js b/ui/file_manager/audio_player/elements/audio_player.js
index a84a7be..7f1e2dd9 100644
--- a/ui/file_manager/audio_player/elements/audio_player.js
+++ b/ui/file_manager/audio_player/elements/audio_player.js
@@ -33,7 +33,7 @@
       type: Boolean,
       observer: 'playingChanged',
       reflectToAttribute: true,
-      notify: true
+      notify: true,
     },
 
     /**
diff --git a/ui/file_manager/audio_player/elements/control_panel.js b/ui/file_manager/audio_player/elements/control_panel.js
index b88bdb4..ce13a14 100644
--- a/ui/file_manager/audio_player/elements/control_panel.js
+++ b/ui/file_manager/audio_player/elements/control_panel.js
@@ -42,7 +42,7 @@
       value: false,
       notify: true,
       reflectToAttribute: true,
-      observer: 'playingChanged_'
+      observer: 'playingChanged_',
     },
 
     /**
diff --git a/ui/file_manager/audio_player/elements/repeat_button.js b/ui/file_manager/audio_player/elements/repeat_button.js
index 5a76f35..92e3bb77 100644
--- a/ui/file_manager/audio_player/elements/repeat_button.js
+++ b/ui/file_manager/audio_player/elements/repeat_button.js
@@ -33,7 +33,7 @@
       type: String,
       notify: true,
       reflectToAttribute: true,
-    }
+    },
   },
 
   listeners: {
diff --git a/ui/file_manager/audio_player/elements/track_list.js b/ui/file_manager/audio_player/elements/track_list.js
index f3b62432..57bb54b 100644
--- a/ui/file_manager/audio_player/elements/track_list.js
+++ b/ui/file_manager/audio_player/elements/track_list.js
@@ -60,7 +60,7 @@
       type: Boolean,
       value: false,
       observer: 'expandedChanged',
-    }
+    },
   },
 
   /**
diff --git a/ui/file_manager/audio_player/js/audio_player.js b/ui/file_manager/audio_player/js/audio_player.js
index 07c8165..0211ae7 100644
--- a/ui/file_manager/audio_player/js/audio_player.js
+++ b/ui/file_manager/audio_player/js/audio_player.js
@@ -78,8 +78,11 @@
   // if the states are changed.
   const STORAGE_PREFIX = 'audioplayer-';
   const KEYS_TO_SAVE_STATES = [
-    'shuffle', 'repeat-mode', 'volume', 'playlist-expanded',
-    'track-info-expanded'
+    'shuffle',
+    'repeat-mode',
+    'volume',
+    'playlist-expanded',
+    'track-info-expanded',
   ];
   const storageKeys = KEYS_TO_SAVE_STATES.map(a => STORAGE_PREFIX + a);
   chrome.storage.local.get(storageKeys, function(results) {
@@ -137,7 +140,7 @@
         seekSlider: strings['MEDIA_PLAYER_SEEK_SLIDER_LABEL'],
         mute: strings['MEDIA_PLAYER_MUTE_BUTTON_LABEL'],
         unmute: strings['MEDIA_PLAYER_UNMUTE_BUTTON_LABEL'],
-        volumeSlider: strings['MEDIA_PLAYER_VOLUME_SLIDER_LABEL']
+        volumeSlider: strings['MEDIA_PLAYER_VOLUME_SLIDER_LABEL'],
       };
       this.player_.ariaExpandArtworkLabel =
           strings['AUDIO_PLAYER_ARTWORK_EXPAND_BUTTON_LABEL'];
diff --git a/ui/file_manager/audio_player/js/background.js b/ui/file_manager/audio_player/js/background.js
index e38eb71..4636e8d 100644
--- a/ui/file_manager/audio_player/js/background.js
+++ b/ui/file_manager/audio_player/js/background.js
@@ -34,7 +34,7 @@
   minWidth: 320,
   height: 4 + 48 + 96,  // collapsed
   width: 320,
-  frame: {color: '#fafafa'}
+  frame: {color: '#fafafa'},
 };
 
 class AudioPlayerBackground extends BackgroundBaseImpl {
diff --git a/ui/file_manager/file_manager/background/js/background.js b/ui/file_manager/file_manager/background/js/background.js
index 92b176f9..790b7cd 100644
--- a/ui/file_manager/file_manager/background/js/background.js
+++ b/ui/file_manager/file_manager/background/js/background.js
@@ -460,7 +460,7 @@
               // Do not clone the selection url, only the current directory.
               currentDirectoryURL:
                   window.appWindows[key]
-                      .contentWindow.appState.currentDirectoryURL
+                      .contentWindow.appState.currentDirectoryURL,
             };
             launcher.launchFileManager(appState);
           })
@@ -553,7 +553,7 @@
     chrome.contextMenus.create({
       id: 'new-window',
       contexts: ['launcher'],
-      title: str('NEW_WINDOW_BUTTON_LABEL')
+      title: str('NEW_WINDOW_BUTTON_LABEL'),
     });
   }
 }
diff --git a/ui/file_manager/file_manager/background/js/crostini_unittest.js b/ui/file_manager/file_manager/background/js/crostini_unittest.js
index 27f5a972..31a1145 100644
--- a/ui/file_manager/file_manager/background/js/crostini_unittest.js
+++ b/ui/file_manager/file_manager/background/js/crostini_unittest.js
@@ -175,8 +175,13 @@
   // enforces allowed write paths.
 
   const allowed = [
-    'downloads', 'removable', 'android_files', 'drive',
-    'shared_drives_grand_root', 'team_drive', 'drive_shared_with_me'
+    'downloads',
+    'removable',
+    'android_files',
+    'drive',
+    'shared_drives_grand_root',
+    'team_drive',
+    'drive_shared_with_me',
   ];
   for (const type of allowed) {
     volumeManagerRootType = type;
diff --git a/ui/file_manager/file_manager/background/js/device_handler.js b/ui/file_manager/file_manager/background/js/device_handler.js
index 6b1062c0..68680d52 100644
--- a/ui/file_manager/file_manager/background/js/device_handler.js
+++ b/ui/file_manager/file_manager/background/js/device_handler.js
@@ -502,7 +502,7 @@
   // There is one child error.
   CHILD_ERROR: 'childError',
   // There is multiple child results and at least one is failure.
-  MULTIPART_ERROR: 'multipartError'
+  MULTIPART_ERROR: 'multipartError',
 };
 Object.freeze(DeviceHandler.MountStatus);
 
@@ -651,7 +651,7 @@
           message: message || (str(this.message) + additionalMessage),
           iconUrl: getFilesAppIconURL().toString(),
           buttons: buttons,
-          isClickable: this.isClickable
+          isClickable: this.isClickable,
         },
         callback);
     metrics.recordEnum(
diff --git a/ui/file_manager/file_manager/background/js/device_handler_unittest.js b/ui/file_manager/file_manager/background/js/device_handler_unittest.js
index eab1b80..4ce5952 100644
--- a/ui/file_manager/file_manager/background/js/device_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/device_handler_unittest.js
@@ -123,9 +123,9 @@
       isParentDevice: true,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -151,9 +151,9 @@
       isParentDevice: true,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -182,9 +182,9 @@
       isParentDevice: true,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   // Since arcRemovableMediaAccessEnabled is true here, "Play Store apps have
@@ -221,7 +221,7 @@
     eventType: 'mount',
     status: 'success',
     volumeMetadata: {volumeId: 'blabbity', deviceType: 'usb'},
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   reportPromise(
@@ -251,7 +251,7 @@
     eventType: 'mount',
     status: 'success',
     volumeMetadata: {volumeId: 'blabbity', deviceType: 'mtp'},
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   reportPromise(
@@ -269,9 +269,9 @@
       isParentDevice: true,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: false
+    shouldNotify: false,
   });
 
   assertEquals(0, Object.keys(mockChrome.notifications.items).length);
@@ -286,9 +286,9 @@
       isParentDevice: true,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -306,9 +306,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   // Mounting the same device repeatedly should produce only
@@ -320,9 +320,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   reportPromise(
@@ -343,9 +343,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   reportPromise(
@@ -369,7 +369,7 @@
       deviceType: 'usb',
       devicePath: '/device/path',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -394,7 +394,7 @@
       deviceType: 'sd',
       devicePath: '/device/path',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -417,9 +417,9 @@
       isParentDevice: true,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -434,9 +434,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -454,9 +454,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -472,9 +472,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -493,9 +493,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -515,9 +515,9 @@
       deviceType: 'usb',
       devicePath: '/device/path',
       // "RA (U+30E9) BE (U+30D9) RU (U+30EB)" in Katakana letters.
-      deviceLabel: '\u30E9\u30D9\u30EB'
+      deviceLabel: '\u30E9\u30D9\u30EB',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   reportPromise(
@@ -539,9 +539,9 @@
       isParentDevice: true,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -557,9 +557,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -575,9 +575,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -594,9 +594,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 
   await waitUntil(() => {
@@ -645,7 +645,7 @@
   mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({
     type: 'format_success',
     devicePath: '/device/path',
-    deviceLabel: 'label'
+    deviceLabel: 'label',
   });
 
   await waitUntil(() => {
@@ -698,7 +698,7 @@
     mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({
       type: 'partition_success',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     });
     return Promise.resolve(true);
   };
@@ -706,7 +706,7 @@
   mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({
     type: 'partition_start',
     devicePath: '/device/path',
-    deviceLabel: 'label'
+    deviceLabel: 'label',
   });
 }
 
@@ -739,7 +739,7 @@
     mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({
       type: 'partition_fail',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     });
     return Promise.resolve(true);
   };
@@ -748,7 +748,7 @@
   mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({
     type: 'partition_start',
     devicePath: '/device/path',
-    deviceLabel: 'label'
+    deviceLabel: 'label',
   });
 }
 
@@ -853,9 +853,9 @@
       isParentDevice: false,
       deviceType: 'usb',
       devicePath: '/device/path',
-      deviceLabel: 'label'
+      deviceLabel: 'label',
     },
-    shouldNotify: true
+    shouldNotify: true,
   });
 }
 
@@ -876,7 +876,7 @@
   mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({
     type: 'format_success',
     devicePath: '/device/path',
-    deviceLabel: 'label'
+    deviceLabel: 'label',
   });
   assertEquals(0, progressCenter.getItemCount());
 }
@@ -898,7 +898,7 @@
   mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({
     type: 'format_success',
     devicePath: '/device/path',
-    deviceLabel: 'label'
+    deviceLabel: 'label',
   });
   assertEquals(0, progressCenter.getItemCount());
 }
@@ -920,7 +920,7 @@
   mockChrome.fileManagerPrivate.onDeviceChanged.dispatch({
     type: 'format_success',
     devicePath: '/device/path',
-    deviceLabel: 'label'
+    deviceLabel: 'label',
   });
   assertEquals(0, progressCenter.getItemCount());
 }
@@ -954,13 +954,13 @@
         dispatch: null,
         addListener: function(listener) {
           mockChrome.fileManagerPrivate.onDeviceChanged.dispatch = listener;
-        }
+        },
       },
       onMountCompleted: {
         dispatch: null,
         addListener: function(listener) {
           mockChrome.fileManagerPrivate.onMountCompleted.dispatch = listener;
-        }
+        },
       },
       getProfiles: function(callback) {
         callback([{profileId: 'userid@xyz.domain.org'}]);
@@ -969,7 +969,7 @@
         callback({
           arcEnabled: mockChrome.fileManagerPrivate.arcEnabledPref,
           arcRemovableMediaAccessEnabled:
-              mockChrome.fileManagerPrivate.arcRemovableMediaAccessEnabledPref
+              mockChrome.fileManagerPrivate.arcRemovableMediaAccessEnabledPref,
         });
       },
       arcEnabledPref: false,
@@ -978,7 +978,7 @@
     i18n: {
       getUILanguage: function() {
         return 'en-US';
-      }
+      },
     },
     notifications: {
       resolver: new importer.Resolver(),
@@ -1002,24 +1002,24 @@
         dispatch: null,
         addListener: function(listener) {
           mockChrome.notifications.onButtonClicked.dispatch = listener;
-        }
+        },
       },
       onClicked: {
         dispatch: null,
         addListener: function(listener) {
           mockChrome.notifications.onClicked.dispatch = listener;
-        }
+        },
       },
       getAll: function(callback) {
         callback([]);
-      }
+      },
     },
     runtime: {
       getURL: function(path) {
         return path;
       },
-      onStartup: {addListener: function() {}}
-    }
+      onStartup: {addListener: function() {}},
+    },
   };
 
   installMockChrome(mockChrome);
diff --git a/ui/file_manager/file_manager/background/js/drive_sync_handler.js b/ui/file_manager/file_manager/background/js/drive_sync_handler.js
index bf42c726..1d2e1fd 100644
--- a/ui/file_manager/file_manager/background/js/drive_sync_handler.js
+++ b/ui/file_manager/file_manager/background/js/drive_sync_handler.js
@@ -129,7 +129,7 @@
           {single: 'SYNC_FILE_NAME', plural: 'SYNC_FILE_NUMBER'},
       [this.pinItem_.id]: {
         single: 'OFFLINE_PROGRESS_MESSAGE',
-        plural: 'OFFLINE_PROGRESS_MESSAGE_PLURAL'
+        plural: 'OFFLINE_PROGRESS_MESSAGE_PLURAL',
       },
     };
     Object.freeze(this.statusMessages_);
@@ -216,7 +216,7 @@
           message: str('DISABLED_MOBILE_SYNC_NOTIFICATION_MESSAGE'),
           iconUrl: getFilesAppIconURL().toString(),
           buttons:
-              [{title: str('DISABLED_MOBILE_SYNC_NOTIFICATION_ENABLE_BUTTON')}]
+              [{title: str('DISABLED_MOBILE_SYNC_NOTIFICATION_ENABLE_BUTTON')}],
         },
         () => {});
   }
@@ -437,7 +437,7 @@
             buttons: [
               {title: str('OFFLINE_ENABLE_REJECT')},
               {title: str('OFFLINE_ENABLE_ACCEPT')},
-            ]
+            ],
           },
           () => {});
       this.savedDialogEvent_ = event;
diff --git a/ui/file_manager/file_manager/background/js/drive_sync_handler_unittest.js b/ui/file_manager/file_manager/background/js/drive_sync_handler_unittest.js
index efe6daf..00b149e 100644
--- a/ui/file_manager/file_manager/background/js/drive_sync_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/drive_sync_handler_unittest.js
@@ -44,7 +44,7 @@
     removeListener: function() {
       mockChrome.fileManagerPrivate.onFileTransfersUpdated.listener_ = null;
     },
-    listener_: null
+    listener_: null,
   },
   onPinTransfersUpdated: {
     addListener: function(callback) {
@@ -53,7 +53,7 @@
     removeListener: function() {
       mockChrome.fileManagerPrivate.onPinTransfersUpdated.listener_ = null;
     },
-    listener_: null
+    listener_: null,
   },
   onDriveSyncError: {
     addListener: function(callback) {
@@ -62,7 +62,7 @@
     removeListener: function() {
       mockChrome.fileManagerPrivate.onDriveSyncError.listener_ = null;
     },
-    listener_: null
+    listener_: null,
   },
   onDriveConfirmDialog: {
     addListener: function(callback) {
@@ -71,7 +71,7 @@
     removeListener: function() {
       mockChrome.fileManagerPrivate.onDriveConfirmDialog.listener_ = null;
     },
-    listener_: null
+    listener_: null,
   },
   onPreferencesChanged: {
     addListener: function(callback) {
@@ -80,7 +80,7 @@
     removeListener: function() {
       mockChrome.fileManagerPrivate.onPreferencesChanged.listener_ = null;
     },
-    listener_: null
+    listener_: null,
   },
   onDriveConnectionStatusChanged: {
     addListener: function(callback) {
@@ -91,7 +91,7 @@
       mockChrome.fileManagerPrivate.onDriveConnectionStatusChanged.listener_ =
           null;
     },
-    listener_: null
+    listener_: null,
   },
   onMountCompleted: {
     addListener: function(callback) {
@@ -100,7 +100,7 @@
     removeListener: function() {
       mockChrome.fileManagerPrivate.onMountCompleted.listener_ = null;
     },
-    listener_: null
+    listener_: null,
   },
   getPreferences: function() {},
   setPreferences: function() {},
@@ -118,7 +118,7 @@
     removeListener: function() {
       mockChrome.notifications.onButtonClicked.listener_ = null;
     },
-    listener_: null
+    listener_: null,
   },
   onClosed: {
     addListener: function(callback) {
@@ -127,7 +127,7 @@
     removeListener: function() {
       mockChrome.notifications.onClosed.listener_ = null;
     },
-    listener_: null
+    listener_: null,
   },
 };
 
diff --git a/ui/file_manager/file_manager/background/js/duplicate_finder.js b/ui/file_manager/file_manager/background/js/duplicate_finder.js
index 6c33400..c3bcd3a 100644
--- a/ui/file_manager/file_manager/background/js/duplicate_finder.js
+++ b/ui/file_manager/file_manager/background/js/duplicate_finder.js
@@ -260,7 +260,7 @@
           return Promise
               .all([
                 history.wasCopied(entry, destination),
-                history.wasImported(entry, destination)
+                history.wasImported(entry, destination),
               ])
               .then(
                   /**
diff --git a/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js b/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js
index 3b44c06..63039f36 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js
@@ -79,7 +79,7 @@
     COPY_TARGET_EXISTS_ERROR: '$1 is already exists.',
     COPY_FILESYSTEM_ERROR: 'Copy filesystem error: $1',
     FILE_ERROR_GENERIC: 'File error generic.',
-    COPY_UNEXPECTED_ERROR: 'Copy unexpected error: $1'
+    COPY_UNEXPECTED_ERROR: 'Copy unexpected error: $1',
   });
 
   // Install mock chrome APIs.
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager.js b/ui/file_manager/file_manager/background/js/file_operation_manager.js
index 2912563..d335a09 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager.js
@@ -497,7 +497,7 @@
           processedBytes: 0,
           cancelRequested: false,
           trashedEntries: [],
-          permanentlyDelete
+          permanentlyDelete,
         }));
 
     // Obtains entry size and sum them up.
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
index 15cb4ae1..b90b1ad 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
@@ -24,7 +24,7 @@
 const mockChrome = {};
 
 mockChrome.runtime = {
-  lastError: null
+  lastError: null,
 };
 
 mockChrome.power = {
@@ -34,7 +34,7 @@
   releaseKeepAwake: function() {
     mockChrome.power.keepAwakeRequested = false;
   },
-  keepAwakeRequested: false
+  keepAwakeRequested: false,
 };
 
 mockChrome.fileManagerPrivate = {
@@ -45,7 +45,7 @@
     removeListener: function() {
       mockChrome.fileManagerPrivate.onCopyProgress.listener_ = null;
     },
-    listener_: null
+    listener_: null,
   },
 
 };
@@ -119,7 +119,7 @@
       return {
         type: type,
         sourceUrl: source.toURL(),
-        destinationUrl: destination.toURL()
+        destinationUrl: destination.toURL(),
       };
     };
 
@@ -171,7 +171,7 @@
     return /** @type {!EntryLocation} */ ({
       rootType: 'downloads',
       volumeInfo:
-          {volumeType: 'downloads', label: 'Downloads', remoteMountPath: ''}
+          {volumeType: 'downloads', label: 'Downloads', remoteMountPath: ''},
     });
   }
 }
@@ -525,7 +525,7 @@
           return {
             type: type,
             sourceUrl: source.toURL(),
-            destinationUrl: destination.toURL()
+            destinationUrl: destination.toURL(),
           };
         };
         callback(1);
@@ -776,7 +776,7 @@
     },
     getLocationInfo: function() {
       return null;
-    }
+    },
   };
   fileOperationManager = new FileOperationManagerImpl();
 
@@ -975,7 +975,7 @@
       return {
         type: type,
         sourceUrl: source.toURL(),
-        destinationUrl: destination.toURL()
+        destinationUrl: destination.toURL(),
       };
     };
 
diff --git a/ui/file_manager/file_manager/background/js/file_operation_util.js b/ui/file_manager/file_manager/background/js/file_operation_util.js
index 78e3825..4d529c6c 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_util.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_util.js
@@ -674,7 +674,7 @@
       processedBytes: this.processedBytes,
       processingEntryName: processingEntry ? processingEntry.name : '',
       targetDirEntryName: this.targetDirEntry.name,
-      remainingTime: this.speedometer_.getRemainingTime()
+      remainingTime: this.speedometer_.getRemainingTime(),
     };
   }
 
diff --git a/ui/file_manager/file_manager/background/js/import_history.js b/ui/file_manager/file_manager/background/js/import_history.js
index 21aa55b9..7fbc319 100644
--- a/ui/file_manager/file_manager/background/js/import_history.js
+++ b/ui/file_manager/file_manager/background/js/import_history.js
@@ -19,7 +19,7 @@
  */
 importerHistory.ImportHistoryState = {
   'COPIED': 'copied',
-  'IMPORTED': 'imported'
+  'IMPORTED': 'imported',
 };
 
 /**
@@ -27,7 +27,7 @@
  */
 importerHistory.RecordType_ = {
   COPY: 0,
-  IMPORT: 1
+  IMPORT: 1,
 };
 
 /**
@@ -192,7 +192,7 @@
     if (!this.copiedEntries_[key].hasOwnProperty(destination)) {
       this.copiedEntries_[key][destination] = {
         sourceUrl: sourceUrl,
-        destinationUrl: destinationUrl
+        destinationUrl: destinationUrl,
       };
       return true;
     }
@@ -238,9 +238,11 @@
              */
             key => {
               return this.storeRecord_([
-                importerHistory.RecordType_.COPY, key, destination,
+                importerHistory.RecordType_.COPY,
+                key,
+                destination,
                 importer.deflateAppUrl(entry.toURL()),
-                importer.deflateAppUrl(destinationUrl)
+                importer.deflateAppUrl(destinationUrl),
               ]);
             })
         .then(this.notifyObservers_.bind(
diff --git a/ui/file_manager/file_manager/background/js/import_history_unittest.js b/ui/file_manager/file_manager/background/js/import_history_unittest.js
index 60d6d15d..0e45e4e 100644
--- a/ui/file_manager/file_manager/background/js/import_history_unittest.js
+++ b/ui/file_manager/file_manager/background/js/import_history_unittest.js
@@ -64,7 +64,7 @@
       testFileSystem, FILE_PATH,
       /** @type Metadata */ ({
         size: FILE_SIZE,
-        modificationTime: FILE_LAST_MODIFIED
+        modificationTime: FILE_LAST_MODIFIED,
       }));
 
   testFileSystem.entries[FILE_PATH] = testFileEntry;
@@ -251,7 +251,7 @@
 export function testRecordStorage_SerializingOperations(callback) {
   const recorder = new TestCallRecorder();
   testPromise = createRealStorage([
-                  'recordStorageTestForSerializing.data'
+                  'recordStorageTestForSerializing.data',
                 ]).then(storage => {
     const writePromises = [];
     const WRITES_COUNT = 20;
diff --git a/ui/file_manager/file_manager/background/js/launcher.js b/ui/file_manager/file_manager/background/js/launcher.js
index c9c3a13..620f704f 100644
--- a/ui/file_manager/file_manager/background/js/launcher.js
+++ b/ui/file_manager/file_manager/background/js/launcher.js
@@ -21,7 +21,7 @@
 export const LaunchType = {
   ALWAYS_CREATE: 0,
   FOCUS_ANY_OR_CREATE: 1,
-  FOCUS_SAME_OR_CREATE: 2
+  FOCUS_SAME_OR_CREATE: 2,
 };
 
 /**
@@ -47,11 +47,11 @@
     // We choose 1000px as default window width to fit 4 columns in grid view,
     // as long as the width doesn't exceed 80% of the screen width.
     width: Math.min(Math.round(window.screen.availWidth * 0.8), 1000),
-    height: Math.min(Math.round(window.screen.availHeight * 0.8), 600)
+    height: Math.min(Math.round(window.screen.availHeight * 0.8), 600),
   },
   frame: {color: '#ffffff'},
   minWidth: 480,
-  minHeight: 300
+  minHeight: 300,
 };
 
 /**
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler.js b/ui/file_manager/file_manager/background/js/media_import_handler.js
index 3bd1e9b..63bcaf16 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler.js
@@ -619,5 +619,5 @@
  * @enum {string}
  */
 mediaImport.UpdateType = {
-  ENTRY_CHANGED: 'ENTRY_CHANGED'
+  ENTRY_CHANGED: 'ENTRY_CHANGED',
 };
diff --git a/ui/file_manager/file_manager/background/js/media_scanner.js b/ui/file_manager/file_manager/background/js/media_scanner.js
index fc53763..1802615 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner.js
@@ -503,7 +503,7 @@
         total: this.candidateCount_,
         processed: this.candidatesProcessed_,
       },
-      progress: this.calculateProgress_()
+      progress: this.calculateProgress_(),
     };
   }
 
diff --git a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js b/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
index 0d69951..94b0644 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
@@ -20,7 +20,7 @@
  */
 const metrics = {
   recordTime: function() {},
-  recordValue: function() {}
+  recordValue: function() {},
 };
 
 /** @type {!mediaScanner.DefaultMediaScanner} */
@@ -180,7 +180,7 @@
   const expectedFiles = [
     '/testScanFilesIgnoresPreviousImports/foo.jpg',
     '/testScanFilesIgnoresPreviousImports/bar.gif',
-    '/testScanFilesIgnoresPreviousImports/baz.avi'
+    '/testScanFilesIgnoresPreviousImports/baz.avi',
   ];
   reportPromise(
       makeTestFileSystemRoot('testScanFilesIgnoresPreviousImports')
@@ -380,7 +380,8 @@
 
 export function testMultiLevel(callback) {
   const filenames = [
-    'foo.jpg', 'bar',
+    'foo.jpg',
+    'bar',
     [
       'dir1',
       'bar.0.jpg',
@@ -392,7 +393,7 @@
         'dir3',
         'bar.1.0.avi',
       ],
-    ]
+    ],
   ];
   const expectedFiles = [
     '/testMultiLevel/foo.jpg',
@@ -418,7 +419,8 @@
 
 export function testDedupesFilesInScanResult(callback) {
   const filenames = [
-    'foo.jpg', 'bar.jpg',
+    'foo.jpg',
+    'bar.jpg',
     [
       'dir1',
       'foo.jpg',
@@ -433,7 +435,7 @@
         'foo.jpg',
         'bar.jpg',
       ],
-    ]
+    ],
   ];
   const expectedFiles = [
     '/testDedupesFilesInScanResult/foo.jpg',
diff --git a/ui/file_manager/file_manager/background/js/mock_file_operation_manager.js b/ui/file_manager/file_manager/background/js/mock_file_operation_manager.js
index bacbeae..df00226 100644
--- a/ui/file_manager/file_manager/background/js/mock_file_operation_manager.js
+++ b/ui/file_manager/file_manager/background/js/mock_file_operation_manager.js
@@ -55,7 +55,7 @@
         sourceEntries: sourceEntries,
         targetEntry: targetEntry,
         isMove: isMove,
-        opt_taskId: opt_taskId
+        opt_taskId: opt_taskId,
       });
       // Reset the resolver for the next paste call.
       this.pasteResolver = null;
diff --git a/ui/file_manager/file_manager/background/js/mock_media_scanner.js b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
index 54e9274..695e190 100644
--- a/ui/file_manager/file_manager/background/js/mock_media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
@@ -239,7 +239,7 @@
       scanDuration: this.scanDuration,
       newFileCount: this.fileEntries.length,
       duplicates: duplicates,
-      sizeBytes: this.totalBytes
+      sizeBytes: this.totalBytes,
     });
   }
 
diff --git a/ui/file_manager/file_manager/background/js/mount_metrics_unittest.js b/ui/file_manager/file_manager/background/js/mount_metrics_unittest.js
index bb16ece..f9420715c 100644
--- a/ui/file_manager/file_manager/background/js/mount_metrics_unittest.js
+++ b/ui/file_manager/file_manager/background/js/mount_metrics_unittest.js
@@ -39,7 +39,7 @@
   volumeMetadata: {
     volumeType: VolumeManagerCommon.VolumeType.PROVIDED,
     providerId: 'fubar',
-  }
+  },
 });
 
 // Set up the test components.
@@ -57,8 +57,8 @@
               listener => {
                 listener(event);
               });
-        }
-      }
+        },
+      },
     },
   };
   installMockChrome(mockChrome);
diff --git a/ui/file_manager/file_manager/background/js/progress_center.js b/ui/file_manager/file_manager/background/js/progress_center.js
index f818446..9d8a94d 100644
--- a/ui/file_manager/file_manager/background/js/progress_center.js
+++ b/ui/file_manager/file_manager/background/js/progress_center.js
@@ -298,7 +298,7 @@
             item.progressRateInPercent :
             undefined,
         priority: (item.state === ProgressItemState.ERROR || !item.quiet) ? 0 :
-                                                                            -1
+                                                                            -1,
       };
 
       if (newlyAdded) {
@@ -356,5 +356,5 @@
  */
 ProgressCenterImpl.Notifications_.NotificationState_ = {
   VISIBLE: 'visible',
-  DISMISSED: 'dismissed'
+  DISMISSED: 'dismissed',
 };
diff --git a/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js b/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
index 1120f02..9f0a62c 100644
--- a/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
+++ b/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
@@ -84,7 +84,7 @@
     // The hidden attribute is not in the element.attributes even if
     // element.hasAttribute('hidden') is true.
     hidden: !!element.hidden,
-    hasShadowRoot: !!element.shadowRoot
+    hasShadowRoot: !!element.shadowRoot,
   };
 
   const styleNames = opt_styleNames || [];
@@ -132,13 +132,13 @@
     const windowWrapper = window.appWindows[id];
     windows[id] = {
       outerWidth: windowWrapper.contentWindow.outerWidth,
-      outerHeight: windowWrapper.contentWindow.outerHeight
+      outerHeight: windowWrapper.contentWindow.outerHeight,
     };
   }
   for (const id in window.background.dialogs) {
     windows[id] = {
       outerWidth: window.background.dialogs[id].outerWidth,
-      outerHeight: window.background.dialogs[id].outerHeight
+      outerHeight: window.background.dialogs[id].outerHeight,
     };
   }
   return windows;
diff --git a/ui/file_manager/file_manager/background/js/test_util.js b/ui/file_manager/file_manager/background/js/test_util.js
index 7d71bb5..466672f 100644
--- a/ui/file_manager/file_manager/background/js/test_util.js
+++ b/ui/file_manager/file_manager/background/js/test_util.js
@@ -64,7 +64,7 @@
       row.querySelector('.filename-label').textContent,
       row.querySelector('.size').textContent,
       row.querySelector('.type').textContent,
-      row.querySelector('.date').textContent
+      row.querySelector('.date').textContent,
     ]);
   }
   return fileList;
@@ -185,7 +185,7 @@
           callback(
               test.util.sync.fakeMouseDown(contentWindow, query) &&
               test.util.sync.fakeMouseClick(contentWindow, query));
-        }
+        },
       };
       steps.checkQuery();
     };
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_factory.js b/ui/file_manager/file_manager/background/js/volume_manager_factory.js
index 7c4396d5..85804d7 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_factory.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_factory.js
@@ -56,7 +56,7 @@
   return {
     getInstance: getInstance,
     getInstanceForDebug: getInstanceForDebug,
-    revokeInstanceForTesting: revokeInstanceForTesting
+    revokeInstanceForTesting: revokeInstanceForTesting,
   };
 })();
 
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_impl.js b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
index febe6bb2..5c4e9d0 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
@@ -571,7 +571,7 @@
           errorCallbacks: [errorCallback],
 
           timeout: setTimeout(
-              this.onTimeout_.bind(this, key), volumeManagerUtil.TIMEOUT)
+              this.onTimeout_.bind(this, key), volumeManagerUtil.TIMEOUT),
         };
       }
     });
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_unittest.js b/ui/file_manager/file_manager/background/js/volume_manager_unittest.js
index a162c93..7442928 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_unittest.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_unittest.js
@@ -57,7 +57,7 @@
         const event = {
           eventType: 'unmount',
           status: 'success',
-          volumeMetadata: {volumeId: volumeId}
+          volumeMetadata: {volumeId: volumeId},
         };
         mockChrome.fileManagerPrivate.onMountCompleted.dispatchEvent(event);
         callback();
@@ -72,7 +72,7 @@
               .forEach(listener => {
                 listener(event);
               });
-        }
+        },
       },
       onMountCompleted: {
         addListener: function(listener) {
@@ -84,7 +84,7 @@
               listener => {
                 listener(event);
               });
-        }
+        },
       },
       getDriveConnectionState: function(callback) {
         callback(mockChrome.fileManagerPrivate.driveConnectionState_);
@@ -97,7 +97,7 @@
         mockChrome.fileManagerPrivate.onDriveConnectionStatusChanged
             .dispatchEvent(null);
       },
-    }
+    },
   };
   installMockChrome(mockChrome);
   new MockCommandLinePrivate();
@@ -110,7 +110,7 @@
       profile: getMockProfile(),
       configurable: false,
       watchable: true,
-      source: VolumeManagerCommon.Source.SYSTEM
+      source: VolumeManagerCommon.Source.SYSTEM,
     },
     {
       volumeId: 'drive:drive-foobar%40chromium.org-hash',
@@ -120,7 +120,7 @@
       profile: getMockProfile(),
       configurable: false,
       watchable: true,
-      source: VolumeManagerCommon.Source.NETWORK
+      source: VolumeManagerCommon.Source.NETWORK,
     },
     {
       volumeId: 'android_files:0',
@@ -130,14 +130,14 @@
       provile: getMockProfile(),
       configurable: false,
       watchable: true,
-      source: VolumeManagerCommon.Source.SYSTEM
-    }
+      source: VolumeManagerCommon.Source.SYSTEM,
+    },
   ];
   chrome.fileManagerPrivate.fileSystemMap_ = {
     'download:Downloads': new MockFileSystem('download:Downloads'),
     'drive:drive-foobar%40chromium.org-hash':
         new MockFileSystem('drive:drive-foobar%40chromium.org-hash'),
-    'android_files:0': new MockFileSystem('android_files:0')
+    'android_files:0': new MockFileSystem('android_files:0'),
   };
 
   createVolumeInfoOriginal = volumeManagerUtil.createVolumeInfo;
@@ -167,7 +167,7 @@
   return {
     displayName: 'foobar@chromium.org',
     isCurrentProfile: true,
-    profileId: ''
+    profileId: '',
   };
 }
 
@@ -543,8 +543,8 @@
         volumeId: 'drive:drive-foobar%40chromium.org-hash',
         volumeType: VolumeManagerCommon.VolumeType.DRIVE,
         sourcePath: '/drive',
-        profile: getMockProfile()
-      }
+        profile: getMockProfile(),
+      },
     });
 
     // Wait until volume manager initialization calls getVolumeMetadataList().
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_util.js b/ui/file_manager/file_manager/background/js/volume_manager_util.js
index a26a2398..f2bfddc 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_util.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_util.js
@@ -106,7 +106,7 @@
             chrome.fileManagerPrivate.getVolumeRoot(
                 {
                   volumeId: volumeMetadata.volumeId,
-                  writable: !volumeMetadata.isReadOnly
+                  writable: !volumeMetadata.isReadOnly,
                 },
                 rootDirectoryEntry => {
                   if (chrome.runtime.lastError) {
diff --git a/ui/file_manager/file_manager/common/js/app_util.js b/ui/file_manager/file_manager/common/js/app_util.js
index 65a427b..3334d782 100644
--- a/ui/file_manager/file_manager/common/js/app_util.js
+++ b/ui/file_manager/file_manager/common/js/app_util.js
@@ -91,7 +91,7 @@
     if (value != null) {
       map[key] = {
         value: value,
-        expire: Date.now() + (opt_lifetime || appUtil.AppCache.LIFETIME)
+        expire: Date.now() + (opt_lifetime || appUtil.AppCache.LIFETIME),
       };
     } else if (key in map) {
       delete map[key];
diff --git a/ui/file_manager/file_manager/common/js/dialog_type.js b/ui/file_manager/file_manager/common/js/dialog_type.js
index a5426be..1911db70 100644
--- a/ui/file_manager/file_manager/common/js/dialog_type.js
+++ b/ui/file_manager/file_manager/common/js/dialog_type.js
@@ -17,7 +17,7 @@
   SELECT_SAVEAS_FILE: 'saveas-file',
   SELECT_OPEN_FILE: 'open-file',
   SELECT_OPEN_MULTI_FILE: 'open-multi-file',
-  FULL_PAGE: 'full-page'
+  FULL_PAGE: 'full-page',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/common/js/file_operation_common.js b/ui/file_manager/file_manager/common/js/file_operation_common.js
index 823b9c0..510eb82 100644
--- a/ui/file_manager/file_manager/common/js/file_operation_common.js
+++ b/ui/file_manager/file_manager/common/js/file_operation_common.js
@@ -46,7 +46,7 @@
   CANCELED: 'CANCELED',
   ERROR: 'ERROR',
   PROGRESS: 'PROGRESS',
-  SUCCESS: 'SUCCESS'
+  SUCCESS: 'SUCCESS',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/common/js/file_type.js b/ui/file_manager/file_manager/common/js/file_type.js
index 62d2f6c..8481776 100644
--- a/ui/file_manager/file_manager/common/js/file_type.js
+++ b/ui/file_manager/file_manager/common/js/file_type.js
@@ -29,7 +29,7 @@
   translationKey: 'FOLDER',
   type: '.folder',
   icon: 'folder',
-  subtype: ''
+  subtype: '',
 };
 
 /**
@@ -41,7 +41,7 @@
   translationKey: 'NO_EXTENSION_FILE_TYPE',
   type: 'UNKNOWN',
   icon: '',
-  subtype: ''
+  subtype: '',
 };
 
 /**
@@ -115,7 +115,7 @@
     translationKey: 'GENERIC_FILE_TYPE',
     type: 'UNKNOWN',
     subtype: extension.substr(1).toUpperCase(),
-    icon: ''
+    icon: '',
   };
 };
 
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js b/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js
index 301fc65..d8ea4d1 100644
--- a/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js
+++ b/ui/file_manager/file_manager/common/js/files_app_entry_types_unittest.js
@@ -32,7 +32,7 @@
   const fakeVolumeInfo = {
     displayRoot: displayRoot,
     label: kLabel,
-    volumeType: volumeType
+    volumeType: volumeType,
   };
   Object.assign(fakeVolumeInfo, additionalProperties || {});
   // Create the VolumeEntry via casting (duck typing).
@@ -127,7 +127,7 @@
     name: 'Linux files',
     toURL: function() {
       return 'fake-entry://linux-files';
-    }
+    },
   });
 
   entryList.addEntry(downloads);
@@ -178,7 +178,7 @@
     name: 'Linux files',
     toURL: function() {
       return 'fake-entry://linux-files';
-    }
+    },
   });
 
   volumeEntry.addEntry(crostini);
@@ -500,7 +500,7 @@
         successCallback(fakeRootEntry);
         callbackTriggered = true;
       }, 0);
-    }
+    },
   });
 
   // rootEntry_ starts as null.
diff --git a/ui/file_manager/file_manager/common/js/importer_common.js b/ui/file_manager/file_manager/common/js/importer_common.js
index 5abbb18..c002194 100644
--- a/ui/file_manager/file_manager/common/js/importer_common.js
+++ b/ui/file_manager/file_manager/common/js/importer_common.js
@@ -21,14 +21,14 @@
   PROGRESS: 'PROGRESS',
   COMPLETE: 'COMPLETE',
   ERROR: 'ERROR',
-  CANCELED: 'CANCELED'
+  CANCELED: 'CANCELED',
 };
 
 /** @enum {string} */
 importer.ScanEvent = {
   FINALIZED: 'finalized',
   INVALIDATED: 'invalidated',
-  UPDATED: 'updated'
+  UPDATED: 'updated',
 };
 
 /**
@@ -53,7 +53,7 @@
   CONTENT_DUPLICATE: 'content-dupe',
   HISTORY_DUPLICATE: 'history-dupe',
   ORIGINAL: 'original',
-  SCAN_DUPLICATE: 'scan-dupe'
+  SCAN_DUPLICATE: 'scan-dupe',
 };
 
 /**
@@ -63,7 +63,7 @@
 importer.Setting = {
   HAS_COMPLETED_IMPORT: 'importer-has-completed-import',
   MACHINE_ID: 'importer-machine-id',
-  LAST_KNOWN_LOG_ID: 'importer-last-known-log-id'
+  LAST_KNOWN_LOG_ID: 'importer-last-known-log-id',
 };
 
 /**
@@ -81,7 +81,7 @@
  */
 importer.ValidImportRoots_ = {
   DCIM: 'DCIM',
-  MP_ROOT: 'MP_ROOT'  // MP_ROOT is a Sony thing.
+  MP_ROOT: 'MP_ROOT',  // MP_ROOT is a Sony thing.
 };
 
 /**
@@ -90,7 +90,7 @@
 importer.Destination = {
   // locally copied, but not imported to cloud as of yet.
   DEVICE: 'device',
-  GOOGLE_DRIVE: 'google-drive'
+  GOOGLE_DRIVE: 'google-drive',
 };
 
 /**
@@ -383,7 +383,7 @@
   return Promise
       .all([
         importer.getOrCreateHistoryFile(),
-        importer.getMachineId().then(importer.getUnownedHistoryFiles_)
+        importer.getMachineId().then(importer.getUnownedHistoryFiles_),
       ])
       .then(
           /** @param {!Array<!FileEntry|!Array<!FileEntry>>} entries */
diff --git a/ui/file_manager/file_manager/common/js/importer_common_unittest.js b/ui/file_manager/file_manager/common/js/importer_common_unittest.js
index 6ddd309..7820fd74 100644
--- a/ui/file_manager/file_manager/common/js/importer_common_unittest.js
+++ b/ui/file_manager/file_manager/common/js/importer_common_unittest.js
@@ -144,7 +144,7 @@
             return Promise.all([
               storage.get('lamb').then(assertEquals.bind(null, 'chop')),
               storage.get('isPoodle').then(assertEquals.bind(null, true)),
-              storage.get('age of grandma').then(assertEquals.bind(null, 103))
+              storage.get('age of grandma').then(assertEquals.bind(null, 103)),
             ]);
           });
 
@@ -249,7 +249,7 @@
   const entry =
       MockFileEntry.create(volume.fileSystem, path, /** @type{Metadata} */ ({
                              size: 1234,
-                             modificationTime: new Date().toString()
+                             modificationTime: new Date().toString(),
                            }));
   // Ensure the file entry has a volumeID...necessary for lookups
   // via the VolumeManager.
diff --git a/ui/file_manager/file_manager/common/js/mediasession_types.js b/ui/file_manager/file_manager/common/js/mediasession_types.js
index 5a6b93c1..754ee76 100644
--- a/ui/file_manager/file_manager/common/js/mediasession_types.js
+++ b/ui/file_manager/file_manager/common/js/mediasession_types.js
@@ -13,5 +13,5 @@
 export const MediaSessionPlaybackState = {
   NONE: 'none',
   PAUSED: 'paused',
-  PLAYING: 'playing'
+  PLAYING: 'playing',
 };
diff --git a/ui/file_manager/file_manager/common/js/metrics_base.js b/ui/file_manager/file_manager/common/js/metrics_base.js
index 9324208..ab07cff 100644
--- a/ui/file_manager/file_manager/common/js/metrics_base.js
+++ b/ui/file_manager/file_manager/common/js/metrics_base.js
@@ -130,9 +130,9 @@
       'type': type,
       'min': min,
       'max': max,
-      'buckets': buckets
+      'buckets': buckets,
     },
-    value
+    value,
   ]);
 };
 
@@ -222,7 +222,7 @@
     'type': chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LINEAR,
     'min': 1,
     'max': boundaryValue - 1,
-    'buckets': boundaryValue
+    'buckets': boundaryValue,
   };
   metricsBase.call_('recordValue', [metricDescr, index]);
 };
diff --git a/ui/file_manager/file_manager/common/js/notifications_browser_proxy.js b/ui/file_manager/file_manager/common/js/notifications_browser_proxy.js
index a5d5b55..030f55e 100644
--- a/ui/file_manager/file_manager/common/js/notifications_browser_proxy.js
+++ b/ui/file_manager/file_manager/common/js/notifications_browser_proxy.js
@@ -53,7 +53,7 @@
 const NotificationEventTypes = {
   CLICKED: 'onClicked',
   BUTTON_CLICKED: 'onButtonClicked',
-  CLOSED: 'onClosed'
+  CLOSED: 'onClosed',
 };
 
 Object.freeze(NotificationEventTypes);
diff --git a/ui/file_manager/file_manager/common/js/progress_center_common.js b/ui/file_manager/file_manager/common/js/progress_center_common.js
index 7b256f4..43aad3c 100644
--- a/ui/file_manager/file_manager/common/js/progress_center_common.js
+++ b/ui/file_manager/file_manager/common/js/progress_center_common.js
@@ -10,7 +10,7 @@
   PROGRESSING: 'progressing',
   COMPLETED: 'completed',
   ERROR: 'error',
-  CANCELED: 'canceled'
+  CANCELED: 'canceled',
 };
 Object.freeze(ProgressItemState);
 
@@ -43,7 +43,7 @@
   // The item is archive operation.
   MOUNT_ARCHIVE: 'mount_archive',
   // The item is external drive partitioning operation.
-  PARTITION: 'partition'
+  PARTITION: 'partition',
 };
 Object.freeze(ProgressItemType);
 
diff --git a/ui/file_manager/file_manager/common/js/recent_date_bucket.js b/ui/file_manager/file_manager/common/js/recent_date_bucket.js
index cd1a9e1b..a2f6edcf 100644
--- a/ui/file_manager/file_manager/common/js/recent_date_bucket.js
+++ b/ui/file_manager/file_manager/common/js/recent_date_bucket.js
@@ -56,27 +56,27 @@
   const DATE_BUCKET_TO_TRANSLATION_KEY_MAP = new Map([
     [
       chrome.fileManagerPrivate.RecentDateBucket.TODAY,
-      'RECENT_TIME_HEADING_TODAY'
+      'RECENT_TIME_HEADING_TODAY',
     ],
     [
       chrome.fileManagerPrivate.RecentDateBucket.YESTERDAY,
-      'RECENT_TIME_HEADING_YESTERDAY'
+      'RECENT_TIME_HEADING_YESTERDAY',
     ],
     [
       chrome.fileManagerPrivate.RecentDateBucket.EARLIER_THIS_WEEK,
-      'RECENT_TIME_HEADING_THIS_WEEK'
+      'RECENT_TIME_HEADING_THIS_WEEK',
     ],
     [
       chrome.fileManagerPrivate.RecentDateBucket.EARLIER_THIS_MONTH,
-      'RECENT_TIME_HEADING_THIS_MONTH'
+      'RECENT_TIME_HEADING_THIS_MONTH',
     ],
     [
       chrome.fileManagerPrivate.RecentDateBucket.EARLIER_THIS_YEAR,
-      'RECENT_TIME_HEADING_THIS_YEAR'
+      'RECENT_TIME_HEADING_THIS_YEAR',
     ],
     [
       chrome.fileManagerPrivate.RecentDateBucket.OLDER,
-      'RECENT_TIME_HEADING_OLDER'
+      'RECENT_TIME_HEADING_OLDER',
     ],
   ]);
   return DATE_BUCKET_TO_TRANSLATION_KEY_MAP.get(dateBucket);
diff --git a/ui/file_manager/file_manager/common/js/recent_date_bucket_unittest.js b/ui/file_manager/file_manager/common/js/recent_date_bucket_unittest.js
index 486afbb..254b02ab7 100644
--- a/ui/file_manager/file_manager/common/js/recent_date_bucket_unittest.js
+++ b/ui/file_manager/file_manager/common/js/recent_date_bucket_unittest.js
@@ -24,7 +24,7 @@
         EARLIER_THIS_YEAR: 'earlier_this_year',
         OLDER: 'older',
       },
-    }
+    },
   };
   installMockChrome(mockChrome);
 }
@@ -53,7 +53,7 @@
         {
           // May 9, 2022, Monday, 08:30am Local time.
           date: new Date(2022, 4, 9, 8, 30, 0),
-          bucket: chrome.fileManagerPrivate.RecentDateBucket.TODAY
+          bucket: chrome.fileManagerPrivate.RecentDateBucket.TODAY,
         },
         {
           // May 8, 2022, Sunday, 10:30am Local time.
@@ -75,7 +75,7 @@
           date: new Date(2022, 3, 28, 10, 30, 0),
           bucket: chrome.fileManagerPrivate.RecentDateBucket.EARLIER_THIS_YEAR,
         },
-      ]
+      ],
     },
 
     {
@@ -105,8 +105,8 @@
           // Feb 10, 2022, Thursday, 10:30am Local time.
           date: new Date(2022, 1, 10, 10, 30, 0),
           bucket: chrome.fileManagerPrivate.RecentDateBucket.EARLIER_THIS_YEAR,
-        }
-      ]
+        },
+      ],
     },
 
     {
@@ -142,9 +142,9 @@
           // Nov 28, 2021, Sunday, 10:30am Local time.
           date: new Date(2021, 10, 28, 10, 30, 0),
           bucket: chrome.fileManagerPrivate.RecentDateBucket.OLDER,
-        }
-      ]
-    }
+        },
+      ],
+    },
 
   ];
 
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index aac66e98..4664ed4 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1279,9 +1279,10 @@
  */
 util.timeoutPromise = (promise, ms, opt_message) => {
   return Promise.race([
-    promise, util.delay(ms).then(() => {
+    promise,
+    util.delay(ms).then(() => {
       throw new Error(opt_message || 'Operation timed out.');
-    })
+    }),
   ]);
 };
 
diff --git a/ui/file_manager/file_manager/common/js/volume_manager_types.js b/ui/file_manager/file_manager/common/js/volume_manager_types.js
index 8f81cdf..223d8db 100644
--- a/ui/file_manager/file_manager/common/js/volume_manager_types.js
+++ b/ui/file_manager/file_manager/common/js/volume_manager_types.js
@@ -254,7 +254,7 @@
   FILE: 'file',
   DEVICE: 'device',
   NETWORK: 'network',
-  SYSTEM: 'system'
+  SYSTEM: 'system',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/common/js/xfm.js b/ui/file_manager/file_manager/common/js/xfm.js
index 8ef41a3b..2efa2c2e 100644
--- a/ui/file_manager/file_manager/common/js/xfm.js
+++ b/ui/file_manager/file_manager/common/js/xfm.js
@@ -26,5 +26,5 @@
         window.focus();
       },
     });
-  }
+  },
 };
diff --git a/ui/file_manager/file_manager/externs/background/import_history.js b/ui/file_manager/file_manager/externs/background/import_history.js
index dca4bb8..77f589d 100644
--- a/ui/file_manager/file_manager/externs/background/import_history.js
+++ b/ui/file_manager/file_manager/externs/background/import_history.js
@@ -83,7 +83,7 @@
  */
 importerHistoryInterfaces.ImportHistoryState = {
   'COPIED': 'copied',
-  'IMPORTED': 'imported'
+  'IMPORTED': 'imported',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/externs/chrome_cast.js b/ui/file_manager/file_manager/externs/chrome_cast.js
index ca8f3a6b..b819bf32 100644
--- a/ui/file_manager/file_manager/externs/chrome_cast.js
+++ b/ui/file_manager/file_manager/externs/chrome_cast.js
@@ -15,7 +15,7 @@
 chrome.cast.AutoJoinPolicy = {
   TAB_AND_ORIGIN_SCOPED: 'tab_and_origin_scoped',
   ORIGIN_SCOPED: 'origin_scoped',
-  PAGE_SCOPED: 'page_scoped'
+  PAGE_SCOPED: 'page_scoped',
 };
 
 
@@ -25,7 +25,7 @@
  */
 chrome.cast.DefaultActionPolicy = {
   CREATE_SESSION: 'create_session',
-  CAST_THIS_TAB: 'cast_this_tab'
+  CAST_THIS_TAB: 'cast_this_tab',
 };
 
 
@@ -37,7 +37,7 @@
   VIDEO_OUT: 'video_out',
   AUDIO_OUT: 'audio_out',
   VIDEO_IN: 'video_in',
-  AUDIO_IN: 'audio_in'
+  AUDIO_IN: 'audio_in',
 };
 
 
@@ -55,7 +55,7 @@
   RECEIVER_UNAVAILABLE: 'receiver_unavailable',
   SESSION_ERROR: 'session_error',
   CHANNEL_ERROR: 'channel_error',
-  LOAD_MEDIA_FAILED: 'load_media_failed'
+  LOAD_MEDIA_FAILED: 'load_media_failed',
 };
 
 
@@ -65,7 +65,7 @@
  */
 chrome.cast.ReceiverAvailability = {
   AVAILABLE: 'available',
-  UNAVAILABLE: 'unavailable'
+  UNAVAILABLE: 'unavailable',
 };
 
 
@@ -76,7 +76,7 @@
 chrome.cast.SenderPlatform = {
   CHROME: 'chrome',
   IOS: 'ios',
-  ANDROID: 'android'
+  ANDROID: 'android',
 };
 
 
@@ -87,7 +87,7 @@
 chrome.cast.ReceiverType = {
   CAST: 'cast',
   HANGOUT: 'hangout',
-  CUSTOM: 'custom'
+  CUSTOM: 'custom',
 };
 
 
@@ -97,7 +97,7 @@
  */
 chrome.cast.ReceiverAction = {
   CAST: 'cast',
-  STOP: 'stop'
+  STOP: 'stop',
 };
 
 
@@ -108,7 +108,7 @@
 chrome.cast.SessionStatus = {
   CONNECTED: 'connected',
   DISCONNECTED: 'disconnected',
-  STOPPED: 'stopped'
+  STOPPED: 'stopped',
 };
 
 
@@ -126,7 +126,7 @@
   PAUSE: 'pause',
   SEEK: 'seek',
   STREAM_VOLUME: 'stream_volume',
-  STREAM_MUTE: 'stream_mute'
+  STREAM_MUTE: 'stream_mute',
 };
 
 
@@ -139,7 +139,7 @@
   TV_SHOW: 1,
   MOVIE: 2,
   MUSIC_TRACK: 3,
-  PHOTO: 4
+  PHOTO: 4,
 };
 
 
@@ -151,7 +151,7 @@
   IDLE: 'IDLE',
   PLAYING: 'PLAYING',
   PAUSED: 'PAUSED',
-  BUFFERING: 'BUFFERING'
+  BUFFERING: 'BUFFERING',
 };
 
 
@@ -161,7 +161,7 @@
  */
 chrome.cast.media.ResumeState = {
   PLAYBACK_START: 'PLAYBACK_START',
-  PLAYBACK_PAUSE: 'PLAYBACK_PAUSE'
+  PLAYBACK_PAUSE: 'PLAYBACK_PAUSE',
 };
 
 
@@ -172,7 +172,7 @@
 chrome.cast.media.StreamType = {
   BUFFERED: 'BUFFERED',
   LIVE: 'LIVE',
-  OTHER: 'OTHER'
+  OTHER: 'OTHER',
 };
 
 
@@ -184,7 +184,7 @@
   CANCELLED: 'CANCELLED',
   INTERRUPTED: 'INTERRUPTED',
   FINISHED: 'FINISHED',
-  ERROR: 'ERROR'
+  ERROR: 'ERROR',
 };
 
 
@@ -195,7 +195,7 @@
 chrome.cast.media.TrackType = {
   TEXT: 'TEXT',
   AUDIO: 'AUDIO',
-  VIDEO: 'VIDEO'
+  VIDEO: 'VIDEO',
 };
 
 
@@ -208,7 +208,7 @@
   CAPTIONS: 'CAPTIONS',
   DESCRIPTIONS: 'DESCRIPTIONS',
   CHAPTERS: 'CHAPTERS',
-  METADATA: 'METADATA'
+  METADATA: 'METADATA',
 };
 
 
@@ -221,7 +221,7 @@
   OUTLINE: 'OUTLINE',
   DROP_SHADOW: 'DROP_SHADOW',
   RAISED: 'RAISED',
-  DEPRESSED: 'DEPRESSED'
+  DEPRESSED: 'DEPRESSED',
 };
 
 
@@ -232,7 +232,7 @@
 chrome.cast.media.TextTrackWindowType = {
   NONE: 'NONE',
   NORMAL: 'NORMAL',
-  ROUNDED_CORNERS: 'ROUNDED_CORNERS'
+  ROUNDED_CORNERS: 'ROUNDED_CORNERS',
 };
 
 
@@ -247,7 +247,7 @@
   MONOSPACED_SERIF: 'MONOSPACED_SERIF',
   CASUAL: 'CASUAL',
   CURSIVE: 'CURSIVE',
-  SMALL_CAPITALS: 'SMALL_CAPITALS'
+  SMALL_CAPITALS: 'SMALL_CAPITALS',
 };
 
 
@@ -259,7 +259,7 @@
   NORMAL: 'NORMAL',
   BOLD: 'BOLD',
   BOLD_ITALIC: 'BOLD_ITALIC',
-  ITALIC: 'ITALIC'
+  ITALIC: 'ITALIC',
 };
 
 
diff --git a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js
index 2fd49663..78aec2f 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.js
@@ -47,7 +47,7 @@
     isErase_: {
       type: Boolean,
       value: false,
-    }
+    },
   },
 
   ready: function() {
diff --git a/ui/file_manager/file_manager/foreground/elements/files_icon_button.js b/ui/file_manager/file_manager/foreground/elements/files_icon_button.js
index a884e4ba..cd5526d 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_icon_button.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_icon_button.js
@@ -34,7 +34,7 @@
     } else {
       this.classList.remove('keyboard-focus');
     }
-  }
+  },
 });
 
 //# sourceURL=//ui/file_manager/file_manager/foreground/elements/files_icon_button.js
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.js b/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
index d97c7272..aeacb2a 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
@@ -382,7 +382,7 @@
     // Catch and re-fire the 'close' event such that it bubbles across Shadow
     // DOM v1.
     this.fire('close');
-  }
+  },
 });
 
 //# sourceURL=//ui/file_manager/file_manager/foreground/elements/files_quick_view.js
diff --git a/ui/file_manager/file_manager/foreground/elements/files_ripple.js b/ui/file_manager/file_manager/foreground/elements/files_ripple.js
index 537a3b1..3fccb1057 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_ripple.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_ripple.js
@@ -62,21 +62,23 @@
    */
   performPressAnimation: function() {
     /** @type {EventTarget} */
-    const animationPlayer = this.ripple_.animate([
-      {
-        width: '2%',
-        height: '2%',
-        opacity: 0,
-        offset: 0,
-        easing: 'linear'
-      },
-      {
-        width: '50%',
-        height: '50%',
-        opacity: 0.2,
-        offset: 1
-      }
-    ], 150);
+    const animationPlayer = this.ripple_.animate(
+        [
+          {
+            width: '2%',
+            height: '2%',
+            opacity: 0,
+            offset: 0,
+            easing: 'linear',
+          },
+          {
+            width: '50%',
+            height: '50%',
+            opacity: 0.2,
+            offset: 1,
+          },
+        ],
+        150);
 
     this._setPressed(true);
 
@@ -98,32 +100,36 @@
     pressAnimationPromise.then(() => {
       this._setPressed(false);
 
-      this.ripple_.animate([
-        {
-          opacity: 0.2,
-          offset: 0,
-          easing: 'linear'
-        },
-        {
-          opacity: 0,
-          offset: 1
-        }
-      ], 150);
-      this.ripple_.animate([
-        {
-          width: '50%',
-          height: '50%',
-          offset: 0,
-          easing: 'cubic-bezier(0, 0, 0.6, 1)'
-        },
-        {
-          width: '83.0%',
-          height: '83.0%',
-          offset: 1
-        }
-      ], 150);
+      this.ripple_.animate(
+          [
+            {
+              opacity: 0.2,
+              offset: 0,
+              easing: 'linear',
+            },
+            {
+              opacity: 0,
+              offset: 1,
+            },
+          ],
+          150);
+      this.ripple_.animate(
+          [
+            {
+              width: '50%',
+              height: '50%',
+              offset: 0,
+              easing: 'cubic-bezier(0, 0, 0.6, 1)',
+            },
+            {
+              width: '83.0%',
+              height: '83.0%',
+              offset: 1,
+            },
+          ],
+          150);
     });
-  }
+  },
 });
 
 //# sourceURL=//ui/file_manager/file_manager/foreground/elements/files_ripple.js
diff --git a/ui/file_manager/file_manager/foreground/elements/files_safe_media.js b/ui/file_manager/file_manager/foreground/elements/files_safe_media.js
index 6a2fb42f..866fa48 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_safe_media.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_safe_media.js
@@ -26,7 +26,7 @@
     type: {
       type: String,
       readonly: true,
-    }
+    },
   },
 
   listeners: {
@@ -148,7 +148,7 @@
         this.fire('files-safe-media-load-error');
       }
     });
-  }
+  },
 });
 
 //# sourceURL=//ui/file_manager/file_manager/foreground/elements/files_safe_media.js
diff --git a/ui/file_manager/file_manager/foreground/elements/files_toast.js b/ui/file_manager/file_manager/foreground/elements/files_toast.js
index d02ca99d5a..499e03d 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_toast.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_toast.js
@@ -131,7 +131,7 @@
     if (this.visible) {
       this.$.container.hide();
     }
-  }
+  },
 });
 
 //# sourceURL=//ui/file_manager/file_manager/foreground/elements/files_toast.js
diff --git a/ui/file_manager/file_manager/foreground/elements/files_toast_unittest.js b/ui/file_manager/file_manager/foreground/elements/files_toast_unittest.js
index faddf16..7686e92 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_toast_unittest.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_toast_unittest.js
@@ -36,7 +36,7 @@
     text: 'a1',
     callback: () => {
       a1Called = true;
-    }
+    },
   });
   await waitFor(() => getToastOpacity() === 1);
   assertTrue(toast.visible);
@@ -50,7 +50,7 @@
     text: 'a2',
     callback: () => {
       a2Called = true;
-    }
+    },
   });
   toast.show('t3');
   assertEquals('t1', text.innerText);
diff --git a/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.js b/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.js
index 23e2b0e..3576e8d5 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.js
@@ -58,45 +58,51 @@
   performActivateAnimation_: function() {
     const borderRadius = Math.min(this.clientWidth, this.clientHeight) / 2;
 
-    this.$.ripple.animate([
-      {opacity: 0, offset: 0, easing: 'linear'},
-      {opacity: 0.2, offset: 1}
-    ], 50);
-    this.$.ripple.animate([
-      {
-        width: '31.9%',
-        height: '31.9%',
-        offset: 0,
-        easing: 'cubic-bezier(0, 0, 0.330066603741, 0.931189591041)'
-      },
-      {
-        width: '78.6%',
-        height: '78.6%',
-        offset: 0.5,
-        easing: 'cubic-bezier(0.435623148352, 0.141946042876, 0.2, 1.0)'
-      },
-      {
-        width: '50%',
-        height: '50%',
-        offset: 1
-      }
-    ], 500);
-    this.$.ripple.animate([
-      {
-        borderRadius: borderRadius + 'px',
-        offset: 0,
-        easing: 'linear'
-      },
-      {
-        borderRadius: borderRadius + 'px',
-        offset: 0.333,
-        easing: 'cubic-bezier(0.109613342381, 0.32112094549, 0.2, 1.0)'
-      },
-      {
-        borderRadius: '2px',
-        offset: 1
-      }
-    ], 750);
+    this.$.ripple.animate(
+        [
+          {opacity: 0, offset: 0, easing: 'linear'},
+          {opacity: 0.2, offset: 1},
+        ],
+        50);
+    this.$.ripple.animate(
+        [
+          {
+            width: '31.9%',
+            height: '31.9%',
+            offset: 0,
+            easing: 'cubic-bezier(0, 0, 0.330066603741, 0.931189591041)',
+          },
+          {
+            width: '78.6%',
+            height: '78.6%',
+            offset: 0.5,
+            easing: 'cubic-bezier(0.435623148352, 0.141946042876, 0.2, 1.0)',
+          },
+          {
+            width: '50%',
+            height: '50%',
+            offset: 1,
+          },
+        ],
+        500);
+    this.$.ripple.animate(
+        [
+          {
+            borderRadius: borderRadius + 'px',
+            offset: 0,
+            easing: 'linear',
+          },
+          {
+            borderRadius: borderRadius + 'px',
+            offset: 0.333,
+            easing: 'cubic-bezier(0.109613342381, 0.32112094549, 0.2, 1.0)',
+          },
+          {
+            borderRadius: '2px',
+            offset: 1,
+          },
+        ],
+        750);
   },
 
   /**
@@ -106,35 +112,41 @@
   performDeactivateAnimation_: function() {
     const borderRadius = Math.min(this.clientWidth, this.clientHeight) / 2;
 
-    this.$.ripple.animate([
-      {opacity: 0.2, offset: 0, easing: 'linear'},
-      {opacity: 0, offset: 1}
-    ], 150);
-    this.$.ripple.animate([
-      {
-        width: '50%',
-        height: '50%',
-        offset: 0,
-        easing: 'cubic-bezier(0, 0, 0.6, 1)'
-      },
-      {
-        width: '83.0%',
-        height: '83.0%',
-        offset: 1
-      }
-    ], 150);
-    this.$.ripple.animate([
-      {
-        borderRadius: '2px',
-        offset: 0,
-        easing: 'cubic-bezier(0, 0, 0.2, 1)'
-      },
-      {
-        borderRadius: borderRadius + 'px',
-        offset: 1
-      }
-    ], 150);
-  }
+    this.$.ripple.animate(
+        [
+          {opacity: 0.2, offset: 0, easing: 'linear'},
+          {opacity: 0, offset: 1},
+        ],
+        150);
+    this.$.ripple.animate(
+        [
+          {
+            width: '50%',
+            height: '50%',
+            offset: 0,
+            easing: 'cubic-bezier(0, 0, 0.6, 1)',
+          },
+          {
+            width: '83.0%',
+            height: '83.0%',
+            offset: 1,
+          },
+        ],
+        150);
+    this.$.ripple.animate(
+        [
+          {
+            borderRadius: '2px',
+            offset: 0,
+            easing: 'cubic-bezier(0, 0, 0.2, 1)',
+          },
+          {
+            borderRadius: borderRadius + 'px',
+            offset: 1,
+          },
+        ],
+        150);
+  },
 });
 
 //# sourceURL=//ui/file_manager/file_manager/foreground/elements/files_toggle_ripple.js
diff --git a/ui/file_manager/file_manager/foreground/elements/files_tooltip.js b/ui/file_manager/file_manager/foreground/elements/files_tooltip.js
index e580964..c17acabc 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_tooltip.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_tooltip.js
@@ -26,7 +26,7 @@
     showTimeout: {
       type: Number,
       value: 500,  // ms
-      readOnly: true
+      readOnly: true,
     },
 
     /**
@@ -35,8 +35,8 @@
     hideTimeout: {
       type: Number,
       value: 250,  // ms
-      readOnly: true
-    }
+      readOnly: true,
+    },
   },
 
   /**
@@ -337,7 +337,7 @@
   cleanupCardTooltip_: function() {
     this.className = '';
     this.$.label.className = '';
-  }
+  },
 });
 
 //# sourceURL=//ui/file_manager/file_manager/foreground/elements/files_tooltip.js
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model.js b/ui/file_manager/file_manager/foreground/js/actions_model.js
index 9f47836..8ab4228 100644
--- a/ui/file_manager/file_manager/foreground/js/actions_model.js
+++ b/ui/file_manager/file_manager/foreground/js/actions_model.js
@@ -296,7 +296,7 @@
         this.ui_.alertDialog.show(
             strf('OFFLINE_FAILURE_MESSAGE', unescape(currentEntry.name)), null,
             null, null);
-      }
+      },
     };
     steps.start();
 
@@ -894,7 +894,7 @@
 ActionsModel.CommonActionId = {
   SHARE: 'SHARE',
   SAVE_FOR_OFFLINE: 'SAVE_FOR_OFFLINE',
-  OFFLINE_NOT_NECESSARY: 'OFFLINE_NOT_NECESSARY'
+  OFFLINE_NOT_NECESSARY: 'OFFLINE_NOT_NECESSARY',
 };
 
 /**
@@ -903,5 +903,5 @@
 ActionsModel.InternalActionId = {
   CREATE_FOLDER_SHORTCUT: 'pin-folder',
   REMOVE_FOLDER_SHORTCUT: 'unpin-folder',
-  MANAGE_IN_DRIVE: 'manage-in-drive'
+  MANAGE_IN_DRIVE: 'manage-in-drive',
 };
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
index 6fa827d..721cefc2 100644
--- a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
@@ -82,7 +82,7 @@
     this.listContainer = /** @type {!ListContainer} */ ({
       currentView: {
         updateListItemsMetadata: function() {},
-      }
+      },
     });
 
     this.alertDialog = /** @type {!FilesAlertDialog} */ ({
diff --git a/ui/file_manager/file_manager/foreground/js/app_state_controller.js b/ui/file_manager/file_manager/foreground/js/app_state_controller.js
index 1dbec3d..de628e9 100644
--- a/ui/file_manager/file_manager/foreground/js/app_state_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/app_state_controller.js
@@ -132,7 +132,7 @@
       columnConfig: {},
       listType: this.ui_.listContainer.currentListType,
       isAllAndroidFoldersVisible:
-          this.directoryModel_.getFileFilter().isAllAndroidFoldersVisible()
+          this.directoryModel_.getFileFilter().isAllAndroidFoldersVisible(),
     };
     const cm = this.ui_.listContainer.table.columnModel;
     prefs.columnConfig = cm.exportColumnConfig();
diff --git a/ui/file_manager/file_manager/foreground/js/banner_controller.js b/ui/file_manager/file_manager/foreground/js/banner_controller.js
index b3715fd..28a1d6a 100644
--- a/ui/file_manager/file_manager/foreground/js/banner_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/banner_controller.js
@@ -307,8 +307,8 @@
         shouldShow: () => !!this.volumeSizeStats_[this.currentVolume_.volumeId],
         context: () => ({
           remainingSize:
-              this.volumeSizeStats_[this.currentVolume_.volumeId].remainingSize
-        })
+              this.volumeSizeStats_[this.currentVolume_.volumeId].remainingSize,
+        }),
       });
 
       // Register a custom filter that checks if the removable device has an
diff --git a/ui/file_manager/file_manager/foreground/js/banner_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/banner_controller_unittest.js
index 14a993a4..33f1cc1 100644
--- a/ui/file_manager/file_manager/foreground/js/banner_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/banner_controller_unittest.js
@@ -365,7 +365,7 @@
   const volumeManager = /** @type{!VolumeManager} */ ({
     getVolumeInfo: (entry) => {
       return volumeManagerGetVolumeInfoType;
-    }
+    },
   });
   const crostini = /** @type {!Crostini} */ ({});
   controller = new BannerController(directoryModel, volumeManager, crostini);
@@ -1095,8 +1095,9 @@
 export async function testBannersAreUpdatedOnDismissClick() {
   // Add 3 educational banner and 1 warning banner.
   controller.setEducationalBannersInOrder([
-    testEducationalBanners[0].tagName, testEducationalBanners[1].tagName,
-    testEducationalBanners[2].tagName
+    testEducationalBanners[0].tagName,
+    testEducationalBanners[1].tagName,
+    testEducationalBanners[2].tagName,
   ]);
   controller.setWarningBannersInOrder([testWarningBanners[0].tagName]);
 
diff --git a/ui/file_manager/file_manager/foreground/js/banner_util_unittest.js b/ui/file_manager/file_manager/foreground/js/banner_util_unittest.js
index d2c81218..eb2c090 100644
--- a/ui/file_manager/file_manager/foreground/js/banner_util_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/banner_util_unittest.js
@@ -77,7 +77,7 @@
       createAndSetVolumeInfo(VolumeManagerCommon.VolumeType.DOWNLOADS);
   allowedVolumes = [
     {type: VolumeManagerCommon.VolumeType.DOWNLOADS},
-    {type: VolumeManagerCommon.VolumeType.ANDROID_FILES}
+    {type: VolumeManagerCommon.VolumeType.ANDROID_FILES},
   ];
 
   assertTrue(isAllowedVolume(
@@ -92,7 +92,7 @@
       createAndSetVolumeInfo(VolumeManagerCommon.VolumeType.DOWNLOADS);
   allowedVolumes = [
     {type: VolumeManagerCommon.VolumeType.ARCHIVE},
-    {type: VolumeManagerCommon.VolumeType.ANDROID_FILES}
+    {type: VolumeManagerCommon.VolumeType.ANDROID_FILES},
   ];
 
   assertFalse(isAllowedVolume(
@@ -110,7 +110,7 @@
       type: VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER,
       id: 'provider_a',
     },
-    {type: VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER, id: 'provider_b'}
+    {type: VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER, id: 'provider_b'},
   ];
 
   assertTrue(isAllowedVolume(
@@ -128,7 +128,7 @@
       type: VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER,
       id: 'provider_b',
     },
-    {type: VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER, id: 'provider_c'}
+    {type: VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER, id: 'provider_c'},
   ];
 
   assertFalse(isAllowedVolume(
@@ -160,7 +160,7 @@
     },
     {
       root: VolumeManagerCommon.RootType.DRIVE,
-    }
+    },
   ];
   assertTrue(isAllowedVolume(
       /* currentVolume */ null, currentRootType, allowedVolumes));
@@ -178,7 +178,7 @@
     },
     {
       root: VolumeManagerCommon.RootType.DRIVE,
-    }
+    },
   ];
   assertFalse(isAllowedVolume(
       /* currentVolume */ null, currentRootType, allowedVolumes));
@@ -293,7 +293,7 @@
       type: VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER,
       root: VolumeManagerCommon.RootType.DOCUMENTS_PROVIDER,
       id: 'provider_a',
-    }
+    },
   ];
   assertTrue(isAllowedVolume(currentVolume, currentRootType, allowedVolumes));
 }
@@ -316,7 +316,7 @@
       type: VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER,
       root: VolumeManagerCommon.RootType.DOCUMENTS_PROVIDER,
       id: 'provider_a',
-    }
+    },
   ];
   assertFalse(isAllowedVolume(currentVolume, currentRootType, allowedVolumes));
 }
diff --git a/ui/file_manager/file_manager/foreground/js/crostini_controller.js b/ui/file_manager/file_manager/foreground/js/crostini_controller.js
index d4e75dd..b2872464 100644
--- a/ui/file_manager/file_manager/foreground/js/crostini_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/crostini_controller.js
@@ -95,13 +95,13 @@
         callback: () => {
           chrome.fileManagerPrivate.openSettingsSubpage(subPage);
           CommandHandler.recordMenuItemSelected(umaItem);
-        }
+        },
       });
     };
 
     const [crostiniShareCount, pluginVmShareCount] = await Promise.all([
       getSharedPaths(constants.DEFAULT_CROSTINI_VM),
-      getSharedPaths(constants.PLUGIN_VM)
+      getSharedPaths(constants.PLUGIN_VM),
     ]);
 
     toast(
diff --git a/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js b/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js
index d6941ed..8b611ad9 100644
--- a/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/dialog_action_controller.js
@@ -132,7 +132,7 @@
       this.selectFilesAndClose_({
         urls: [url],
         multiple: false,
-        filterIndex: this.dialogFooter_.selectedFilterIndex
+        filterIndex: this.dialogFooter_.selectedFilterIndex,
       });
     } catch (error) {
       if (!(error instanceof UserCanceledError)) {
@@ -168,7 +168,7 @@
       const singleSelection = {
         urls: [url],
         multiple: false,
-        filterIndex: this.dialogFooter_.selectedFilterIndex
+        filterIndex: this.dialogFooter_.selectedFilterIndex,
       };
       this.selectFilesAndClose_(singleSelection);
       return;
@@ -222,7 +222,7 @@
     const singleSelection = {
       urls: [files[0]],
       multiple: false,
-      filterIndex: this.dialogFooter_.selectedFilterIndex
+      filterIndex: this.dialogFooter_.selectedFilterIndex,
     };
     this.selectFilesAndClose_(singleSelection);
   }
diff --git a/ui/file_manager/file_manager/foreground/js/directory_contents_unittest.js b/ui/file_manager/file_manager/foreground/js/directory_contents_unittest.js
index 3af69b0..e763fe8 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_contents_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_contents_unittest.js
@@ -40,7 +40,7 @@
             /** @type {!FileEntry} */ ({name: '3.png'}),
           ];
           callback(entries);
-        }
+        },
   },
 };
 
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js
index 84c23b4..562c3242 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -1295,7 +1295,7 @@
       onDirectoryChange_: function(event) {
         tracker.stop();
         tracker.hasChanged = true;
-      }
+      },
     };
     return tracker;
   }
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model_unittest.js b/ui/file_manager/file_manager/foreground/js/directory_model_unittest.js
index 0992630..48c7ea9 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_model_unittest.js
@@ -60,7 +60,7 @@
          */
         addListener(callback) {
           onIOTaskProgressStatusCallback = callback;
-        }
+        },
       },
       IOTaskType: {
         DELETE: 'delete',
diff --git a/ui/file_manager/file_manager/foreground/js/drop_effect_and_label.js b/ui/file_manager/file_manager/foreground/js/drop_effect_and_label.js
index 4d40d011..2668244 100644
--- a/ui/file_manager/file_manager/foreground/js/drop_effect_and_label.js
+++ b/ui/file_manager/file_manager/foreground/js/drop_effect_and_label.js
@@ -10,7 +10,7 @@
   NONE: 'none',
   COPY: 'copy',
   MOVE: 'move',
-  LINK: 'link'
+  LINK: 'link',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/file_list_model_unittest.js b/ui/file_manager/file_manager/foreground/js/file_list_model_unittest.js
index a2b954f..665bcf8 100644
--- a/ui/file_manager/file_manager/foreground/js/file_list_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/file_list_model_unittest.js
@@ -358,19 +358,19 @@
           startIndex: 3,
           endIndex: 4,
           label: 'earlier_this_week',
-          group: 'earlier_this_week'
+          group: 'earlier_this_week',
         },
         {
           startIndex: 5,
           endIndex: 5,
           label: 'earlier_this_month',
-          group: 'earlier_this_month'
+          group: 'earlier_this_month',
         },
         {
           startIndex: 6,
           endIndex: 6,
           label: 'earlier_this_year',
-          group: 'earlier_this_year'
+          group: 'earlier_this_year',
         },
       ],
       expectedReversedGroups: [
@@ -378,19 +378,19 @@
           startIndex: 0,
           endIndex: 0,
           label: 'earlier_this_year',
-          group: 'earlier_this_year'
+          group: 'earlier_this_year',
         },
         {
           startIndex: 1,
           endIndex: 1,
           label: 'earlier_this_month',
-          group: 'earlier_this_month'
+          group: 'earlier_this_month',
         },
         {
           startIndex: 2,
           endIndex: 3,
           label: 'earlier_this_week',
-          group: 'earlier_this_week'
+          group: 'earlier_this_week',
         },
         {startIndex: 4, endIndex: 4, label: 'yesterday', group: 'yesterday'},
         {startIndex: 5, endIndex: 6, label: 'today', group: 'today'},
@@ -481,7 +481,7 @@
     const files = Object.keys(test.metadataMap).map(fileName => {
       return {
         name: fileName,
-        isDirectory: test.metadataMap[fileName].isDirectory
+        isDirectory: test.metadataMap[fileName].isDirectory,
       };
     });
     fileListModel.push(...files);
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index ec90573..9a93aebf 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -1011,7 +1011,7 @@
   execute(event, fileManager) {
     fileManager.launchFileManager({
       currentDirectoryURL: fileManager.getCurrentDirectoryEntry() &&
-          fileManager.getCurrentDirectoryEntry().toURL()
+          fileManager.getCurrentDirectoryEntry().toURL(),
     });
   }
 
@@ -1299,7 +1299,7 @@
       callback: () => {
         fileManager.fileOperationManager.restoreDeleted(
             assert(e.trashedEntries));
-      }
+      },
     });
   };
 
@@ -2424,7 +2424,7 @@
           chrome.fileManagerPrivate.openSettingsSubpage('crostini/sharedPaths');
           CommandHandler.recordMenuItemSelected(
               CommandHandler.MenuCommandsForUMA.MANAGE_LINUX_SHARING_TOAST);
-        }
+        },
       });
     }
     // Show a confirmation dialog if we are sharing the root of a volume.
@@ -2501,7 +2501,7 @@
               'app-management/pluginVm/sharedPaths');
           CommandHandler.recordMenuItemSelected(
               CommandHandler.MenuCommandsForUMA.MANAGE_PLUGIN_VM_SHARING_TOAST);
-        }
+        },
       });
     }
     // Show a confirmation dialog if we are sharing the root of a volume.
@@ -2982,7 +2982,7 @@
                 {
                   data: arrayBuffer,
                   layout: chrome.wallpaper.WallpaperLayout.CENTER_CROPPED,
-                  filename: 'wallpaper'
+                  filename: 'wallpaper',
                 },
                 () => {
                   if (chrome.runtime.lastError) {
diff --git a/ui/file_manager/file_manager/foreground/js/file_selection.js b/ui/file_manager/file_manager/foreground/js/file_selection.js
index 88cbcef..d1345d1 100644
--- a/ui/file_manager/file_manager/foreground/js/file_selection.js
+++ b/ui/file_manager/file_manager/foreground/js/file_selection.js
@@ -331,7 +331,7 @@
    * If multiple changes are happened during the term, only one CHANGE_THROTTLED
    * event is dispatched.
    */
-  CHANGE_THROTTLED: 'changethrottled'
+  CHANGE_THROTTLED: 'changethrottled',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks.js b/ui/file_manager/file_manager/foreground/js/file_tasks.js
index e0cf390..9950bf0 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks.js
@@ -658,7 +658,7 @@
       const descriptor = {
         appId: LEGACY_FILES_EXTENSION_ID,
         taskType: 'file',
-        actionId: 'view-in-browser'
+        actionId: 'view-in-browser',
       };
       chrome.fileManagerPrivate.executeTask(
           descriptor, this.entries_, onViewFiles);
@@ -1077,7 +1077,7 @@
     } else {
       combobutton.defaultItem = {
         type: FileTasks.TaskMenuButtonItemType.ShowMenu,
-        label: str('OPEN_WITH_BUTTON_LABEL')
+        label: str('OPEN_WITH_BUTTON_LABEL'),
       };
     }
 
@@ -1097,7 +1097,7 @@
         combobutton.addSeparator();
         const changeDefaultMenuItem = combobutton.addDropDownItem({
           type: FileTasks.TaskMenuButtonItemType.ChangeDefaultTask,
-          label: loadTimeData.getString('CHANGE_DEFAULT_MENU_ITEM')
+          label: loadTimeData.getString('CHANGE_DEFAULT_MENU_ITEM'),
         });
         changeDefaultMenuItem.classList.add('change-default');
       }
@@ -1169,7 +1169,7 @@
       task: task,
       bold: opt_bold || false,
       isDefault: opt_isDefault || false,
-      isGenericFileHandler: /** @type {boolean} */ (task.isGenericFileHandler)
+      isGenericFileHandler: /** @type {boolean} */ (task.isGenericFileHandler),
     };
   }
 
@@ -1265,7 +1265,7 @@
 FileTasks.INSTALL_LINUX_PACKAGE_TASK_DESCRIPTOR = {
   appId: LEGACY_FILES_EXTENSION_ID,
   taskType: 'app',
-  actionId: 'install-linux-package'
+  actionId: 'install-linux-package',
 };
 
 /**
@@ -1275,7 +1275,7 @@
 FileTasks.TaskMenuButtonItemType = {
   ShowMenu: 'ShowMenu',
   RunTask: 'RunTask',
-  ChangeDefaultTask: 'ChangeDefaultTask'
+  ChangeDefaultTask: 'ChangeDefaultTask',
 };
 
 /**
@@ -1323,7 +1323,7 @@
   '.gslides',  '.arw',         '.cr2',
   '.dng',      '.nef',         '.nrw',
   '.orf',      '.raf',         '.rw2',
-  '.tini'
+  '.tini',
 ]);
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js b/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js
index 0252423..84c6b1435 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js
@@ -240,7 +240,7 @@
       getCurrentRootType: function() {
         return null;
       },
-      changeDirectoryEntry: function(displayRoot) {}
+      changeDirectoryEntry: function(displayRoot) {},
     }),
     crostini: crostini,
     progressCenter: /** @type {!ProgressCenter} */ (new MockProgressCenter()),
@@ -541,7 +541,7 @@
     descriptor: {
       appId: LEGACY_FILES_EXTENSION_ID,
       taskType: 'app',
-      actionId: 'install-linux-package'
+      actionId: 'install-linux-package',
     },
     isDefault: false,
     isGenericFileHandler: false,
@@ -598,7 +598,7 @@
                 descriptor: {
                   appId: LEGACY_FILES_EXTENSION_ID,
                   taskType: 'app',
-                  actionId: 'import-crostini-image'
+                  actionId: 'import-crostini-image',
                 },
                 isDefault: false,
                 isGenericFileHandler: false,
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index b006bfb..57bab95a 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -477,7 +477,7 @@
             callback: () => {
               util.visitURL(
                   'https://support.google.com/chrome/a/?p=chromeos_datacontrols');
-            }
+            },
           });
       throw new Error('ABORT');
     }
@@ -1707,11 +1707,11 @@
     // Confirmation type for team drives.
     const source = {
       isTeamDrive: util.isSharedDriveEntry(this.sourceEntries[0]),
-      teamDriveName: util.getTeamDriveName(this.sourceEntries[0])
+      teamDriveName: util.getTeamDriveName(this.sourceEntries[0]),
     };
     const destination = {
       isTeamDrive: util.isSharedDriveEntry(this.destinationEntry),
-      teamDriveName: util.getTeamDriveName(this.destinationEntry)
+      teamDriveName: util.getTeamDriveName(this.destinationEntry),
     };
     if (this.isMove) {
       if (source.isTeamDrive) {
@@ -1768,14 +1768,14 @@
       case FileTransferController.ConfirmationType.MOVE_BETWEEN_SHARED_DRIVES:
         return [
           strf('DRIVE_CONFIRM_TD_MEMBERS_LOSE_ACCESS', sourceName),
-          strf('DRIVE_CONFIRM_TD_MEMBERS_GAIN_ACCESS_TO_COPY', destinationName)
+          strf('DRIVE_CONFIRM_TD_MEMBERS_GAIN_ACCESS_TO_COPY', destinationName),
         ];
       // TODO(yamaguchi): notify ownership transfer if the two Shared Drives
       // belong to different domains.
       case FileTransferController.ConfirmationType
           .MOVE_FROM_SHARED_DRIVE_TO_OTHER:
         return [
-          strf('DRIVE_CONFIRM_TD_MEMBERS_LOSE_ACCESS', sourceName)
+          strf('DRIVE_CONFIRM_TD_MEMBERS_LOSE_ACCESS', sourceName),
           // TODO(yamaguchi): Warn if the operation moves at least one
           // directory to My Drive, as it's no undoable.
         ];
diff --git a/ui/file_manager/file_manager/foreground/js/file_type_filters_controller.js b/ui/file_manager/file_manager/foreground/js/file_type_filters_controller.js
index 9f8a5d0..0eeb2a1b 100644
--- a/ui/file_manager/file_manager/foreground/js/file_type_filters_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_type_filters_controller.js
@@ -29,24 +29,24 @@
     this.filterTypeToTranslationKeyMap_ = new Map([
       [
         chrome.fileManagerPrivate.RecentFileType.ALL,
-        'MEDIA_VIEW_ALL_ROOT_LABEL'
+        'MEDIA_VIEW_ALL_ROOT_LABEL',
       ],
       [
         chrome.fileManagerPrivate.RecentFileType.AUDIO,
-        'MEDIA_VIEW_AUDIO_ROOT_LABEL'
+        'MEDIA_VIEW_AUDIO_ROOT_LABEL',
       ],
       [
         chrome.fileManagerPrivate.RecentFileType.IMAGE,
-        'MEDIA_VIEW_IMAGES_ROOT_LABEL'
+        'MEDIA_VIEW_IMAGES_ROOT_LABEL',
       ],
       [
         chrome.fileManagerPrivate.RecentFileType.VIDEO,
-        'MEDIA_VIEW_VIDEOS_ROOT_LABEL'
+        'MEDIA_VIEW_VIDEOS_ROOT_LABEL',
       ],
       [
         chrome.fileManagerPrivate.RecentFileType.DOCUMENT,
         'MEDIA_VIEW_DOCUMENTS_ROOT_LABEL',
-      ]
+      ],
     ]);
 
     /**
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js
index 16e9dfd..b831bf8 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller.js
@@ -27,7 +27,7 @@
   INSUFFICIENT_CLOUD_SPACE: 'insufficient-cloud-space',
   INSUFFICIENT_LOCAL_SPACE: 'insufficient-local-space',
   NO_MEDIA: 'no-media',
-  SCANNING: 'scanning'
+  SCANNING: 'scanning',
 };
 
 /**
@@ -543,7 +543,7 @@
   DESTINATION: 'destination',
   IMPORT: 'import',
   MAIN: 'main',
-  SIDE: 'side'
+  SIDE: 'side',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js
index 0544831..17342b7 100644
--- a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js
+++ b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js
@@ -442,7 +442,7 @@
         .then(metadata => {
           const loadTargets = [
             ThumbnailLoader.LoadTarget.CONTENT_METADATA,
-            ThumbnailLoader.LoadTarget.EXTERNAL_METADATA
+            ThumbnailLoader.LoadTarget.EXTERNAL_METADATA,
           ];
 
           // If the file is on a network filesystem, don't generate thumbnails
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.js
index c6b8d34..e48a2b9 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.js
@@ -27,7 +27,7 @@
     },
     toURL: function() {
       return dataUrl;
-    }
+    },
   };
 }
 // clang-format off
@@ -75,16 +75,17 @@
           data: {
             verb: 'result',
             arguments: [
-              message.arguments[0], {
+              message.arguments[0],
+              {
                 thumbnailURL: message.arguments[0] + ',url',
-                thumbnailTransform: message.arguments[0] + ',transform'
-              }
-            ]
-          }
+                thumbnailTransform: message.arguments[0] + ',transform',
+              },
+            ],
+          },
         }));
       }
     },
-    start: function() {}
+    start: function() {},
   });
 
   // TODO(ryoh): chrome.mediaGalleries API is not available in unit tests.
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/dlp_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/dlp_metadata_provider.js
index 2d4a4fc8..f5d61ba 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/dlp_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/dlp_metadata_provider.js
@@ -58,5 +58,6 @@
 /** @const {!Array<string>} */
 DlpMetadataProvider.PROPERTY_NAMES = [
   // TODO(crbug.com/1329770): Consider using an enum for this property.
-  'isDlpRestricted', 'sourceUrl'
+  'isDlpRestricted',
+  'sourceUrl',
 ];
\ No newline at end of file
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/exif_constants.js b/ui/file_manager/file_manager/foreground/js/metadata/exif_constants.js
index 2411188..6311156 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/exif_constants.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/exif_constants.js
@@ -20,7 +20,7 @@
   // APP0 block, most commonly JFIF data.
   APP0: 0xffe0,
   // Start of exif block.
-  EXIF: 0xffe1
+  EXIF: 0xffe1,
 };
 
 /**
@@ -31,7 +31,7 @@
   // Indicates little endian exif data.
   LITTLE: 0x4949,
   // Indicates big endian exif data.
-  BIG: 0x4d4d
+  BIG: 0x4d4d,
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser.js b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser.js
index 88ffabe..aeec8f3 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser.js
@@ -444,7 +444,7 @@
       return {
         scaleX: ExifParser.SCALEX[index],
         scaleY: ExifParser.SCALEY[index],
-        rotate90: ExifParser.ROTATE90[index]
+        rotate90: ExifParser.ROTATE90[index],
       };
     }
     return null;
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js
index b64daebd..c5b1435 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js
@@ -185,7 +185,7 @@
   // Little endian byte order.
   LITTLE_ENDIAN: 0,
   // Big endian byte order.
-  BIG_ENDIAN: 1
+  BIG_ENDIAN: 1,
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.js
index fb96d39..f4499a5 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.js
@@ -57,9 +57,9 @@
             isMachineRoot: false,
             isExternalMedia: false,
             isArbitrarySyncFolder: false,
-          }
+          },
         ]);
-      }
+      },
     },
     runtime: {
       lastError: null,
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.js
index e75e13b..ebe7441 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.js
@@ -17,7 +17,7 @@
   getMetadata: function(fulfill, reject) {
     Promise.resolve({modificationTime: new Date(2015, 1, 1), size: 1024})
         .then(fulfill, reject);
-  }
+  },
 });
 
 /** @const {!Entry} */
@@ -28,19 +28,23 @@
   getMetadata: function(fulfill, reject) {
     Promise.resolve({modificationTime: new Date(2015, 2, 2), size: 2048})
         .then(fulfill, reject);
-  }
+  },
 });
 
 export function testFileSystemMetadataProviderBasic(callback) {
   const provider = new FileSystemMetadataProvider();
   const names = [
-    'modificationTime', 'size', 'contentMimeType', 'present', 'availableOffline'
+    'modificationTime',
+    'size',
+    'contentMimeType',
+    'present',
+    'availableOffline',
   ];
   reportPromise(
       provider
           .get([
             new MetadataRequest(entryA, names),
-            new MetadataRequest(entryB, names)
+            new MetadataRequest(entryB, names),
           ])
           .then(results => {
             assertEquals(2, results.length);
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/id3_parser.js b/ui/file_manager/file_manager/foreground/js/metadata/id3_parser.js
index 6197da8..6ca8c130 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/id3_parser.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/id3_parser.js
@@ -286,7 +286,7 @@
               }
             }
             this.nextStep();
-          }
+          },
         ],
         this, () => {}, error => {});
 
@@ -369,7 +369,7 @@
                   id3v2[key].value.trim().length > 0) {
                 metadata.description.push({
                   key: Id3Parser.v2.MAPPERS[key],
-                  value: id3v2[key].value.trim()
+                  value: id3v2[key].value.trim(),
                 });
               }
             }
@@ -397,7 +397,7 @@
                   Id3Parser.METADATA_ORDER.indexOf(b.key);
             });
             this.nextStep();
-          }
+          },
         ],
         this, () => {}, error => {});
 
@@ -443,7 +443,7 @@
   'ID3_OFFICIAL_AUDIO_FILE_WEBPAGE',
   'ID3_OFFICIAL_ARTIST',
   'ID3_OFFICIAL_AUDIO_SOURCE_WEBPAGE',
-  'ID3_PUBLISHERS_OFFICIAL_WEBPAGE'
+  'ID3_PUBLISHERS_OFFICIAL_WEBPAGE',
 ];
 
 
@@ -606,8 +606,8 @@
     'Thrash Metal',
     'Anime',
     'Jpop',
-    'Synthpop'
-  ]
+    'Synthpop',
+  ],
 };
 
 /**
@@ -652,7 +652,7 @@
      * @const
      * @type {number}
      */
-    UTF_8: 3
+    UTF_8: 3,
   },
   HANDLERS: {
     // User defined text information frame
@@ -670,7 +670,7 @@
     PIC: Id3Parser.prototype.readPIC_,
 
     // User attached image
-    APIC: Id3Parser.prototype.readAPIC_
+    APIC: Id3Parser.prototype.readAPIC_,
   },
   MAPPERS: {
     TALB: 'ID3_ALBUM',
@@ -692,6 +692,6 @@
     WOAF: 'ID3_OFFICIAL_AUDIO_FILE_WEBPAGE',
     WOAR: 'ID3_OFFICIAL_ARTIST',
     WOAS: 'ID3_OFFICIAL_AUDIO_SOURCE_WEBPAGE',
-    WPUB: 'ID3_PUBLISHERS_OFFICIAL_WEBPAGE'
-  }
+    WPUB: 'ID3_PUBLISHERS_OFFICIAL_WEBPAGE',
+  },
 };
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item.js
index a061312..67f0c3c 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item.js
@@ -165,7 +165,7 @@
 const MetadataCacheItemPropertyState = {
   INVALIDATED: 'invalidated',
   LOADING: 'loading',
-  FULFILLED: 'fulfilled'
+  FULFILLED: 'fulfilled',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_dispatcher.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_dispatcher.js
index 73d8373..c10d9fb 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_dispatcher.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_dispatcher.js
@@ -61,7 +61,7 @@
 
     this.messageHandlers_ = {
       init: this.init_.bind(this),
-      request: this.request_.bind(this)
+      request: this.request_.bind(this),
     };
   }
 
@@ -212,7 +212,7 @@
         } catch (e) {
           onError(e.stack);
         }
-      }
+      },
     ];
 
     nextStep();
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/mpeg_parser.js b/ui/file_manager/file_manager/foreground/js/metadata/mpeg_parser.js
index d6a163e8..0483e2a 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/mpeg_parser.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/mpeg_parser.js
@@ -115,9 +115,9 @@
         '©nam': {data: parseDataString.bind(null, 'title')},
         '©alb': {data: parseDataString.bind(null, 'album')},
         '©art': {data: parseDataString.bind(null, 'artist')},
-        'covr': {data: parseCovr}
+        'covr': {data: parseCovr},
       },
-      versioned: true
+      versioned: true,
     };
 
     // main parser for the entire file structure.
@@ -132,14 +132,14 @@
               stbl: {stsd: parseStsd},
             },
           },
-          meta: parseMeta
+          meta: parseMeta,
         },
         udta: {
           meta: parseMeta,
         },
-        meta: parseMeta
+        meta: parseMeta,
       },
-      meta: parseMeta
+      meta: parseMeta,
     };
   }
 
@@ -225,7 +225,7 @@
             start: offset + MpegParser.HEADER_SIZE,
             end: offset + size,
             name: name,
-            parent: parentAtom
+            parent: parentAtom,
           },
           filePos);
 
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider.js
index 3abdef6..416d0ff5 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider.js
@@ -177,8 +177,11 @@
     // Merge results.
     return Promise
         .all([
-          fileSystemPromise, externalPromise, contentPromise,
-          fallbackContentPromise, dlpPromise
+          fileSystemPromise,
+          externalPromise,
+          contentPromise,
+          fallbackContentPromise,
+          dlpPromise,
         ])
         .then(resultsList => {
           const integratedResults = {};
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider_unittest.js
index 2f8867c..f5cf2f6 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/multi_metadata_provider_unittest.js
@@ -59,7 +59,7 @@
       };
     }
     assertNotReached();
-  }
+  },
 });
 
 export function testMultiMetadataProviderBasic(callback) {
@@ -71,7 +71,7 @@
           assertArrayEquals(['size', 'modificationTime'], requests[0].names);
           return Promise.resolve(
               [{modificationTime: new Date(2015, 0, 1), size: 1024}]);
-        }
+        },
       }),
       /** @type {!ExternalMetadataProvider} */ ({
         get: function(requests) {
@@ -80,7 +80,7 @@
           assertArrayEquals(['size', 'modificationTime'], requests[0].names);
           return Promise.resolve(
               [{modificationTime: new Date(2015, 1, 2), size: 2048}]);
-        }
+        },
       }),
       /** @type {!ContentMetadataProvider} */ ({
         get: function(requests) {
@@ -94,15 +94,15 @@
           assertArrayEquals(['contentThumbnailUrl'], requests[1].names);
           return Promise.resolve([
             {contentThumbnailUrl: 'THUMBNAIL_URL_A'},
-            {contentThumbnailUrl: 'THUMBNAIL_URL_B'}
+            {contentThumbnailUrl: 'THUMBNAIL_URL_B'},
           ]);
-        }
+        },
       }),
       /** @type {!DlpMetadataProvider} */ ({
         get: function(requests) {
           assertEquals(0, requests.length);
           return Promise.resolve([]);
-        }
+        },
       }),
       volumeManager);
 
@@ -112,7 +112,7 @@
             new MetadataRequest(
                 entryA, ['size', 'modificationTime', 'contentThumbnailUrl']),
             new MetadataRequest(
-                entryB, ['size', 'modificationTime', 'contentThumbnailUrl'])
+                entryB, ['size', 'modificationTime', 'contentThumbnailUrl']),
           ])
           .then(results => {
             assertEquals(2, results.length);
@@ -136,7 +136,7 @@
         get: function(requests) {
           assertEquals(0, requests.length);
           return Promise.resolve([]);
-        }
+        },
       }),
       /** @type {!ExternalMetadataProvider} */ ({
         get: function(requests) {
@@ -149,7 +149,7 @@
             {present: false, imageWidth: 200},
             {present: true, imageWidth: 400},
           ]);
-        }
+        },
       }),
       /** @type {!ContentMetadataProvider} */ ({
         get: function(requests) {
@@ -166,7 +166,7 @@
         get: function(requests) {
           assertEquals(0, requests.length);
           return Promise.resolve([]);
-        }
+        },
       }),
       volumeManager);
 
@@ -175,7 +175,7 @@
           .get([
             new MetadataRequest(entryA, ['imageWidth']),
             new MetadataRequest(entryB, ['imageWidth']),
-            new MetadataRequest(entryC, ['imageWidth'])
+            new MetadataRequest(entryC, ['imageWidth']),
           ])
           .then(results => {
             assertEquals(3, results.length);
@@ -195,7 +195,7 @@
         get: function(requests) {
           assertEquals(0, requests.length);
           return Promise.resolve([]);
-        }
+        },
       }),
       /** @type {!ExternalMetadataProvider} */ ({
         get: function(requests) {
@@ -203,8 +203,12 @@
           assertEquals('filesystem://D', requests[0].entry.toURL());
           assertArrayEquals(
               [
-                'canCopy', 'canDelete', 'canRename', 'canAddChildren',
-                'modificationTime', 'size'
+                'canCopy',
+                'canDelete',
+                'canRename',
+                'canAddChildren',
+                'modificationTime',
+                'size',
               ],
               requests[0].names);
           return Promise.resolve([
@@ -217,7 +221,7 @@
               modificationTime: new Date(2015, 1, 2),
             },
           ]);
-        }
+        },
       }),
       /** @type {!ContentMetadataProvider} */ ({
         get: function(requests) {
@@ -229,7 +233,7 @@
         get: function(requests) {
           assertEquals(0, requests.length);
           return Promise.resolve([]);
-        }
+        },
       }),
       volumeManager);
 
@@ -239,7 +243,11 @@
             new MetadataRequest(
                 entryD,
                 [
-                  'size', 'canCopy', 'canDelete', 'canRename', 'canAddChildren'
+                  'size',
+                  'canCopy',
+                  'canDelete',
+                  'canRename',
+                  'canAddChildren',
                 ]),
           ])
           .then(results => {
@@ -262,13 +270,13 @@
         get: function(requests) {
           assertEquals(0, requests.length);
           return Promise.resolve([]);
-        }
+        },
       }),
       /** @type {!ExternalMetadataProvider} */ ({
         get: function(requests) {
           assertEquals(0, requests.length);
           return Promise.resolve([]);
-        }
+        },
       }),
       /** @type {!ContentMetadataProvider} */ ({
         get: function(requests) {
@@ -283,7 +291,7 @@
             sourceUrl: 'url',
             isDlpRestricted: true,
           }]);
-        }
+        },
       }),
       volumeManager);
 
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model.js b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model.js
index 89e6cd8..3aa3b09 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model.js
@@ -35,8 +35,12 @@
         .get(
             entries,
             [
-              'modificationTime', 'customIconUrl', 'contentMimeType',
-              'thumbnailUrl', 'croppedThumbnailUrl', 'present'
+              'modificationTime',
+              'customIconUrl',
+              'contentMimeType',
+              'thumbnailUrl',
+              'croppedThumbnailUrl',
+              'present',
             ])
         .then(metadataList => {
           const contentRequestEntries = [];
@@ -47,7 +51,7 @@
             results[url] = {
               filesystem: {
                 modificationTime: metadataList[i].modificationTime,
-                modificationTimeError: metadataList[i].modificationTimeError
+                modificationTimeError: metadataList[i].modificationTimeError,
               },
               external: {
                 thumbnailUrl: metadataList[i].thumbnailUrl,
@@ -58,10 +62,10 @@
                 customIconUrl: metadataList[i].customIconUrl,
                 customIconUrlError: metadataList[i].customIconUrlError,
                 present: metadataList[i].present,
-                presentError: metadataList[i].presentError
+                presentError: metadataList[i].presentError,
               },
               thumbnail: {},
-              media: {}
+              media: {},
             };
             const canUseContentThumbnail = metadataList[i].present &&
                 (FileType.isImage(
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.js
index 465f620..6d4a407 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.js
@@ -14,14 +14,14 @@
   name: 'image.jpg',
   toURL: function() {
     return 'filesystem://A';
-  }
+  },
 };
 
 const nonImageEntry = {
   name: 'note.txt',
   toURL: function() {
     return 'filesystem://B';
-  }
+  },
 };
 
 const contentThumbnailTransform = {
@@ -58,7 +58,7 @@
         result[name] = metadata[name];
       }
       return Promise.resolve([result]);
-    }
+    },
   }));
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/mock_thumbnail_loader.js b/ui/file_manager/file_manager/foreground/js/mock_thumbnail_loader.js
index 4ddc77f..a55636b 100644
--- a/ui/file_manager/file_manager/foreground/js/mock_thumbnail_loader.js
+++ b/ui/file_manager/file_manager/foreground/js/mock_thumbnail_loader.js
@@ -36,7 +36,7 @@
     return Promise.resolve({
       data: MockThumbnailLoader.testImageDataUrl,
       width: MockThumbnailLoader.testImageWidth,
-      height: MockThumbnailLoader.testImageHeight
+      height: MockThumbnailLoader.testImageHeight,
     });
   }
 }
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
index d0dc1eab..c965c40 100644
--- a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
@@ -385,7 +385,7 @@
 
   const shortcutListModel = new MockFolderShortcutDataModel([
     MockFileEntry.create(drive, '/root/shortcut'),
-    MockFileEntry.create(drive, '/root/shortcut2')
+    MockFileEntry.create(drive, '/root/shortcut2'),
   ]);
 
   const recentItem = new NavigationModelFakeItem(
@@ -542,7 +542,7 @@
   // Create a downloads folder inside the item.
   const downloadsVolume = volumeManager.volumeInfoList.item(1);
   /** @type {!MockFileSystem} */ (downloadsVolume.fileSystem).populate([
-    '/Downloads/'
+    '/Downloads/',
   ]);
 
   const shortcutListModel = new MockFolderShortcutDataModel([]);
diff --git a/ui/file_manager/file_manager/foreground/js/path_component_unittest.js b/ui/file_manager/file_manager/foreground/js/path_component_unittest.js
index 3600dea0..33b0d27b 100644
--- a/ui/file_manager/file_manager/foreground/js/path_component_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/path_component_unittest.js
@@ -37,14 +37,14 @@
   await validate('/.files-by-id/1234/file', [
     [
       'DRIVE_SHARED_WITH_ME_COLLECTION_LABEL',
-      'fake-entry://drive_shared_with_me'
+      'fake-entry://drive_shared_with_me',
     ],
     ['file', 'filesystem:drive/.files-by-id/1234/file'],
   ]);
   await validate('/.files-by-id/1234/a/file', [
     [
       'DRIVE_SHARED_WITH_ME_COLLECTION_LABEL',
-      'fake-entry://drive_shared_with_me'
+      'fake-entry://drive_shared_with_me',
     ],
     ['a', 'filesystem:drive/.files-by-id/1234/a'],
     ['file', 'filesystem:drive/.files-by-id/1234/a/file'],
@@ -53,14 +53,14 @@
   await validate('/.shortcut-targets-by-id/1-abc-xyz/file', [
     [
       'DRIVE_SHARED_WITH_ME_COLLECTION_LABEL',
-      'fake-entry://drive_shared_with_me'
+      'fake-entry://drive_shared_with_me',
     ],
     ['file', 'filesystem:drive/.shortcut-targets-by-id/1-abc-xyz/file'],
   ]);
   await validate('/.shortcut-targets-by-id/1-abc-xyz/a/file', [
     [
       'DRIVE_SHARED_WITH_ME_COLLECTION_LABEL',
-      'fake-entry://drive_shared_with_me'
+      'fake-entry://drive_shared_with_me',
     ],
     ['a', 'filesystem:drive/.shortcut-targets-by-id/1-abc-xyz/a'],
     ['file', 'filesystem:drive/.shortcut-targets-by-id/1-abc-xyz/a/file'],
diff --git a/ui/file_manager/file_manager/foreground/js/providers_model_unittest.js b/ui/file_manager/file_manager/foreground/js/providers_model_unittest.js
index f6cf353b..65b1884 100644
--- a/ui/file_manager/file_manager/foreground/js/providers_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/providers_model_unittest.js
@@ -158,7 +158,7 @@
           NOT_MOUNTED_SINGLE_PROVIDING_EXTENSION,
           MOUNTED_MULTIPLE_PROVIDING_EXTENSION,
           NOT_MOUNTED_FILE_PROVIDING_EXTENSION,
-          NOT_MOUNTED_DEVICE_PROVIDING_EXTENSION
+          NOT_MOUNTED_DEVICE_PROVIDING_EXTENSION,
         ]);
       },
     },
diff --git a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
index 223c4ee..5931f1f0 100644
--- a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
@@ -147,7 +147,7 @@
           descriptor: {
             appId: 'handler-extension-id',
             taskType: 'file',
-            actionId: 'open'
+            actionId: 'open',
           },
           isDefault: false,
         }),
@@ -155,7 +155,7 @@
           descriptor: {
             appId: 'handler-extension-id',
             taskType: 'file',
-            actionId: 'play'
+            actionId: 'play',
           },
           isDefault: true,
         }),
diff --git a/ui/file_manager/file_manager/foreground/js/task_history.js b/ui/file_manager/file_manager/foreground/js/task_history.js
index 76e1686b..062ef61 100644
--- a/ui/file_manager/file_manager/foreground/js/task_history.js
+++ b/ui/file_manager/file_manager/foreground/js/task_history.js
@@ -122,7 +122,7 @@
  * @enum {string}
  */
 TaskHistory.EventType = {
-  UPDATE: 'update'
+  UPDATE: 'update',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
index ca81bf2..2245e29 100644
--- a/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
+++ b/ui/file_manager/file_manager/foreground/js/thumbnail_loader.js
@@ -45,7 +45,7 @@
     const loadTargets = opt_loadTargets || [
       ThumbnailLoader.LoadTarget.CONTENT_METADATA,
       ThumbnailLoader.LoadTarget.EXTERNAL_METADATA,
-      ThumbnailLoader.LoadTarget.FILE_ENTRY
+      ThumbnailLoader.LoadTarget.FILE_ENTRY,
     ];
 
     /** @private @const {!Entry} */
@@ -200,7 +200,7 @@
           cache: true,
           priority: this.priority_,
           timestamp: modificationTime,
-          orientation: this.transform_
+          orientation: this.transform_,
         }),
         this.image_, () => {}, () => {
           this.image_.onerror(new Event('load-error'));
@@ -253,7 +253,7 @@
         cache: true,
         priority: this.priority_,
         timestamp: modificationTime,
-        orientation: this.transform_
+        orientation: this.transform_,
       });
 
       if (fillMode === ThumbnailLoader.FillMode.OVER_FILL) {
@@ -337,7 +337,7 @@
           cache: true,
           priority: this.priority_,
           timestamp: modificationTime,
-          orientation: this.transform_
+          orientation: this.transform_,
         }),
         this.image_, () => {}, () => {
           this.image_.onerror(new Event('load-error'));
@@ -516,7 +516,7 @@
   FILL: 0,       // Fill whole box. Image may be cropped.
   FIT: 1,        // Keep aspect ratio, do not crop.
   OVER_FILL: 2,  // Fill whole box with possible stretching.
-  AUTO: 3        // Try to fill, but if incompatible aspect ratio, then fit.
+  AUTO: 3,       // Try to fill, but if incompatible aspect ratio, then fit.
 };
 
 /**
@@ -525,7 +525,7 @@
  */
 ThumbnailLoader.LoaderType = {
   IMAGE: 0,
-  CANVAS: 1
+  CANVAS: 1,
 };
 
 /**
@@ -538,7 +538,7 @@
   // e.g. EXIF thumbnail.
   CONTENT_METADATA: 'contentMetadata',
   // Image file itself.
-  FILE_ENTRY: 'fileEntry'
+  FILE_ENTRY: 'fileEntry',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js b/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js
index a51c843..0c0cdebb 100644
--- a/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/thumbnail_loader_unittest.js
@@ -125,7 +125,7 @@
       status: 'success',
       data: generateSampleImageDataUrl(32, 64),
       width: 32,
-      height: 64
+      height: 64,
     });
   });
 
@@ -136,8 +136,8 @@
         rotate90: 1,
         scaleX: 1,
         scaleY: -1,
-      }
-    }
+      },
+    },
   };
 
   const fileSystem = new MockFileSystem('volume-id');
@@ -164,15 +164,15 @@
       status: 'success',
       data: externalThumbnailDataUrl,
       width: 32,
-      height: 32
+      height: 32,
     });
   });
 
   const metadata = {
     external: {
       thumbnailUrl: externalThumbnailUrl,
-      croppedThumbnailUrl: externalCroppedThumbnailUrl
-    }
+      croppedThumbnailUrl: externalCroppedThumbnailUrl,
+    },
   };
 
   const fileSystem = new MockFileSystem('volume-id');
@@ -198,7 +198,7 @@
       status: 'success',
       data: generateSampleImageDataUrl(32, 64),
       width: 32,
-      height: 64
+      height: 64,
     });
   });
 
@@ -209,8 +209,8 @@
         rotate90: 1,
         scaleX: 1,
         scaleY: -1,
-      }
-    }
+      },
+    },
   };
 
   const fileSystem = new MockFileSystem('volume-id');
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners/drive_low_space_banner.js b/ui/file_manager/file_manager/foreground/js/ui/banners/drive_low_space_banner.js
index 201c80e9..9fdbe9e 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/banners/drive_low_space_banner.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners/drive_low_space_banner.js
@@ -53,7 +53,7 @@
   allowedVolumes() {
     return [{
       type: VolumeManagerCommon.VolumeType.DRIVE,
-      root: VolumeManagerCommon.RootType.DRIVE
+      root: VolumeManagerCommon.RootType.DRIVE,
     }];
   }
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners/drive_offline_pinning_banner.js b/ui/file_manager/file_manager/foreground/js/ui/banners/drive_offline_pinning_banner.js
index 85ee91011..64ee74f 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/banners/drive_offline_pinning_banner.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners/drive_offline_pinning_banner.js
@@ -52,7 +52,7 @@
     if (util.isDriveDssPinEnabled()) {
       return [{
         type: VolumeManagerCommon.VolumeType.DRIVE,
-        root: VolumeManagerCommon.RootType.DRIVE
+        root: VolumeManagerCommon.RootType.DRIVE,
       }];
     }
     return [];
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners/educational_banner.js b/ui/file_manager/file_manager/foreground/js/ui/banners/educational_banner.js
index 55002a0..b1d29ac 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/banners/educational_banner.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners/educational_banner.js
@@ -119,7 +119,7 @@
               new CustomEvent(Banner.Event.BANNER_DISMISSED_FOREVER, {
                 bubbles: true,
                 composed: true,
-                detail: {banner: this.getBannerInstance_()}
+                detail: {banner: this.getBannerInstance_()},
               }));
         }
         e.preventDefault();
@@ -156,7 +156,7 @@
     this.dispatchEvent(new CustomEvent(Banner.Event.BANNER_DISMISSED_FOREVER, {
       bubbles: true,
       composed: true,
-      detail: {banner: this.getBannerInstance_()}
+      detail: {banner: this.getBannerInstance_()},
     }));
   }
 }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners/holding_space_welcome_banner.js b/ui/file_manager/file_manager/foreground/js/ui/banners/holding_space_welcome_banner.js
index 4f2f732..a6fbf17 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/banners/holding_space_welcome_banner.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners/holding_space_welcome_banner.js
@@ -42,7 +42,7 @@
       if (type === VolumeManagerCommon.VolumeType.DRIVE) {
         return {
           type: VolumeManagerCommon.VolumeType.DRIVE,
-          root: VolumeManagerCommon.RootType.DRIVE
+          root: VolumeManagerCommon.RootType.DRIVE,
         };
       }
       return {type};
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners/shared_with_crostini_pluginvm_banner.js b/ui/file_manager/file_manager/foreground/js/ui/banners/shared_with_crostini_pluginvm_banner.js
index 737f583a..394895185 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/banners/shared_with_crostini_pluginvm_banner.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners/shared_with_crostini_pluginvm_banner.js
@@ -51,7 +51,7 @@
       {root: VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME},
       {root: VolumeManagerCommon.RootType.CROSTINI},
       {root: VolumeManagerCommon.RootType.ARCHIVE},
-      {root: VolumeManagerCommon.RootType.SMB}
+      {root: VolumeManagerCommon.RootType.SMB},
     ];
   }
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/combobutton.js b/ui/file_manager/file_manager/foreground/js/ui/combobutton.js
index 5385983..d53b2cf 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/combobutton.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/combobutton.js
@@ -111,7 +111,7 @@
         }
       },
       enumerable: true,
-      configurable: true
+      configurable: true,
     });
   }
 
@@ -148,7 +148,7 @@
         this.setMenu_(menu);
       },
       enumerable: true,
-      configurable: true
+      configurable: true,
     });
     Object.defineProperty(el, 'defaultItem', {
       get() {
@@ -158,7 +158,7 @@
         this.setDefaultItem_(defaultItem);
       },
       enumerable: true,
-      configurable: true
+      configurable: true,
     });
     el.addBooleanProperty_('multiple');
     el.addBooleanProperty_('disabled');
diff --git a/ui/file_manager/file_manager/foreground/js/ui/dialog_footer.js b/ui/file_manager/file_manager/foreground/js/ui/dialog_footer.js
index 5136690..cc9b2558 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/dialog_footer.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/dialog_footer.js
@@ -74,7 +74,7 @@
         return this.getSelectValue();
       },
       enumerable: true,
-      configurable: true
+      configurable: true,
     });
     this.fileTypeSelector.getSelectValue = this.getSelectValue_.bind(this);
     this.fileTypeSelector.addEventListener(
diff --git a/ui/file_manager/file_manager/foreground/js/ui/drag_selector.js b/ui/file_manager/file_manager/foreground/js/ui/drag_selector.js
index 4766f79..f3b7a70 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/drag_selector.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/drag_selector.js
@@ -74,7 +74,7 @@
     const rect = element.cachedBounds;
     return {
       x: event.clientX - rect.left + element.scrollLeft,
-      y: event.clientY - rect.top + element.scrollTop
+      y: event.clientY - rect.top + element.scrollTop,
     };
   }
 
@@ -137,7 +137,8 @@
       left: Math.max(Math.min(this.startX_, pos.x), 0),
       top: Math.max(Math.min(this.startY_, pos.y), 0),
       right: Math.min(Math.max(this.startX_, pos.x), this.target_.scrollWidth),
-      bottom: Math.min(Math.max(this.startY_, pos.y), this.target_.scrollHeight)
+      bottom:
+          Math.min(Math.max(this.startY_, pos.y), this.target_.scrollHeight),
     };
     borderBounds.width = borderBounds.right - borderBounds.left;
     borderBounds.height = borderBounds.bottom - borderBounds.top;
@@ -236,5 +237,5 @@
  */
 DragSelector.SelectionFlag_ = {
   IN_LAST_SELECTION: 1 << 0,
-  IN_CURRENT_SELECTION: 1 << 1
+  IN_CURRENT_SELECTION: 1 << 1,
 };
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
index d0fb32a..b3a5055 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
@@ -514,7 +514,7 @@
       // consumed in redraw() method in the parent class.
       first: beginIndex + 1,
       length: endIndex - beginIndex - 1,
-      last: endIndex - 1
+      last: endIndex - 1,
     };
     return result;
   }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_grid_unittest.js b/ui/file_manager/file_manager/foreground/js/ui/file_grid_unittest.js
index 78fa5ab..92f1283b 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_grid_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_grid_unittest.js
@@ -164,19 +164,19 @@
         startIndex: 5,
         endIndex: 9,
         label: 'earlier_this_week',
-        group: 'earlier_this_week'
+        group: 'earlier_this_week',
       },
       {
         startIndex: 10,
         endIndex: 15,
         label: 'earlier_this_month',
-        group: 'earlier_this_month'
+        group: 'earlier_this_month',
       },
       {
         startIndex: 16,
         endIndex: 16,
         label: 'earlier_this_year',
-        group: 'earlier_this_year'
+        group: 'earlier_this_year',
       },
       {startIndex: 17, endIndex: 19, label: 'older', group: 'older'},
     ];
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
index 98c65a8..c83846776 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
@@ -702,7 +702,7 @@
       handleSplitterDragEnd: function(e) {
         FileSplitter.prototype.handleSplitterDragEnd.apply(this, arguments);
         this.ownerDocument.documentElement.classList.remove('col-resize');
-      }
+      },
     };
 
     /** @type Object */ (customSplitter).decorate(splitterElement);
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_metadata_formatter.js b/ui/file_manager/file_manager/foreground/js/ui/file_metadata_formatter.js
index 1e41d7d..e66620f 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_metadata_formatter.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_metadata_formatter.js
@@ -35,7 +35,7 @@
       day: 'numeric',
       hour: 'numeric',
       minute: 'numeric',
-      hour12: use12hourClock
+      hour12: use12hourClock,
     });
     dispatchSimpleEvent(this, 'date-time-format-changed');
   }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table.js b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
index 03ed87d4..d2f458c 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
@@ -210,7 +210,7 @@
     const config = {};
     for (let i = 0; i < this.columns_.length; i++) {
       config[this.columns_[i].id] = {
-        width: snapshot.newPos[i + 1] - snapshot.newPos[i]
+        width: snapshot.newPos[i + 1] - snapshot.newPos[i],
       };
     }
     return config;
@@ -983,7 +983,7 @@
             history => {
               return Promise.all([
                 history.wasImported(fileEntry, destination),
-                history.wasCopied(fileEntry, destination)
+                history.wasCopied(fileEntry, destination),
               ]);
             })
         .then(
@@ -1120,8 +1120,13 @@
             this.metadataModel_.getCache(
                 [entry],
                 [
-                  'availableOffline', 'customIconUrl', 'shared',
-                  'isMachineRoot', 'isExternalMedia', 'hosted', 'pinned'
+                  'availableOffline',
+                  'customIconUrl',
+                  'shared',
+                  'isMachineRoot',
+                  'isExternalMedia',
+                  'hosted',
+                  'pinned',
                 ])[0],
             util.isTeamDriveRoot(entry));
       });
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js b/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js
index 315dc44..a6c4f761 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js
@@ -297,8 +297,13 @@
   // updated when the metadata is ready via updateListItemsMetadata. For files
   // not on an external backend, externalProps is not available.
   const externalProps = metadataModel.getCache([entry], [
-    'hosted', 'availableOffline', 'customIconUrl', 'shared', 'isMachineRoot',
-    'isExternalMedia', 'pinned'
+    'hosted',
+    'availableOffline',
+    'customIconUrl',
+    'shared',
+    'isMachineRoot',
+    'isExternalMedia',
+    'pinned',
   ])[0];
   filelist.updateListItemExternalProps(
       li, externalProps, util.isTeamDriveRoot(entry));
@@ -325,7 +330,7 @@
       } else {
         this.removeAttribute('selected');
       }
-    }
+    },
   });
 };
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table_list_unittest.js b/ui/file_manager/file_manager/foreground/js/ui/file_table_list_unittest.js
index 70ecd8e8..aaa27d1 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table_list_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table_list_unittest.js
@@ -369,19 +369,19 @@
         startIndex: 3,
         endIndex: 4,
         label: 'earlier_this_week',
-        group: 'earlier_this_week'
+        group: 'earlier_this_week',
       },
       {
         startIndex: 5,
         endIndex: 6,
         label: 'earlier_this_month',
-        group: 'earlier_this_month'
+        group: 'earlier_this_month',
       },
       {
         startIndex: 7,
         endIndex: 8,
         label: 'earlier_this_year',
-        group: 'earlier_this_year'
+        group: 'earlier_this_year',
       },
       {startIndex: 9, endIndex: 9, label: 'older', group: 'older'},
     ];
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler.js b/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler.js
index c9f4d76..d2a5d506 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler.js
@@ -282,5 +282,5 @@
   TAP: 'tap',
   LONG_PRESS: 'longpress',
   LONG_TAP: 'longtap',
-  TWO_FINGER_TAP: 'twofingertap'
+  TWO_FINGER_TAP: 'twofingertap',
 };
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler_unittest.js b/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler_unittest.js
index 572a7b1..d183ac3 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler_unittest.js
@@ -39,7 +39,7 @@
     identifier: identifier,
     clientX: clientX,
     clientY: clientY,
-    target: dummyTarget
+    target: dummyTarget,
   });
 }
 
@@ -117,7 +117,7 @@
         cancelable: true,
         changedTouches: [touch0],
         targetTouches: [touch0],
-        touches: [touch0]
+        touches: [touch0],
       }),
       0, handleTap);
   assertEquals(0, events.length);
@@ -220,7 +220,7 @@
         cancelable: true,
         changedTouches: [touch1_1],
         targetTouches: [touch0_0],
-        touches: [touch0_0]
+        touches: [touch0_0],
       }),
       1, handleTap);
   handler.handleTouchEvents(
diff --git a/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog.js
index f22bde7b..51bd915 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog.js
@@ -114,11 +114,11 @@
     const details = [
       [
         str('INSTALL_LINUX_PACKAGE_DETAILS_APPLICATION_LABEL'),
-        linux_package_info.name
+        linux_package_info.name,
       ],
       [
         str('INSTALL_LINUX_PACKAGE_DETAILS_VERSION_LABEL'),
-        linux_package_info.version
+        linux_package_info.version,
       ],
     ];
 
@@ -133,7 +133,8 @@
     }
     if (description) {
       details.push([
-        str('INSTALL_LINUX_PACKAGE_DETAILS_DESCRIPTION_LABEL'), description
+        str('INSTALL_LINUX_PACKAGE_DETAILS_DESCRIPTION_LABEL'),
+        description,
       ]);
     }
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog_unittest.js b/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog_unittest.js
index 1c63b2c..bb05824 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog_unittest.js
@@ -22,7 +22,7 @@
   chrome.fileManagerPrivate = {
     getLinuxPackageInfo: (entry, callback) => {
       getInfoCallback = callback;
-    }
+    },
   };
 
   const info = {name: 'n', version: 'v', info: 'i', summary: 's'};
diff --git a/ui/file_manager/file_manager/foreground/js/ui/list_container.js b/ui/file_manager/file_manager/foreground/js/ui/list_container.js
index f7486f6..f2e55d7 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/list_container.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/list_container.js
@@ -382,7 +382,7 @@
  * @const
  */
 ListContainer.EventType = {
-  TEXT_SEARCH: 'textsearch'
+  TEXT_SEARCH: 'textsearch',
 };
 
 /**
@@ -392,7 +392,7 @@
 ListContainer.ListType = {
   UNINITIALIZED: 'uninitialized',
   DETAIL: 'detail',
-  THUMBNAIL: 'thumb'
+  THUMBNAIL: 'thumb',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/ui/multi_menu_button.js b/ui/file_manager/file_manager/foreground/js/ui/multi_menu_button.js
index 7e43de1..a83d402 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/multi_menu_button.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/multi_menu_button.js
@@ -75,7 +75,7 @@
         this.setMenu_(menu);
       },
       enumerable: true,
-      configurable: true
+      configurable: true,
     });
     element = /** @type {!MultiMenuButton} */ (element);
     element.decorate();
diff --git a/ui/file_manager/file_manager/foreground/js/ui/search_box.js b/ui/file_manager/file_manager/foreground/js/ui/search_box.js
index 24122c47..0bd648d 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/search_box.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/search_box.js
@@ -329,7 +329,7 @@
   // Dispatched when the text in the search box is changed.
   TEXT_CHANGE: 'textchange',
   // Dispatched when the item in the auto complete list is selected.
-  ITEM_SELECT: 'itemselect'
+  ITEM_SELECT: 'itemselect',
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/ui/tree.js b/ui/file_manager/file_manager/foreground/js/ui/tree.js
index 1dca296..9ddeddb8 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/tree.js
@@ -269,7 +269,7 @@
       return this.selectedItem.rowElement.getBoundingClientRect();
     }
     return this.getBoundingClientRect();
-  }
+  },
 };
 
 /**
@@ -696,7 +696,7 @@
 
   get editing() {
     return this.hasAttribute('editing');
-  }
+  },
 };
 
 /**
diff --git a/ui/file_manager/file_manager/state/reducers.ts b/ui/file_manager/file_manager/state/reducers.ts
index 029110da..fa2ca0d 100644
--- a/ui/file_manager/file_manager/state/reducers.ts
+++ b/ui/file_manager/file_manager/state/reducers.ts
@@ -21,7 +21,7 @@
     case Actions.CHANGE_DIRECTORY:
       return Object.assign(state, {
         currentDirectory:
-            changeDirectory(state, action as ChangeDirectoryAction)
+            changeDirectory(state, action as ChangeDirectoryAction),
       });
 
     default:
@@ -99,6 +99,6 @@
         label: c.name,
         key: c.url_,
       };
-    })
+    }),
   });
 }
diff --git a/ui/file_manager/image_loader/image_loader_client_unittest.js b/ui/file_manager/image_loader/image_loader_client_unittest.js
index 00da7aa..dfe109a7 100644
--- a/ui/file_manager/image_loader/image_loader_client_unittest.js
+++ b/ui/file_manager/image_loader/image_loader_client_unittest.js
@@ -14,7 +14,7 @@
     MetricTypeType:
         {HISTOGRAM_LOG: 'histogram-log', HISTOGRAM_LINEAR: 'histogram-linear'},
     recordPercentage: function() {},
-    recordValue: function() {}
+    recordValue: function() {},
   };
 
   chrome.i18n = {
diff --git a/ui/file_manager/image_loader/image_loader_unittest.js b/ui/file_manager/image_loader/image_loader_unittest.js
index 4d67ab3c..6660c90 100644
--- a/ui/file_manager/image_loader/image_loader_unittest.js
+++ b/ui/file_manager/image_loader/image_loader_unittest.js
@@ -33,7 +33,7 @@
   const options = {
     maxWidth: 100,
     maxHeight: 100,
-    orientation: ImageOrientation.fromClockwiseRotation(0)
+    orientation: ImageOrientation.fromClockwiseRotation(0),
   };
   const result = calculateCopyParametersFromOptions(source, options);
   assertEquals(0, result.source.x);
@@ -60,7 +60,7 @@
   const options = {
     maxWidth: 100,
     maxHeight: 100,
-    orientation: ImageOrientation.fromClockwiseRotation(1)
+    orientation: ImageOrientation.fromClockwiseRotation(1),
   };
   const result = calculateCopyParametersFromOptions(source, options);
   assertEquals(0, result.source.x);
@@ -88,7 +88,7 @@
     width: 50,
     height: 50,
     crop: true,
-    orientation: ImageOrientation.fromClockwiseRotation(0)
+    orientation: ImageOrientation.fromClockwiseRotation(0),
   };
   const result = calculateCopyParametersFromOptions(source, options);
   assertEquals(350, result.source.x);
@@ -116,7 +116,7 @@
     width: 50,
     height: 50,
     crop: true,
-    orientation: ImageOrientation.fromClockwiseRotation(0)
+    orientation: ImageOrientation.fromClockwiseRotation(0),
   };
   const result = calculateCopyParametersFromOptions(source, options);
   assertEquals(87, result.source.x);
@@ -144,7 +144,7 @@
     width: 50,
     height: 50,
     crop: true,
-    orientation: ImageOrientation.fromClockwiseRotation(0)
+    orientation: ImageOrientation.fromClockwiseRotation(0),
   };
   const result = calculateCopyParametersFromOptions(source, options);
   assertEquals(5, result.source.x);
@@ -172,7 +172,7 @@
     width: 50,
     height: 50,
     crop: true,
-    orientation: ImageOrientation.fromClockwiseRotation(1)
+    orientation: ImageOrientation.fromClockwiseRotation(1),
   };
   const result = calculateCopyParametersFromOptions(source, options);
   assertEquals(0, result.source.x);
diff --git a/ui/file_manager/image_loader/image_loader_util.js b/ui/file_manager/image_loader/image_loader_util.js
index caf877f..78ef192 100644
--- a/ui/file_manager/image_loader/image_loader_util.js
+++ b/ui/file_manager/image_loader/image_loader_util.js
@@ -155,7 +155,7 @@
       canvas: {
         width: request.width,
         height: request.height,
-      }
+      },
     };
   }
 
@@ -171,17 +171,17 @@
       x: 0,
       y: 0,
       width: source.width,
-      height: source.height
+      height: source.height,
     },
     target: {
       x: 0,
       y: 0,
       width: targetDimensions.width,
-      height: targetDimensions.height
+      height: targetDimensions.height,
     },
     canvas: {
       width: targetCanvasDimensions.width,
-      height: targetCanvasDimensions.height
-    }
+      height: targetCanvasDimensions.height,
+    },
   };
 };
diff --git a/ui/file_manager/image_loader/image_request_task.js b/ui/file_manager/image_loader/image_request_task.js
index 9f6bea3..69420cd 100644
--- a/ui/file_manager/image_loader/image_request_task.js
+++ b/ui/file_manager/image_loader/image_request_task.js
@@ -165,7 +165,7 @@
   svg: 'image/svg',
   bmp: 'image/bmp',
   jpg: 'image/jpeg',
-  jpeg: 'image/jpeg'
+  jpeg: 'image/jpeg',
 };
 
 /**
@@ -472,7 +472,7 @@
           video.src =
               '';  // Make sure to stop loading remaining part of the video.
           throw new Error('Seeking video failed.');
-        })
+        }),
       ])
       .then(() => {
         const canvas = assertInstanceof(
diff --git a/ui/file_manager/image_loader/load_image_request.js b/ui/file_manager/image_loader/load_image_request.js
index 59280f2..18d4c5b7 100644
--- a/ui/file_manager/image_loader/load_image_request.js
+++ b/ui/file_manager/image_loader/load_image_request.js
@@ -13,7 +13,7 @@
  */
 export const LoadImageResponseStatus = {
   SUCCESS: 'success',
-  ERROR: 'error'
+  ERROR: 'error',
 };
 
 /**
@@ -154,7 +154,7 @@
       width: request.width,
       height: request.height,
       maxWidth: request.maxWidth,
-      maxHeight: request.maxHeight
+      maxHeight: request.maxHeight,
     });
   }
 
diff --git a/ui/file_manager/image_loader/piex/tests.js b/ui/file_manager/image_loader/piex/tests.js
index 5e0f23b..d0ab13d 100644
--- a/ui/file_manager/image_loader/piex/tests.js
+++ b/ui/file_manager/image_loader/piex/tests.js
@@ -50,13 +50,14 @@
 
   const browser = await puppeteer.launch({
     headless: !program.debug,
-    args: [...args]
+    args: [...args],
   });
 
   const page = await browser.newPage();
 
   await page.setViewport({
-    width: 1200, height: 800
+    width: 1200,
+    height: 800,
   });
 
   if (program.debug) {
@@ -74,7 +75,7 @@
   });
 
   await page.goto('http://localhost:8123/' + process.argv[2], {
-    waitUntil: 'networkidle2'
+    waitUntil: 'networkidle2',
   });
 
   await page.mainFrame().waitForFunction('document.title == "READY"');
diff --git a/ui/file_manager/image_loader/piex_loader.js b/ui/file_manager/image_loader/piex_loader.js
index 4e47612..f96b0c3 100644
--- a/ui/file_manager/image_loader/piex_loader.js
+++ b/ui/file_manager/image_loader/piex_loader.js
@@ -58,7 +58,7 @@
   onAbort: (error) => {
     piexFailed = true;
     throw error;
-  }
+  },
 };
 
 /** @type {?Promise<undefined>} */
@@ -764,5 +764,5 @@
 };
 
 export const PIEX_LOADER_TEST_ONLY = {
-  getModule: () => PiexModule
+  getModule: () => PiexModule,
 };
diff --git a/ui/file_manager/integration_tests/audio_player/background.js b/ui/file_manager/integration_tests/audio_player/background.js
index 075cf3d..ed113a2 100644
--- a/ui/file_manager/integration_tests/audio_player/background.js
+++ b/ui/file_manager/integration_tests/audio_player/background.js
@@ -104,7 +104,7 @@
       };
       // Run the test.
       chrome.test.runTests([testCase[testCaseSymbol]]);
-    }
+    },
   ];
   steps.shift()();
 });
diff --git a/ui/file_manager/integration_tests/audio_player/click_control_buttons.js b/ui/file_manager/integration_tests/audio_player/click_control_buttons.js
index 22f3bde..d0fd0b7 100644
--- a/ui/file_manager/integration_tests/audio_player/click_control_buttons.js
+++ b/ui/file_manager/integration_tests/audio_player/click_control_buttons.js
@@ -165,35 +165,41 @@
 testcase.changeVolumeLevel = function() {
   var openAudio = launch('local', 'downloads', [ENTRIES.beautiful]);
   var appId;
-  return openAudio.then(function(args) {
-    appId = args[0];
-  }).then(function() {
-    // The Audio Player default volume level should be 50.
-    return remoteCallAudioPlayer.waitForElement(
-        appId, [['audio-player', 'control-panel[volume="50"]']]);
-  }).then(function() {
-    // Clicking the volume button should mute the player.
-    return remoteCallAudioPlayer.callRemoteTestUtil(
-        'fakeMouseClick', appId, ['#volumeButton']);
-  }).then(function() {
-    return Promise.all([
-      remoteCallAudioPlayer.waitForElement(
-          appId, [['audio-player', 'control-panel[volume="0"]']]),
-      remoteCallAudioPlayer.waitForElement(
-          appId, [controlPanelQuery('#volumeButton[aria-label="Unmute"]')])
-    ]);
-  }).then(function() {
-    // Clicking it again should unmute and restore the volume.
-    return remoteCallAudioPlayer.callRemoteTestUtil(
-        'fakeMouseClick', appId, ['#volumeButton']);
-  }).then(function() {
-    return Promise.all([
-      remoteCallAudioPlayer.waitForElement(
-          appId, [['audio-player', 'control-panel[volume="50"]']]),
-      remoteCallAudioPlayer.waitForElement(
-          appId, [controlPanelQuery('#volumeButton[aria-label="Mute"]')])
-    ]);
-  });
+  return openAudio
+      .then(function(args) {
+        appId = args[0];
+      })
+      .then(function() {
+        // The Audio Player default volume level should be 50.
+        return remoteCallAudioPlayer.waitForElement(
+            appId, [['audio-player', 'control-panel[volume="50"]']]);
+      })
+      .then(function() {
+        // Clicking the volume button should mute the player.
+        return remoteCallAudioPlayer.callRemoteTestUtil(
+            'fakeMouseClick', appId, ['#volumeButton']);
+      })
+      .then(function() {
+        return Promise.all([
+          remoteCallAudioPlayer.waitForElement(
+              appId, [['audio-player', 'control-panel[volume="0"]']]),
+          remoteCallAudioPlayer.waitForElement(
+              appId, [controlPanelQuery('#volumeButton[aria-label="Unmute"]')]),
+        ]);
+      })
+      .then(function() {
+        // Clicking it again should unmute and restore the volume.
+        return remoteCallAudioPlayer.callRemoteTestUtil(
+            'fakeMouseClick', appId, ['#volumeButton']);
+      })
+      .then(function() {
+        return Promise.all([
+          remoteCallAudioPlayer.waitForElement(
+              appId, [['audio-player', 'control-panel[volume="50"]']]),
+          remoteCallAudioPlayer.waitForElement(
+              appId, [controlPanelQuery('#volumeButton[aria-label="Mute"]')]),
+        ]);
+      });
 };
 
 /**
@@ -210,10 +216,9 @@
   }).then(function() {
     // Audio player starts playing automatically
     return Promise.all([
+      remoteCallAudioPlayer.waitForElement(appId, 'audio-player[playing]'),
       remoteCallAudioPlayer.waitForElement(
-          appId, 'audio-player[playing]'),
-      remoteCallAudioPlayer.waitForElement(
-          appId, [controlPanelQuery('#play[aria-label="Pause"]')])
+          appId, [controlPanelQuery('#play[aria-label="Pause"]')]),
     ]);
   }).then(function() {
     // ... and track 0 should be active.
@@ -230,7 +235,7 @@
       remoteCallAudioPlayer.waitForElement(
           appId, 'audio-player:not([playing])'),
       remoteCallAudioPlayer.waitForElement(
-          appId, [controlPanelQuery('#play[aria-label="Play"]')])
+          appId, [controlPanelQuery('#play[aria-label="Play"]')]),
     ]);
   }).then(function() {
     // ... and track 0 should still be active.
@@ -246,8 +251,7 @@
     return Promise.all([
       remoteCallAudioPlayer.waitForElement(
           appId, [trackListQuery('.track[index="1"][active]')]),
-      remoteCallAudioPlayer.waitForElement(
-          appId, 'audio-player[playing]')
+      remoteCallAudioPlayer.waitForElement(appId, 'audio-player[playing]'),
     ]);
   });
 };
@@ -266,10 +270,9 @@
   }).then(function() {
     // Audio player starts playing automatically
     return Promise.all([
+      remoteCallAudioPlayer.waitForElement(appId, 'audio-player[playing]'),
       remoteCallAudioPlayer.waitForElement(
-          appId, 'audio-player[playing]'),
-      remoteCallAudioPlayer.waitForElement(
-          appId, [controlPanelQuery('#play[aria-label="Pause"]')])
+          appId, [controlPanelQuery('#play[aria-label="Pause"]')]),
     ]);
   }).then(function() {
     // ... and track 0 should be active.
@@ -285,7 +288,7 @@
       remoteCallAudioPlayer.waitForElement(
           appId, 'audio-player:not([playing])'),
       remoteCallAudioPlayer.waitForElement(
-          appId, [controlPanelQuery('#play[aria-label="Play"]')])
+          appId, [controlPanelQuery('#play[aria-label="Play"]')]),
     ]);
   }).then(function() {
     // ... and track 0 should still be active.
@@ -308,8 +311,7 @@
     return Promise.all([
       remoteCallAudioPlayer.waitForElement(
           appId, trackListQuery('.track[index="0"][active]')),
-      remoteCallAudioPlayer.waitForElement(
-          appId, 'audio-player[playing]')
+      remoteCallAudioPlayer.waitForElement(appId, 'audio-player[playing]'),
     ]);
   });
 };
@@ -328,10 +330,9 @@
   }).then(function() {
     // Audio player starts playing automatically
     return Promise.all([
+      remoteCallAudioPlayer.waitForElement(appId, 'audio-player[playing]'),
       remoteCallAudioPlayer.waitForElement(
-          appId, 'audio-player[playing]'),
-      remoteCallAudioPlayer.waitForElement(
-          appId, [controlPanelQuery('#play[aria-label="Pause"]')])
+          appId, [controlPanelQuery('#play[aria-label="Pause"]')]),
     ]);
   }).then(function() {
     // ... and track 0 should be active.
@@ -347,7 +348,7 @@
       remoteCallAudioPlayer.waitForElement(
           appId, 'audio-player:not([playing])'),
       remoteCallAudioPlayer.waitForElement(
-          appId, [controlPanelQuery('#play[aria-label="Play"]')])
+          appId, [controlPanelQuery('#play[aria-label="Play"]')]),
     ]);
   }).then(function() {
     // ... and track 0 should still be active.
@@ -371,8 +372,7 @@
     return Promise.all([
       remoteCallAudioPlayer.waitForElement(
           appId, trackListQuery('.track[index="1"][active]')),
-      remoteCallAudioPlayer.waitForElement(
-          appId, 'audio-player[playing]')
+      remoteCallAudioPlayer.waitForElement(appId, 'audio-player[playing]'),
     ]);
   });
 };
diff --git a/ui/file_manager/integration_tests/dialog_type.js b/ui/file_manager/integration_tests/dialog_type.js
index f3866a3..857d165e 100644
--- a/ui/file_manager/integration_tests/dialog_type.js
+++ b/ui/file_manager/integration_tests/dialog_type.js
@@ -18,5 +18,5 @@
   SELECT_SAVEAS_FILE: 'saveas-file',
   SELECT_OPEN_FILE: 'open-file',
   SELECT_OPEN_MULTI_FILE: 'open-multi-file',
-  FULL_PAGE: 'full-page'
+  FULL_PAGE: 'full-page',
 };
diff --git a/ui/file_manager/integration_tests/file_manager/android_photos.js b/ui/file_manager/integration_tests/file_manager/android_photos.js
index c78ef301..804da4f 100644
--- a/ui/file_manager/integration_tests/file_manager/android_photos.js
+++ b/ui/file_manager/integration_tests/file_manager/android_photos.js
@@ -39,11 +39,14 @@
   const photosBannerShownQuery =
       '#banners > photos-welcome-banner:not([hidden])';
   const photosBannerTextQuery = [
-    '#banners > photos-welcome-banner', 'educational-banner',
-    '#educational-text-group'
+    '#banners > photos-welcome-banner',
+    'educational-banner',
+    '#educational-text-group',
   ];
   const photosBannerDismissButton = [
-    '#banners > photos-welcome-banner', 'educational-banner', '#dismiss-button'
+    '#banners > photos-welcome-banner',
+    'educational-banner',
+    '#dismiss-button',
   ];
 
   // Initial state: In the new framework banner is lazily loaded so will not be
diff --git a/ui/file_manager/integration_tests/file_manager/background.js b/ui/file_manager/integration_tests/file_manager/background.js
index c7b9badf..732050e 100644
--- a/ui/file_manager/integration_tests/file_manager/background.js
+++ b/ui/file_manager/integration_tests/file_manager/background.js
@@ -368,7 +368,7 @@
       };
       // Run the test.
       chrome.test.runTests([testCase[testCaseSymbol]]);
-    }
+    },
   ];
   steps.shift()();
 });
diff --git a/ui/file_manager/integration_tests/file_manager/crostini.js b/ui/file_manager/integration_tests/file_manager/crostini.js
index b110fa44..79c16c1 100644
--- a/ui/file_manager/integration_tests/file_manager/crostini.js
+++ b/ui/file_manager/integration_tests/file_manager/crostini.js
@@ -112,7 +112,7 @@
           descriptor: pluginVmAppDescriptor,
           title: 'App (Windows)',
           verb: 'open_with',
-        }
+        },
       ]]));
 
   // Right click on 'hello.txt' file, and wait for dialog with 'Open with'.
@@ -187,7 +187,7 @@
           descriptor: pluginVmAppDescriptor,
           title: 'App (Windows)',
           verb: 'open_with',
-        }
+        },
       ]]));
 
   // Right click on 'hello.txt' file, and wait for dialog with 'Open with'.
diff --git a/ui/file_manager/integration_tests/file_manager/directory_tree.js b/ui/file_manager/integration_tests/file_manager/directory_tree.js
index 06b1f6e..b23fa96 100644
--- a/ui/file_manager/integration_tests/file_manager/directory_tree.js
+++ b/ui/file_manager/integration_tests/file_manager/directory_tree.js
@@ -102,7 +102,7 @@
       lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
       nameText: '' + i,
       sizeText: '--',
-      typeText: 'Folder'
+      typeText: 'Folder',
     }));
   }
 
diff --git a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
index bc43427..f458dcc 100644
--- a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
@@ -26,7 +26,7 @@
                      lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
                      nameText: 'destination',
                      sizeText: '--',
-                     typeText: 'Folder'
+                     typeText: 'Folder',
                    })]);
   return appId;
 }
@@ -46,7 +46,7 @@
       lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
       nameText: 'photos',
       sizeText: '--',
-      typeText: 'Folder'
+      typeText: 'Folder',
     })]);
 
 /**
@@ -452,7 +452,7 @@
                      lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
                      nameText: 'child-folder',
                      sizeText: '--',
-                     typeText: 'Folder'
+                     typeText: 'Folder',
                    })]);
 
   // Navigate to child folder.
@@ -826,7 +826,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [ENTRIES.zipArchive.targetPath],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   const zipMenus = [
@@ -870,7 +870,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [ENTRIES.zipArchive.targetPath],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   // Open Files app on Downloads containing a zip file.
@@ -978,7 +978,7 @@
     lastModifiedTime: 'Jan 1, 1990, 11:59 PM',
     nameText: 'photosTwo',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   });
 
   const photosT = new TestEntryInfo({
@@ -989,7 +989,7 @@
     lastModifiedTime: 'Jan 1, 1993, 11:59 PM',
     nameText: 'photosT',
     sizeText: '51 bytes',
-    typeText: 'Plain text'
+    typeText: 'Plain text',
   });
 
   // Open Files app on local Downloads.
diff --git a/ui/file_manager/integration_tests/file_manager/drive_specific.js b/ui/file_manager/integration_tests/file_manager/drive_specific.js
index a4205aa..e373ab43 100644
--- a/ui/file_manager/integration_tests/file_manager/drive_specific.js
+++ b/ui/file_manager/integration_tests/file_manager/drive_specific.js
@@ -434,7 +434,7 @@
     name: 'clickNotificationButton',
     extensionId: FILE_MANAGER_EXTENSIONS_ID,
     notificationId: 'disabled-mobile-sync',
-    index: 0
+    index: 0,
   });
   await repeatUntil(async () => {
     const preferences =
@@ -834,7 +834,9 @@
   await remoteCall.isolateBannerForTesting(appId, 'drive-welcome-banner');
   const driveWelcomeBannerQuery = '#banners > drive-welcome-banner';
   const driveWelcomeBannerDismissButtonQuery = [
-    '#banners > drive-welcome-banner', 'educational-banner', '#dismiss-button'
+    '#banners > drive-welcome-banner',
+    'educational-banner',
+    '#dismiss-button',
   ];
 
   // Open the Drive volume in the files-list.
@@ -958,7 +960,7 @@
     name: 'clickNotificationButton',
     extensionId: FILE_MANAGER_EXTENSIONS_ID,
     notificationId: 'enable-docs-offline',
-    index: 1
+    index: 1,
   });
 
   // Check: the last dialog result should be 1 (accept).
@@ -975,7 +977,7 @@
     name: 'clickNotificationButton',
     extensionId: FILE_MANAGER_EXTENSIONS_ID,
     notificationId: 'enable-docs-offline',
-    index: 0
+    index: 0,
   });
 
   // Check: the last dialog result should be 2 (reject).
diff --git a/ui/file_manager/integration_tests/file_manager/files_tooltip.js b/ui/file_manager/integration_tests/file_manager/files_tooltip.js
index f7a5687..76f92c7 100644
--- a/ui/file_manager/integration_tests/file_manager/files_tooltip.js
+++ b/ui/file_manager/integration_tests/file_manager/files_tooltip.js
@@ -30,7 +30,7 @@
       'Some activities are not supported.',
   'CANCEL_SELECTION_BUTTON_LABEL': 'Cancel selection',
   'CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL': 'Switch to thumbnail view',
-  'CHANGE_TO_LISTVIEW_BUTTON_LABEL': 'Switch to list view'
+  'CHANGE_TO_LISTVIEW_BUTTON_LABEL': 'Switch to list view',
 };
 
 /**
diff --git a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
index 30e6659..3286258 100644
--- a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
+++ b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
@@ -38,40 +38,41 @@
 const DIRECTORY = {
   Drive: {
     contents: [
-      ENTRIES.directoryA.getExpectedRow(), ENTRIES.directoryD.getExpectedRow()
+      ENTRIES.directoryA.getExpectedRow(),
+      ENTRIES.directoryD.getExpectedRow(),
     ],
     name: 'Drive',
     navItem: '.tree-item[entry-label="My Drive"]',
-    treeItem: TREEITEM_DRIVE
+    treeItem: TREEITEM_DRIVE,
   },
   A: {
     contents: [ENTRIES.directoryB.getExpectedRow()],
     name: 'A',
     navItem: '.tree-item[dir-type="ShortcutItem"][entry-label="A"]',
-    treeItem: TREEITEM_A
+    treeItem: TREEITEM_A,
   },
   B: {
     contents: [ENTRIES.directoryC.getExpectedRow()],
     name: 'B',
-    treeItem: TREEITEM_B
+    treeItem: TREEITEM_B,
   },
   C: {
     contents: [],
     name: 'C',
     navItem: '.tree-item[dir-type="ShortcutItem"][entry-label="C"]',
-    treeItem: TREEITEM_C
+    treeItem: TREEITEM_C,
   },
   D: {
     contents: [ENTRIES.directoryE.getExpectedRow()],
     name: 'D',
     navItem: '.tree-item[dir-type="ShortcutItem"][entry-label="D"]',
-    treeItem: TREEITEM_D
+    treeItem: TREEITEM_D,
   },
   E: {
     contents: [ENTRIES.directoryF.getExpectedRow()],
     name: 'E',
-    treeItem: TREEITEM_E
-  }
+    treeItem: TREEITEM_E,
+  },
 };
 
 /**
diff --git a/ui/file_manager/integration_tests/file_manager/format_dialog.js b/ui/file_manager/integration_tests/file_manager/format_dialog.js
index 3297012..d88f950 100644
--- a/ui/file_manager/integration_tests/file_manager/format_dialog.js
+++ b/ui/file_manager/integration_tests/file_manager/format_dialog.js
@@ -104,7 +104,8 @@
 
   // Check the correct size is displayed.
   const warning = await remoteCall.waitForElement(appId, [
-    'files-format-dialog', '#warning-container:not([hidden]) #warning-message'
+    'files-format-dialog',
+    '#warning-container:not([hidden]) #warning-message',
   ]);
   chrome.test.assertEq(
       '51 bytes of files will be deleted', warning.text.trim());
diff --git a/ui/file_manager/integration_tests/file_manager/guest_os.js b/ui/file_manager/integration_tests/file_manager/guest_os.js
index b3eabaf..d223f93 100644
--- a/ui/file_manager/integration_tests/file_manager/guest_os.js
+++ b/ui/file_manager/integration_tests/file_manager/guest_os.js
@@ -104,7 +104,7 @@
     name: 'registerMountableGuest',
     displayName: guestName,
     canMount: true,
-    vmType: 'bruschetta'
+    vmType: 'bruschetta',
   });
   // Open the files app.
   const appId =
diff --git a/ui/file_manager/integration_tests/file_manager/holding_space.js b/ui/file_manager/integration_tests/file_manager/holding_space.js
index e0f373c..fed9c05 100644
--- a/ui/file_manager/integration_tests/file_manager/holding_space.js
+++ b/ui/file_manager/integration_tests/file_manager/holding_space.js
@@ -21,8 +21,9 @@
   const holdingSpaceBannerShown =
       '#banners > holding-space-welcome-banner:not([hidden])';
   const holdingSpaceBannerDismissButton = [
-    '#banners > holding-space-welcome-banner', 'educational-banner',
-    '#dismiss-button'
+    '#banners > holding-space-welcome-banner',
+    'educational-banner',
+    '#dismiss-button',
   ];
   const holdingSpaceBannerHidden =
       '#banners > holding-space-welcome-banner[hidden]';
@@ -93,11 +94,11 @@
       appId, 'holding-space-welcome-banner');
   const text = [
     '#banners > holding-space-welcome-banner:not([hidden])',
-    'educational-banner > span[slot="subtitle"].tablet-mode-disabled'
+    'educational-banner > span[slot="subtitle"].tablet-mode-disabled',
   ];
   const textInTabletMode = [
     '#banners > holding-space-welcome-banner:not([hidden])',
-    'educational-banner > span[slot="subtitle"].tablet-mode-enabled'
+    'educational-banner > span[slot="subtitle"].tablet-mode-enabled',
   ];
   const expectedTextDisplayValue = 'inline';
   // Check: `text` should be displayed but `textInTabletMode` should not.
diff --git a/ui/file_manager/integration_tests/file_manager/keyboard_operations.js b/ui/file_manager/integration_tests/file_manager/keyboard_operations.js
index 63a41d44..7221ae0 100644
--- a/ui/file_manager/integration_tests/file_manager/keyboard_operations.js
+++ b/ui/file_manager/integration_tests/file_manager/keyboard_operations.js
@@ -345,8 +345,9 @@
 
   // Wait for partitions to show up.
   const expectedRows = [
-    ['partition-1', '--', 'ntfs', ''], ['partition-2', '--', 'ext4', ''],
-    ['partition-3', '--', 'vfat', '']
+    ['partition-1', '--', 'ntfs', ''],
+    ['partition-2', '--', 'ext4', ''],
+    ['partition-3', '--', 'vfat', ''],
   ];
   await remoteCall.waitForFiles(
       appId, expectedRows, {ignoreLastModifiedTime: true});
diff --git a/ui/file_manager/integration_tests/file_manager/metadata.js b/ui/file_manager/integration_tests/file_manager/metadata.js
index 93304da7..d54d86a 100644
--- a/ui/file_manager/integration_tests/file_manager/metadata.js
+++ b/ui/file_manager/integration_tests/file_manager/metadata.js
@@ -45,7 +45,7 @@
     type: EntryType.DIRECTORY,
     lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   });
 }
 
diff --git a/ui/file_manager/integration_tests/file_manager/my_files.js b/ui/file_manager/integration_tests/file_manager/my_files.js
index af6d24ab..72625a9 100644
--- a/ui/file_manager/integration_tests/file_manager/my_files.js
+++ b/ui/file_manager/integration_tests/file_manager/my_files.js
@@ -147,7 +147,7 @@
     lastModifiedTime: 'Sep 4, 1998, 12:34 PM',
     nameText: '.hidden-folder',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   });
 
   // Add a hidden folder.
diff --git a/ui/file_manager/integration_tests/file_manager/office.js b/ui/file_manager/integration_tests/file_manager/office.js
index 66875f6..ec461673 100644
--- a/ui/file_manager/integration_tests/file_manager/office.js
+++ b/ui/file_manager/integration_tests/file_manager/office.js
@@ -147,7 +147,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [ENTRIES.smallDocxHosted.targetPath],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   let histogramCount = await getHistogramCount(
diff --git a/ui/file_manager/integration_tests/file_manager/open_sniffed_files.js b/ui/file_manager/integration_tests/file_manager/open_sniffed_files.js
index 16973b8a..899bea5 100644
--- a/ui/file_manager/integration_tests/file_manager/open_sniffed_files.js
+++ b/ui/file_manager/integration_tests/file_manager/open_sniffed_files.js
@@ -20,7 +20,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [entry.targetPath],
-    openType: 'launch'
+    openType: 'launch',
   });
   // Open Files.App on |path|, add imgpdf to Downloads and Drive.
   const appId = await setupAndWaitUntilReady(path, [entry], [entry]);
diff --git a/ui/file_manager/integration_tests/file_manager/providers.js b/ui/file_manager/integration_tests/file_manager/providers.js
index 9c9e1c48..e0715c34 100644
--- a/ui/file_manager/integration_tests/file_manager/providers.js
+++ b/ui/file_manager/integration_tests/file_manager/providers.js
@@ -277,7 +277,7 @@
   // Setup the FSP and wait for the volume to appear in the directory tree.
   await sendTestMessage({
     name: 'launchProviderExtension',
-    manifest: 'manifest_source_device.json'
+    manifest: 'manifest_source_device.json',
   });
   await confirmVolume(appId, true /* ejectExpected */);
 
diff --git a/ui/file_manager/integration_tests/file_manager/recents.js b/ui/file_manager/integration_tests/file_manager/recents.js
index 1a327c7..48c6653 100644
--- a/ui/file_manager/integration_tests/file_manager/recents.js
+++ b/ui/file_manager/integration_tests/file_manager/recents.js
@@ -329,6 +329,31 @@
 }
 
 /**
+ * Wait for the empty folder element to show and assert the content to
+ * match the expected message.
+ *
+ * @param {string} appId Files app windowId.
+ * @param {string} expectedMessage The expected empty folder message
+ */
+async function waitForEmptyFolderMessage(appId, expectedMessage) {
+  const caller = getCaller();
+  // Use repeatUntil() here because when we switch between different filters,
+  // the message changes but the element itself will always show there.
+  await repeatUntil(async () => {
+    const emptyMessage = await remoteCall.waitForElement(
+        appId, '#empty-folder:not(.hidden) > .label');
+    if (emptyMessage.text === expectedMessage) {
+      return;
+    }
+
+    return pending(
+        caller,
+        `Expected empty folder message: "${expectedMessage}", got "${
+            emptyMessage.text}"`);
+  });
+}
+
+/**
  * Tests that file entries populated in the Downloads folder recently will be
  * displayed in Recent folder.
  */
@@ -381,8 +406,9 @@
   // Verifies file list in Recents. Audio files from Play Files folder are
   // not supported in Recents.
   await verifyRecents(appId, [
-    RECENT_MODIFIED_ANDROID_DOCUMENT, RECENT_MODIFIED_ANDROID_IMAGE,
-    RECENT_MODIFIED_ANDROID_VIDEO
+    RECENT_MODIFIED_ANDROID_DOCUMENT,
+    RECENT_MODIFIED_ANDROID_IMAGE,
+    RECENT_MODIFIED_ANDROID_VIDEO,
   ]);
 };
 
@@ -436,8 +462,9 @@
       [ENTRIES.desktop, ENTRIES.world, ENTRIES.testDocument]);
 
   await verifyRecents(appId, RECENT_ENTRY_SET.concat([
-    RECENT_MODIFIED_ANDROID_DOCUMENT, RECENT_MODIFIED_ANDROID_IMAGE,
-    RECENT_MODIFIED_ANDROID_VIDEO
+    RECENT_MODIFIED_ANDROID_DOCUMENT,
+    RECENT_MODIFIED_ANDROID_IMAGE,
+    RECENT_MODIFIED_ANDROID_VIDEO,
   ]));
 };
 
@@ -609,8 +636,9 @@
       BASIC_LOCAL_ENTRY_SET.concat([RECENTLY_MODIFIED_VIDEO]),
       BASIC_DRIVE_ENTRY_SET.concat([RECENTLY_MODIFIED_VIDEO]));
   await verifyRecentVideos(appId, [
-    RECENTLY_MODIFIED_VIDEO, RECENTLY_MODIFIED_VIDEO,
-    RECENT_MODIFIED_ANDROID_VIDEO
+    RECENTLY_MODIFIED_VIDEO,
+    RECENTLY_MODIFIED_VIDEO,
+    RECENT_MODIFIED_ANDROID_VIDEO,
   ]);
 };
 
@@ -651,8 +679,9 @@
       RootPath.DOWNLOADS, [RECENTLY_MODIFIED_DOCUMENT],
       [RECENTLY_MODIFIED_DOCUMENT]);
   await verifyRecentDocuments(appId, [
-    RECENTLY_MODIFIED_DOCUMENT, RECENTLY_MODIFIED_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_DOCUMENT
+    RECENTLY_MODIFIED_DOCUMENT,
+    RECENTLY_MODIFIED_DOCUMENT,
+    RECENT_MODIFIED_ANDROID_DOCUMENT,
   ]);
 };
 
@@ -733,24 +762,30 @@
       RootPath.DOWNLOADS, [ENTRIES.beautiful], [ENTRIES.desktop]);
   await navigateToRecent(appId);
   const files = TestEntryInfo.getExpectedRows([
-    ENTRIES.beautiful, ENTRIES.desktop, RECENT_MODIFIED_ANDROID_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_IMAGE, RECENT_MODIFIED_ANDROID_VIDEO
+    ENTRIES.beautiful,
+    ENTRIES.desktop,
+    RECENT_MODIFIED_ANDROID_DOCUMENT,
+    RECENT_MODIFIED_ANDROID_IMAGE,
+    RECENT_MODIFIED_ANDROID_VIDEO,
   ]);
   await remoteCall.waitForFiles(appId, files);
 
   // Delete a file originated from Downloads.
   await deleteFile(appId, ENTRIES.beautiful.nameText);
   const files1 = TestEntryInfo.getExpectedRows([
-    ENTRIES.desktop, RECENT_MODIFIED_ANDROID_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_IMAGE, RECENT_MODIFIED_ANDROID_VIDEO
+    ENTRIES.desktop,
+    RECENT_MODIFIED_ANDROID_DOCUMENT,
+    RECENT_MODIFIED_ANDROID_IMAGE,
+    RECENT_MODIFIED_ANDROID_VIDEO,
   ]);
   await remoteCall.waitForFiles(appId, files1);
 
   // Delete a file originated from Drive.
   await deleteFile(appId, ENTRIES.desktop.nameText);
   const files2 = TestEntryInfo.getExpectedRows([
-    RECENT_MODIFIED_ANDROID_DOCUMENT, RECENT_MODIFIED_ANDROID_IMAGE,
-    RECENT_MODIFIED_ANDROID_VIDEO
+    RECENT_MODIFIED_ANDROID_DOCUMENT,
+    RECENT_MODIFIED_ANDROID_IMAGE,
+    RECENT_MODIFIED_ANDROID_VIDEO,
   ]);
   await remoteCall.waitForFiles(appId, files2);
 
@@ -771,8 +806,11 @@
       RootPath.DOWNLOADS, [ENTRIES.beautiful], [ENTRIES.desktop]);
   await navigateToRecent(appId);
   const files = TestEntryInfo.getExpectedRows([
-    ENTRIES.beautiful, ENTRIES.desktop, RECENT_MODIFIED_ANDROID_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_IMAGE, RECENT_MODIFIED_ANDROID_VIDEO
+    ENTRIES.beautiful,
+    ENTRIES.desktop,
+    RECENT_MODIFIED_ANDROID_DOCUMENT,
+    RECENT_MODIFIED_ANDROID_IMAGE,
+    RECENT_MODIFIED_ANDROID_VIDEO,
   ]);
   await remoteCall.waitForFiles(appId, files);
 
@@ -849,8 +887,10 @@
       await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
   await navigateToRecent(appId);
   const files = TestEntryInfo.getExpectedRows([
-    ENTRIES.beautiful, RECENT_MODIFIED_ANDROID_DOCUMENT,
-    RECENT_MODIFIED_ANDROID_IMAGE, RECENT_MODIFIED_ANDROID_VIDEO
+    ENTRIES.beautiful,
+    RECENT_MODIFIED_ANDROID_DOCUMENT,
+    RECENT_MODIFIED_ANDROID_IMAGE,
+    RECENT_MODIFIED_ANDROID_VIDEO,
   ]);
   await remoteCall.waitForFiles(appId, files);
 
@@ -933,8 +973,9 @@
   const appId = await setupAndWaitUntilReady(
       RootPath.DOWNLOADS, [ENTRIES.directoryA], []);
   const files = TestEntryInfo.getExpectedRows([
-    RECENT_MODIFIED_ANDROID_DOCUMENT, RECENT_MODIFIED_ANDROID_IMAGE,
-    RECENT_MODIFIED_ANDROID_VIDEO
+    RECENT_MODIFIED_ANDROID_DOCUMENT,
+    RECENT_MODIFIED_ANDROID_IMAGE,
+    RECENT_MODIFIED_ANDROID_VIDEO,
   ]);
   const newFolderBreadcrumb =
       `/My files/Downloads/${ENTRIES.directoryA.nameText}`;
@@ -1004,3 +1045,44 @@
   chrome.test.assertEq(
       yesterdayFile.nameText, gridItems[1].attributes['file-name']);
 };
+
+/**
+ * Tests message will show in Recents for empty folder.
+ */
+testcase.recentsEmptyFolderMessage = async () => {
+  const appId = await setupAndWaitUntilReady(
+      RootPath.DOWNLOADS, [ENTRIES.directoryA], []);
+  await navigateToRecent(appId);
+  // All filter is on by default.
+  await waitForEmptyFolderMessage(appId, 'Your recent files will appear here');
+  // Activates to audio filter.
+  await remoteCall.waitAndClickElement(appId, [`[file-type-filter="audio"]`]);
+  await waitForEmptyFolderMessage(
+      appId, 'Your recent audio files will appear here');
+  // Activates to documents filter.
+  await remoteCall.waitAndClickElement(
+      appId, [`[file-type-filter="document"]`]);
+  await waitForEmptyFolderMessage(
+      appId, 'Your recent documents will appear here');
+  // Activates to images filter.
+  await remoteCall.waitAndClickElement(appId, [`[file-type-filter="image"]`]);
+  await waitForEmptyFolderMessage(appId, 'Your recent images will appear here');
+  // Activates to videos filter.
+  await remoteCall.waitAndClickElement(appId, [`[file-type-filter="video"]`]);
+  await waitForEmptyFolderMessage(appId, 'Your recent videos will appear here');
+};
+
+
+/**
+ * Tests message will show in Recents after the last file is
+ * deleted.
+ */
+testcase.recentsEmptyFolderMessageAfterDeletion = async () => {
+  const appId =
+      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
+  await navigateToRecent(appId);
+  const files = TestEntryInfo.getExpectedRows([ENTRIES.beautiful]);
+  await remoteCall.waitForFiles(appId, files);
+  await deleteFile(appId, ENTRIES.beautiful.nameText);
+  await waitForEmptyFolderMessage(appId, 'Your recent files will appear here');
+};
diff --git a/ui/file_manager/integration_tests/file_manager/tab_index.js b/ui/file_manager/integration_tests/file_manager/tab_index.js
index 3178941..a46f426 100644
--- a/ui/file_manager/integration_tests/file_manager/tab_index.js
+++ b/ui/file_manager/integration_tests/file_manager/tab_index.js
@@ -201,7 +201,7 @@
     expectedTabOrder) {
   await Promise.all([
     addEntries(['local'], BASIC_LOCAL_ENTRY_SET),
-    addEntries(['drive'], BASIC_DRIVE_ENTRY_SET)
+    addEntries(['drive'], BASIC_DRIVE_ENTRY_SET),
   ]);
 
   const selectAndCheckAndClose = async (appId) => {
@@ -243,9 +243,16 @@
  */
 testcase.tabindexOpenDialogDownloads = async () => {
   const tabindexIds = [
-    'cancel-button', 'ok-button', 'directory-tree',
-    /* first breadcrumb */ 'first', 'search-button', 'view-button',
-    'sort-button', 'gear-button', 'dismiss-button', 'file-list'
+    'cancel-button',
+    'ok-button',
+    'directory-tree',
+    /* first breadcrumb */ 'first',
+    'search-button',
+    'view-button',
+    'sort-button',
+    'gear-button',
+    'dismiss-button',
+    'file-list',
   ];
   return tabindexFocus(
       {type: 'openFile'}, 'downloads', BASIC_LOCAL_ENTRY_SET, async (appId) => {
@@ -261,9 +268,16 @@
  */
 testcase.tabindexOpenDialogDrive = async () => {
   const tabindexIds = [
-    'cancel-button', 'ok-button', 'search-button', 'view-button', 'sort-button',
-    'gear-button', 'drive-learn-more-button', 'dismiss-button',
-    'directory-tree', 'file-list'
+    'cancel-button',
+    'ok-button',
+    'search-button',
+    'view-button',
+    'sort-button',
+    'gear-button',
+    'drive-learn-more-button',
+    'dismiss-button',
+    'directory-tree',
+    'file-list',
   ];
   return tabindexFocus(
       {type: 'openFile'}, 'drive', BASIC_DRIVE_ENTRY_SET, async (appId) => {
@@ -279,14 +293,21 @@
   return tabindexFocus(
       {
         type: 'saveFile',
-        suggestedName: 'hoge.txt'  // Prevent showing a override prompt
+        suggestedName: 'hoge.txt',  // Prevent showing a override prompt
       },
       'downloads', BASIC_LOCAL_ENTRY_SET, null, ['#ok-button:not([disabled])'],
       [
-        'cancel-button', 'ok-button', 'directory-tree',
-        /* first breadcrumb */ 'first', 'search-button', 'view-button',
-        'sort-button', 'gear-button', 'file-list', 'new-folder-button',
-        'filename-input-textbox'
+        'cancel-button',
+        'ok-button',
+        'directory-tree',
+        /* first breadcrumb */ 'first',
+        'search-button',
+        'view-button',
+        'sort-button',
+        'gear-button',
+        'file-list',
+        'new-folder-button',
+        'filename-input-textbox',
       ]);
 };
 
@@ -298,11 +319,18 @@
   return tabindexFocus(
       {
         type: 'saveFile',
-        suggestedName: 'hoge.txt'  // Prevent showing a override prompt
+        suggestedName: 'hoge.txt',  // Prevent showing a override prompt
       },
       'drive', BASIC_DRIVE_ENTRY_SET, null, ['#ok-button:not([disabled])'], [
-        'cancel-button', 'ok-button', 'directory-tree', 'search-button',
-        'view-button', 'sort-button', 'gear-button', 'file-list',
-        'new-folder-button', 'filename-input-textbox'
+        'cancel-button',
+        'ok-button',
+        'directory-tree',
+        'search-button',
+        'view-button',
+        'sort-button',
+        'gear-button',
+        'file-list',
+        'new-folder-button',
+        'filename-input-textbox',
       ]);
 };
diff --git a/ui/file_manager/integration_tests/file_manager/tasks.js b/ui/file_manager/integration_tests/file_manager/tasks.js
index 31f342c..046c1411 100644
--- a/ui/file_manager/integration_tests/file_manager/tasks.js
+++ b/ui/file_manager/integration_tests/file_manager/tasks.js
@@ -45,7 +45,7 @@
   new FakeTask(
       false,
       {appId: 'dummytaskid-2', taskType: 'fake-type', actionId: 'open-with'},
-      'DummyTask2')
+      'DummyTask2'),
 ];
 
 /**
@@ -58,7 +58,7 @@
   new FakeTask(true, {
     appId: FILE_MANAGER_EXTENSIONS_ID,
     taskType: 'file',
-    actionId: 'view-in-browser'
+    actionId: 'view-in-browser',
   }),
 ];
 
@@ -72,7 +72,7 @@
   new FakeTask(true, {
     appId: FILE_MANAGER_EXTENSIONS_ID,
     taskType: 'file',
-    actionId: 'view-as-pdf'
+    actionId: 'view-as-pdf',
   }),
 ];
 
@@ -88,7 +88,7 @@
       'DummyTask1'),
   new FakeTask(
       false, {appId: 'dummytaskid-2', taskType: 'drive', actionId: 'open-with'},
-      'DummyTask2')
+      'DummyTask2'),
 ];
 
 /**
@@ -173,12 +173,14 @@
   chrome.test.assertTrue(
       await remoteCall.callRemoteTestUtil('fakeEvent', appId, [
         '#default-task-dialog #default-tasks-list li:nth-of-type(2)',
-        'mousedown', {bubbles: true, button: 0}
+        'mousedown',
+        {bubbles: true, button: 0},
       ]));
   chrome.test.assertTrue(
       await remoteCall.callRemoteTestUtil('fakeEvent', appId, [
-        '#default-task-dialog #default-tasks-list li:nth-of-type(2)', 'click',
-        {bubbles: true}
+        '#default-task-dialog #default-tasks-list li:nth-of-type(2)',
+        'click',
+        {bubbles: true},
       ]));
 
   // Wait for the dialog hidden, and the task is executed.
@@ -314,7 +316,7 @@
   await executeDefaultTask(appId, {
     appId: FILE_MANAGER_EXTENSIONS_ID,
     taskType: 'file',
-    actionId: 'view-in-browser'
+    actionId: 'view-in-browser',
   });
 };
 
@@ -331,7 +333,7 @@
     new FakeTask(
         false,
         {appId: 'dummytaskid-3', taskType: 'fake-type', actionId: 'open-with'},
-        'DummyTask3', true /* isGenericFileHandler */)
+        'DummyTask3', true /* isGenericFileHandler */),
   ];
 
   const appId = await setupTaskTest(RootPath.DOWNLOADS, tasks);
diff --git a/ui/file_manager/integration_tests/file_manager/test_data.js b/ui/file_manager/integration_tests/file_manager/test_data.js
index 2e0fc15..d98c2a1 100644
--- a/ui/file_manager/integration_tests/file_manager/test_data.js
+++ b/ui/file_manager/integration_tests/file_manager/test_data.js
@@ -135,8 +135,12 @@
  * @const
  */
 export const COMPLEX_DRIVE_ENTRY_SET = [
-  ENTRIES.hello, ENTRIES.photos, ENTRIES.readOnlyFolder,
-  ENTRIES.readOnlyDocument, ENTRIES.readOnlyStrictDocument, ENTRIES.readOnlyFile
+  ENTRIES.hello,
+  ENTRIES.photos,
+  ENTRIES.readOnlyFolder,
+  ENTRIES.readOnlyDocument,
+  ENTRIES.readOnlyStrictDocument,
+  ENTRIES.readOnlyFile,
 ];
 
 /**
@@ -147,8 +151,12 @@
  * @const
  */
 export const COMPLEX_DOCUMENTS_PROVIDER_ENTRY_SET = [
-  ENTRIES.hello, ENTRIES.photos, ENTRIES.readOnlyFolder, ENTRIES.readOnlyFile,
-  ENTRIES.deletableFile, ENTRIES.renamableFile
+  ENTRIES.hello,
+  ENTRIES.photos,
+  ENTRIES.readOnlyFolder,
+  ENTRIES.readOnlyFile,
+  ENTRIES.deletableFile,
+  ENTRIES.renamableFile,
 ];
 
 /**
diff --git a/ui/file_manager/integration_tests/file_manager/toolbar.js b/ui/file_manager/integration_tests/file_manager/toolbar.js
index 432a243f..39293807 100644
--- a/ui/file_manager/integration_tests/file_manager/toolbar.js
+++ b/ui/file_manager/integration_tests/file_manager/toolbar.js
@@ -318,7 +318,7 @@
   // Check invoke sharesheet is called.
   chrome.test.assertEq(
       1, await remoteCall.callRemoteTestUtil('staticFakeCounter', appId, [
-        'chrome.fileManagerPrivate.invokeSharesheet'
+        'chrome.fileManagerPrivate.invokeSharesheet',
       ]));
 
   // Remove fakes.
@@ -369,7 +369,7 @@
   // Check invoke sharesheet is called.
   chrome.test.assertEq(
       1, await remoteCall.callRemoteTestUtil('staticFakeCounter', appId, [
-        'chrome.fileManagerPrivate.invokeSharesheet'
+        'chrome.fileManagerPrivate.invokeSharesheet',
       ]));
 
   // Remove fakes.
diff --git a/ui/file_manager/integration_tests/file_manager/transfer.js b/ui/file_manager/integration_tests/file_manager/transfer.js
index 7fd6b23..2e6f8c5 100644
--- a/ui/file_manager/integration_tests/file_manager/transfer.js
+++ b/ui/file_manager/integration_tests/file_manager/transfer.js
@@ -245,25 +245,25 @@
     volumeName: 'drive',
     initialEntries: BASIC_DRIVE_ENTRY_SET.concat([
       ENTRIES.sharedDirectory,
-    ])
+    ]),
   }),
 
   driveWithTeamDriveEntries: new TransferLocationInfo({
     breadcrumbsPath: '/My Drive',
     volumeName: 'drive',
-    initialEntries: SHARED_DRIVE_ENTRY_SET
+    initialEntries: SHARED_DRIVE_ENTRY_SET,
   }),
 
   driveSharedDirectory: new TransferLocationInfo({
     breadcrumbsPath: '/My Drive/Shared',
     volumeName: 'drive',
-    initialEntries: [ENTRIES.sharedDirectoryFile]
+    initialEntries: [ENTRIES.sharedDirectoryFile],
   }),
 
   downloads: new TransferLocationInfo({
     breadcrumbsPath: '/My files/Downloads',
     volumeName: 'downloads',
-    initialEntries: BASIC_LOCAL_ENTRY_SET
+    initialEntries: BASIC_LOCAL_ENTRY_SET,
   }),
 
   sharedWithMe: new TransferLocationInfo({
@@ -272,27 +272,27 @@
     initialEntries: SHARED_WITH_ME_ENTRY_SET.concat([
       ENTRIES.sharedDirectory,
       ENTRIES.sharedDirectoryFile,
-    ])
+    ]),
   }),
 
   driveOffline: new TransferLocationInfo({
     breadcrumbsPath: '/Offline',
     volumeName: 'drive_offline',
-    initialEntries: OFFLINE_ENTRY_SET
+    initialEntries: OFFLINE_ENTRY_SET,
   }),
 
   driveTeamDriveA: new TransferLocationInfo({
     breadcrumbsPath: '/Shared drives/Team Drive A',
     volumeName: 'Team Drive A',
     isTeamDrive: true,
-    initialEntries: SHARED_DRIVE_ENTRY_SET
+    initialEntries: SHARED_DRIVE_ENTRY_SET,
   }),
 
   driveTeamDriveB: new TransferLocationInfo({
     breadcrumbsPath: '/Shared drives/Team Drive B',
     volumeName: 'Team Drive B',
     isTeamDrive: true,
-    initialEntries: SHARED_DRIVE_ENTRY_SET
+    initialEntries: SHARED_DRIVE_ENTRY_SET,
   }),
 
   my_files: new TransferLocationInfo({
@@ -305,7 +305,7 @@
         nameText: 'Play files',
         lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
         sizeText: '--',
-        typeText: 'Folder'
+        typeText: 'Folder',
       }),
       new TestEntryInfo({
         type: EntryType.DIRECTORY,
@@ -313,7 +313,7 @@
         nameText: 'Downloads',
         lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
         sizeText: '--',
-        typeText: 'Folder'
+        typeText: 'Folder',
       }),
       new TestEntryInfo({
         type: EntryType.DIRECTORY,
@@ -321,7 +321,7 @@
         nameText: 'Linux files',
         lastModifiedTime: '...',
         sizeText: '--',
-        typeText: 'Folder'
+        typeText: 'Folder',
       }),
       new TestEntryInfo({
         type: EntryType.DIRECTORY,
@@ -329,9 +329,9 @@
         nameText: 'Trash',
         lastModifiedTime: '...',
         sizeText: '--',
-        typeText: 'Folder'
+        typeText: 'Folder',
       }),
-    ]
+    ],
   }),
 };
 Object.freeze(TRANSFER_LOCATIONS);
@@ -1210,7 +1210,8 @@
   await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
     'item-id-1',
     /* ProgressItemType.FORMAT */ 'format',
-    /* ProgressItemState.PROGRESSING */ 'progressing', 'Formatting'
+    /* ProgressItemState.PROGRESSING */ 'progressing',
+    'Formatting',
   ]);
 
   // Check the progress panel is open.
@@ -1222,8 +1223,10 @@
 
   // Show a |format| error panel.
   await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
-    'item-id-2', /* ProgressItemType.FORMAT */ 'format',
-    /* ProgressItemState.ERROR */ 'error', 'Failed'
+    'item-id-2',
+    /* ProgressItemType.FORMAT */ 'format',
+    /* ProgressItemState.ERROR */ 'error',
+    'Failed',
   ]);
 
   // Check the progress panel is open.
@@ -1243,8 +1246,10 @@
 
   // Show a |format| error in feedback panel.
   await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
-    'item-id', /* ProgressItemType.FORMAT */ 'format',
-    /* ProgressItemState.ERROR */ 'error', 'Failed'
+    'item-id',
+    /* ProgressItemType.FORMAT */ 'format',
+    /* ProgressItemState.ERROR */ 'error',
+    'Failed',
   ]);
 
   // Check the error panel is open.
@@ -1253,8 +1258,10 @@
 
   // Dispatch another |format| feedback panel with the same id and panel type.
   await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
-    'item-id', /* ProgressItemType.FORMAT */ 'format',
-    /* ProgressItemState.ERROR */ 'error', 'Failed new message'
+    'item-id',
+    /* ProgressItemType.FORMAT */ 'format',
+    /* ProgressItemState.ERROR */ 'error',
+    'Failed new message',
   ]);
 
   // Check the progress panel is open.
@@ -1273,10 +1280,11 @@
 
   // Show a |copy| progress in feedback panel.
   await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
-    'item-id', /* ProgressItemType.COPY */ 'copy',
+    'item-id',
+    /* ProgressItemType.COPY */ 'copy',
     /* ProgressItemState.PROGRESSING */ 'progressing',
     'Copying File1.txt to Downloads',
-    /* remainingTime*/ 0
+    /* remainingTime*/ 0,
   ]);
 
   // Check the error panel is open.
diff --git a/ui/file_manager/integration_tests/file_manager/zip_files.js b/ui/file_manager/integration_tests/file_manager/zip_files.js
index 42319d1..3dd2cb1f9 100644
--- a/ui/file_manager/integration_tests/file_manager/zip_files.js
+++ b/ui/file_manager/integration_tests/file_manager/zip_files.js
@@ -27,8 +27,10 @@
 function getUnzippedFileListRowEntries() {
   return [
     [
-      'SUCCESSFULLY_PERFORMED_FAKE_MOUNT.txt', '21 bytes', 'Plain text',
-      'Dec 31, 1980, 12:00 AM'
+      'SUCCESSFULLY_PERFORMED_FAKE_MOUNT.txt',
+      '21 bytes',
+      'Plain text',
+      'Dec 31, 1980, 12:00 AM',
     ],
   ];
 }
@@ -40,7 +42,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [ENTRIES.zipArchive.targetPath],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   // Open Files app on Downloads containing a zip file.
@@ -71,7 +73,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [ENTRIES.zipArchive.targetPath],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   // Open Files app on Downloads containing a zip file.
@@ -94,7 +96,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [ENTRIES.zipArchive.targetPath],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   // Open Files app on Drive containing a zip file.
@@ -125,7 +127,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [ENTRIES.zipArchive.targetPath],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   const USB_VOLUME_QUERY = '#directory-tree [volume-type-icon="removable"]';
@@ -175,7 +177,7 @@
 function getZipSelectionFileListRowEntries() {
   return [
     ['photos', '--', 'Folder', 'Jan 1, 1980, 11:59 PM'],
-    ['photos.zip', '134 bytes', 'ZIP archive', 'Oct 21, 1983, 11:55 AM']
+    ['photos.zip', '134 bytes', 'ZIP archive', 'Oct 21, 1983, 11:55 AM'],
   ];
 }
 
@@ -317,7 +319,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [targetDirectoryName],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   // Open files app.
@@ -381,14 +383,14 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [targetDirectoryName],
-    openType: 'launch'
+    openType: 'launch',
   });
   entry = entries[3];  // ENTRIES.zipSJISArchive.
   targetDirectoryName = entry.nameText.split('.')[0];
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [targetDirectoryName],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   // Open files app.
@@ -565,7 +567,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [targetDirectoryName],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   // Open files app.
@@ -694,7 +696,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [targetDirectoryName],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   // Open files app.
@@ -826,7 +828,7 @@
   await sendTestMessage({
     name: 'expectFileTask',
     fileNames: [targetDirectoryName],
-    openType: 'launch'
+    openType: 'launch',
   });
 
   // Open files app.
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index c2878c2f..1a457e2 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -278,7 +278,7 @@
   DIRECTORY: 'directory',
   LINK: 'link',
   SHARED_DRIVE: 'team_drive',
-  COMPUTER: 'Computer'
+  COMPUTER: 'Computer',
 };
 Object.freeze(EntryType);
 
@@ -508,7 +508,7 @@
     lastModifiedTime: 'Sep 4, 1998, 12:34 PM',
     nameText: 'hello.txt',
     sizeText: '51 bytes',
-    typeText: 'Plain text'
+    typeText: 'Plain text',
   }),
 
   world: new TestEntryInfo({
@@ -520,7 +520,7 @@
     lastModifiedTime: 'Jul 4, 2012, 10:35 AM',
     nameText: 'world.ogv',
     sizeText: '59 KB',
-    typeText: 'OGG video'
+    typeText: 'OGG video',
   }),
 
   webm: new TestEntryInfo({
@@ -531,7 +531,7 @@
     lastModifiedTime: 'Jul 4, 2012, 10:35 AM',
     nameText: 'world.webm',
     sizeText: '17 KB',
-    typeText: 'WebM video'
+    typeText: 'WebM video',
   }),
 
   video: new TestEntryInfo({
@@ -542,7 +542,7 @@
     lastModifiedTime: 'Jan 14, 2019, 16:01 PM',
     nameText: 'video_long.ogv',
     sizeText: '166 KB',
-    typeText: 'OGG video'
+    typeText: 'OGG video',
   }),
 
   subtitle: new TestEntryInfo({
@@ -553,7 +553,7 @@
     lastModifiedTime: 'Feb 7, 2019, 15:03 PM',
     nameText: 'world.vtt',
     sizeText: '46 bytes',
-    typeText: 'VTT text'
+    typeText: 'VTT text',
   }),
 
   unsupported: new TestEntryInfo({
@@ -564,7 +564,7 @@
     lastModifiedTime: 'Jul 4, 2012, 10:36 AM',
     nameText: 'unsupported.foo',
     sizeText: '8 KB',
-    typeText: 'FOO file'
+    typeText: 'FOO file',
   }),
 
   desktop: new TestEntryInfo({
@@ -576,7 +576,7 @@
     lastModifiedTime: 'Jan 18, 2038, 1:02 AM',
     nameText: 'My Desktop Background.png',
     sizeText: '272 bytes',
-    typeText: 'PNG image'
+    typeText: 'PNG image',
   }),
 
   image2: new TestEntryInfo({
@@ -588,7 +588,7 @@
     lastModifiedTime: 'Jan 18, 2038, 1:02 AM',
     nameText: 'image2',
     sizeText: '4 KB',
-    typeText: 'PNG image'
+    typeText: 'PNG image',
   }),
 
   image3: new TestEntryInfo({
@@ -599,7 +599,7 @@
     lastModifiedTime: 'Jan 18, 2038, 1:02 AM',
     nameText: 'image3.jpg',
     sizeText: '3 KB',
-    typeText: 'JPEG image'
+    typeText: 'JPEG image',
   }),
 
   smallJpeg: new TestEntryInfo({
@@ -610,7 +610,7 @@
     lastModifiedTime: 'Jan 18, 2038, 1:02 AM',
     nameText: 'small.jpg',
     sizeText: '1 KB',
-    typeText: 'JPEG image'
+    typeText: 'JPEG image',
   }),
 
   // Used to differentiate between .jpg and .jpeg handling.
@@ -622,7 +622,7 @@
     lastModifiedTime: 'Jan 18, 2038, 1:02 AM',
     nameText: 'sample.jpeg',
     sizeText: '1 KB',
-    typeText: 'JPEG image'
+    typeText: 'JPEG image',
   }),
 
   brokenJpeg: new TestEntryInfo({
@@ -633,7 +633,7 @@
     lastModifiedTime: 'Jan 18, 2038, 1:02 AM',
     nameText: 'broken.jpg',
     sizeText: '1 byte',
-    typeText: 'JPEG image'
+    typeText: 'JPEG image',
   }),
 
   exifImage: new TestEntryInfo({
@@ -644,7 +644,7 @@
     lastModifiedTime: 'Jan 18, 2038, 1:02 AM',
     nameText: 'exif.jpg',
     sizeText: '31 KB',
-    typeText: 'JPEG image'
+    typeText: 'JPEG image',
   }),
 
   webpImage: new TestEntryInfo({
@@ -655,7 +655,7 @@
     lastModifiedTime: 'Jan 19, 2021, 1:10 PM',
     nameText: 'image.webp',
     sizeText: '5 KB',
-    typeText: 'WebP image'
+    typeText: 'WebP image',
   }),
 
   rawImage: new TestEntryInfo({
@@ -666,7 +666,7 @@
     lastModifiedTime: 'May 20, 2019, 10:10 AM',
     nameText: 'raw.orf',
     sizeText: '214 KB',
-    typeText: 'ORF image'
+    typeText: 'ORF image',
   }),
 
   nefImage: new TestEntryInfo({
@@ -677,7 +677,7 @@
     lastModifiedTime: 'May 9, 2015, 11:16 PM',
     nameText: 'raw.nef',
     sizeText: '92 KB',
-    typeText: 'NEF image'
+    typeText: 'NEF image',
   }),
 
   beautiful: new TestEntryInfo({
@@ -688,7 +688,7 @@
     lastModifiedTime: 'Nov 12, 2086, 12:00 PM',
     nameText: 'Beautiful Song.ogg',
     sizeText: '14 KB',
-    typeText: 'OGG audio'
+    typeText: 'OGG audio',
   }),
 
   movFile: new TestEntryInfo({
@@ -698,7 +698,7 @@
     lastModifiedTime: 'Jul 4, 2012, 10:35 AM',
     nameText: 'mac.mov',
     sizeText: '875 bytes',
-    typeText: 'QuickTime video'
+    typeText: 'QuickTime video',
   }),
 
   docxFile: new TestEntryInfo({
@@ -710,7 +710,7 @@
     lastModifiedTime: 'Jul 4, 2038, 10:35 AM',
     nameText: 'word.docx',
     sizeText: '9 KB',
-    typeText: 'Word document'
+    typeText: 'Word document',
   }),
 
   photos: new TestEntryInfo({
@@ -719,7 +719,7 @@
     lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
     nameText: 'photos',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   testDocument: new TestEntryInfo({
@@ -729,7 +729,7 @@
     lastModifiedTime: 'Apr 10, 2013, 4:20 PM',
     nameText: 'Test Document.gdoc',
     sizeText: '--',
-    typeText: 'Google document'
+    typeText: 'Google document',
   }),
 
   testSharedDocument: new TestEntryInfo({
@@ -740,7 +740,7 @@
     lastModifiedTime: 'Mar 20, 2013, 10:40 PM',
     nameText: 'Test Shared Document.gdoc',
     sizeText: '--',
-    typeText: 'Google document'
+    typeText: 'Google document',
   }),
 
   testSharedFile: new TestEntryInfo({
@@ -753,7 +753,7 @@
     nameText: 'test.txt',
     sizeText: '51 bytes',
     typeText: 'Plain text',
-    pinned: true
+    pinned: true,
   }),
 
   sharedDirectory: new TestEntryInfo({
@@ -763,7 +763,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'Shared',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   sharedDirectoryFile: new TestEntryInfo({
@@ -775,7 +775,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'file.txt',
     sizeText: '51 bytes',
-    typeText: 'Plain text'
+    typeText: 'Plain text',
   }),
 
   newlyAdded: new TestEntryInfo({
@@ -786,7 +786,7 @@
     lastModifiedTime: 'Sep 4, 1998, 12:00 AM',
     nameText: 'newly added file.ogg',
     sizeText: '14 KB',
-    typeText: 'OGG audio'
+    typeText: 'OGG audio',
   }),
 
   tallText: new TestEntryInfo({
@@ -874,7 +874,7 @@
     lastModifiedTime: 'Jul 4, 2012, 10:35 AM',
     nameText: 'imgpdf',
     sizeText: '1608 bytes',
-    typeText: 'PDF document'
+    typeText: 'PDF document',
   }),
 
   smallDocx: new TestEntryInfo({
@@ -967,7 +967,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'A',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   directoryB: new TestEntryInfo({
@@ -976,7 +976,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'B',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   directoryC: new TestEntryInfo({
@@ -985,7 +985,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'C',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   directoryD: new TestEntryInfo({
@@ -994,7 +994,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'D',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   directoryE: new TestEntryInfo({
@@ -1003,7 +1003,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'E',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   directoryF: new TestEntryInfo({
@@ -1012,7 +1012,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'F',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   dotTrash: new TestEntryInfo({
@@ -1021,7 +1021,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: '.Trash',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   deeplyBurriedSmallJpeg: new TestEntryInfo({
@@ -1032,7 +1032,7 @@
     lastModifiedTime: 'Jan 18, 2038, 1:02 AM',
     nameText: 'deep.jpg',
     sizeText: '886 bytes',
-    typeText: 'JPEG image'
+    typeText: 'JPEG image',
   }),
 
   linkGtoB: new TestEntryInfo({
@@ -1042,7 +1042,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'G',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   linkHtoFile: new TestEntryInfo({
@@ -1053,7 +1053,7 @@
     lastModifiedTime: 'Jan 18, 2038, 1:02 AM',
     nameText: 'H.jpg',
     sizeText: '886 bytes',
-    typeText: 'JPEG image'
+    typeText: 'JPEG image',
   }),
 
   linkTtoTransitiveDirectory: new TestEntryInfo({
@@ -1063,7 +1063,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'T',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   zipArchive: new TestEntryInfo({
@@ -1074,7 +1074,7 @@
     lastModifiedTime: 'Jan 1, 2014, 1:00 AM',
     nameText: 'archive.zip',
     sizeText: '743 bytes',
-    typeText: 'ZIP archive'
+    typeText: 'ZIP archive',
   }),
 
   zipSJISArchive: new TestEntryInfo({
@@ -1085,7 +1085,7 @@
     lastModifiedTime: 'Apr 6, 2022, 1:00 AM',
     nameText: 'sjis.zip',
     sizeText: '479 bytes',
-    typeText: 'ZIP archive'
+    typeText: 'ZIP archive',
   }),
 
   zipExtArchive: new TestEntryInfo({
@@ -1096,7 +1096,7 @@
     lastModifiedTime: 'Apr 6, 2022, 1:00 AM',
     nameText: 'tera.zip',
     sizeText: '250 bytes',
-    typeText: 'ZIP archive'
+    typeText: 'ZIP archive',
   }),
 
   debPackage: new TestEntryInfo({
@@ -1107,7 +1107,7 @@
     lastModifiedTime: 'Jan 1, 2014, 1:00 AM',
     nameText: 'package.deb',
     sizeText: '724 bytes',
-    typeText: 'DEB file'
+    typeText: 'DEB file',
   }),
 
   tiniFile: new TestEntryInfo({
@@ -1118,7 +1118,7 @@
     lastModifiedTime: 'Jan 1, 2014, 1:00 AM',
     nameText: 'test.tini',
     sizeText: '439 bytes',
-    typeText: 'Crostini image file'
+    typeText: 'Crostini image file',
   }),
 
   hiddenFile: new TestEntryInfo({
@@ -1129,7 +1129,7 @@
     lastModifiedTime: 'Sep 30, 2014, 3:30 PM',
     nameText: '.hiddenfile.txt',
     sizeText: '51 bytes',
-    typeText: 'Plain text'
+    typeText: 'Plain text',
   }),
 
   // Team-drive entries.
@@ -1275,7 +1275,7 @@
     computerName: 'Computer A',
     nameText: 'A',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   // Read-only and write-restricted entries.
@@ -1296,7 +1296,7 @@
       canAddChildren: false,
       canRename: false,
       canDelete: false,
-      canShare: true
+      canShare: true,
     },
   }),
 
@@ -1315,7 +1315,7 @@
       canAddChildren: false,
       canRename: false,
       canDelete: false,
-      canShare: true
+      canShare: true,
     },
   }),
 
@@ -1333,7 +1333,7 @@
       canAddChildren: false,
       canRename: false,
       canDelete: false,
-      canShare: false
+      canShare: false,
     },
   }),
 
@@ -1353,7 +1353,7 @@
       canAddChildren: false,
       canRename: false,
       canDelete: false,
-      canShare: true
+      canShare: true,
     },
   }),
 
@@ -1374,7 +1374,7 @@
       canAddChildren: false,
       canRename: false,
       canDelete: false,
-      canShare: true
+      canShare: true,
     },
   }),
 
@@ -1392,7 +1392,7 @@
       canCopy: true,
       canAddChildren: false,
       canRename: false,
-      canDelete: true
+      canDelete: true,
     },
   }),
 
@@ -1410,7 +1410,7 @@
       canCopy: true,
       canAddChildren: false,
       canRename: true,
-      canDelete: false
+      canDelete: false,
     },
   }),
 
@@ -1427,7 +1427,7 @@
       canAddChildren: true,
       canRename: false,
       canDelete: false,
-      canShare: true
+      canShare: true,
     },
   }),
 
@@ -1443,7 +1443,7 @@
       canAddChildren: true,
       canRename: false,
       canDelete: false,
-      canShare: true
+      canShare: true,
     },
   }),
 
@@ -1459,7 +1459,7 @@
       canAddChildren: true,
       canRename: false,
       canDelete: false,
-      canShare: true
+      canShare: true,
     },
   }),
 
@@ -1475,7 +1475,7 @@
       canAddChildren: true,
       canRename: false,
       canDelete: false,
-      canShare: true
+      canShare: true,
     },
   }),
 
@@ -1499,7 +1499,7 @@
     lastModifiedTime: 'Jul 4, 2012, 10:35 AM',
     nameText: 'android.webm',
     sizeText: '17 KB',
-    typeText: 'WebM video'
+    typeText: 'WebM video',
   }),
 
   musicAudio: new TestEntryInfo({
@@ -1510,7 +1510,7 @@
     lastModifiedTime: 'Sep 4, 1998, 12:00 AM',
     nameText: 'android.ogg',
     sizeText: '14 KB',
-    typeText: 'OGG audio'
+    typeText: 'OGG audio',
   }),
 
   picturesImage: new TestEntryInfo({
@@ -1521,7 +1521,7 @@
     lastModifiedTime: 'Jan 18, 2012, 1:02 AM',
     nameText: 'android.jpg',
     sizeText: '3 KB',
-    typeText: 'JPEG image'
+    typeText: 'JPEG image',
   }),
 
   neverSync: new TestEntryInfo({
@@ -1532,7 +1532,7 @@
     lastModifiedTime: 'Sep 4, 1998, 12:34 PM',
     nameText: 'never-sync.txt',
     sizeText: '51 bytes',
-    typeText: 'Plain text'
+    typeText: 'Plain text',
   }),
 
   sharedWithMeDirectory: new TestEntryInfo({
@@ -1542,7 +1542,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'Shared Directory',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   sharedWithMeDirectoryFile: new TestEntryInfo({
@@ -1554,7 +1554,7 @@
     lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
     nameText: 'file.txt',
     sizeText: '51 bytes',
-    typeText: 'Plain text'
+    typeText: 'Plain text',
   }),
 
   crdownload: new TestEntryInfo({
@@ -1565,7 +1565,7 @@
     lastModifiedTime: 'Sep 4, 1998, 12:34 PM',
     nameText: 'hello.crdownload',
     sizeText: '51 bytes',
-    typeText: 'CRDOWNLOAD file'
+    typeText: 'CRDOWNLOAD file',
   }),
 
   pluginVm: new TestEntryInfo({
@@ -1574,7 +1574,7 @@
     lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
     nameText: 'Windows Files',
     sizeText: '--',
-    typeText: 'Folder'
+    typeText: 'Folder',
   }),
 
   invalidLastModifiedDate: new TestEntryInfo({
@@ -1584,7 +1584,7 @@
     mimeType: 'text/plain',
     nameText: 'invalidLastModifiedDate.txt',
     sizeText: '51 bytes',
-    typeText: 'Plain text'
+    typeText: 'Plain text',
   }),
 };
 
@@ -1605,7 +1605,7 @@
     sizeText: '51 bytes',
     typeText: 'Plain text',
     sourceFileName: 'text.txt',
-    mimeType: 'text/plain'
+    mimeType: 'text/plain',
   });
 }
 
diff --git a/ui/file_manager/integration_tests/testing_provider/background.js b/ui/file_manager/integration_tests/testing_provider/background.js
index dee1848b..89700999 100644
--- a/ui/file_manager/integration_tests/testing_provider/background.js
+++ b/ui/file_manager/integration_tests/testing_provider/background.js
@@ -12,20 +12,20 @@
     const index = mounted.length + 1;
     chrome.fileSystemProvider.mount({
       fileSystemId: 'test-fs-' + index,
-      displayName: 'Test (' + index + ')'
+      displayName: 'Test (' + index + ')',
     });
   });
 }
 
-chrome.fileSystemProvider.onGetMetadataRequested.addListener(
-    function(options, onSuccess, onError) {
-      onSuccess({
-        isDirectory: true,
-        name: '',
-        size: 0,
-        modificationTime: new Date()
-      });
-    });
+chrome.fileSystemProvider.onGetMetadataRequested.addListener(function(
+    options, onSuccess, onError) {
+  onSuccess({
+    isDirectory: true,
+    name: '',
+    size: 0,
+    modificationTime: new Date(),
+  });
+});
 
 chrome.fileSystemProvider.onReadDirectoryRequested.addListener(function(
     options, onSuccess, onError) {
diff --git a/ui/gtk/BUILD.gn b/ui/gtk/BUILD.gn
index 7f00a119..734f3ff 100644
--- a/ui/gtk/BUILD.gn
+++ b/ui/gtk/BUILD.gn
@@ -126,7 +126,6 @@
     "//ui/aura",
     "//ui/base",
     "//ui/base:buildflags",
-    "//ui/base/cursor:theme_manager",
     "//ui/base/ime",
     "//ui/base/ime/linux",
     "//ui/color",
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc
index 86ee61e..50c01fb4 100644
--- a/ui/gtk/gtk_ui.cc
+++ b/ui/gtk/gtk_ui.cc
@@ -16,8 +16,6 @@
 #include "base/containers/flat_map.h"
 #include "base/debug/leak_annotations.h"
 #include "base/environment.h"
-#include "base/files/dir_reader_linux.h"
-#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/nix/mime_util_xdg.h"
 #include "base/nix/xdg_util.h"
@@ -28,7 +26,6 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkShader.h"
-#include "ui/base/cursor/cursor_theme_manager_observer.h"
 #include "ui/base/glib/glib_cast.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/linux/fake_input_method_context.h"
@@ -64,6 +61,7 @@
 #include "ui/gtk/select_file_dialog_linux_gtk.h"
 #include "ui/gtk/settings_provider_gtk.h"
 #include "ui/gtk/window_frame_provider_gtk.h"
+#include "ui/linux/cursor_theme_manager_observer.h"
 #include "ui/linux/device_scale_factor_observer.h"
 #include "ui/linux/nav_button_provider.h"
 #include "ui/linux/window_button_order_observer.h"
@@ -72,6 +70,7 @@
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/shell_dialogs/select_file_dialog_linux.h"
 #include "ui/shell_dialogs/select_file_policy.h"
+#include "ui/shell_dialogs/shell_dialog_linux.h"
 #include "ui/views/window/window_button_order_provider.h"
 
 #if defined(USE_GIO)
@@ -202,23 +201,6 @@
   }
 }
 
-void ListGtkThemes(const base::FilePath path,
-                   std::vector<std::string>& themes) {
-  std::string gtk_version =
-      "gtk-" + base::NumberToString(GtkVersion().components()[0]) + ".0";
-  base::DirReaderLinux dir_reader(path.value().c_str());
-
-  if (!dir_reader.IsValid())
-    return;
-
-  while (dir_reader.Next()) {
-    std::string theme_name = dir_reader.name();
-    base::FilePath theme_path = path.Append(theme_name).Append(gtk_version);
-    if (base::PathExists(theme_path))
-      themes.emplace_back(theme_name);
-  }
-}
-
 }  // namespace
 
 GtkUi::GtkUi() : window_frame_actions_() {
@@ -229,6 +211,8 @@
 GtkUi::~GtkUi() {
   DCHECK_EQ(g_gtk_ui, this);
   g_gtk_ui = nullptr;
+
+  shell_dialog_linux::Finalize();
 }
 
 // static
@@ -257,7 +241,7 @@
 
   // This creates an extra thread that may race against GtkInitFromCommandLine,
   // so this must be done after to avoid the race condition.
-  ShellDialogLinux::Initialize();
+  shell_dialog_linux::Initialize();
 
   using Action = ui::LinuxUi::WindowFrameAction;
   using ActionSource = ui::LinuxUi::WindowFrameActionSource;
@@ -495,9 +479,11 @@
 }
 
 ui::SelectFileDialog* GtkUi::CreateSelectFileDialog(
-    ui::SelectFileDialog::Listener* listener,
+    void* listener,
     std::unique_ptr<ui::SelectFilePolicy> policy) const {
-  return new SelectFileDialogLinuxGtk(listener, std::move(policy));
+  return new SelectFileDialogLinuxGtk(
+      static_cast<ui::SelectFileDialog::Listener*>(listener),
+      std::move(policy));
 }
 
 ui::LinuxUi::WindowFrameAction GtkUi::GetWindowFrameAction(
@@ -615,23 +601,6 @@
   return size;
 }
 
-std::vector<std::string> GtkUi::GetAvailableSystemThemeNamesForTest() const {
-  std::vector<std::string> themes;
-  const gchar* const* dirs = g_get_system_data_dirs();
-  for (std::size_t i = 0; dirs[i]; i++)
-    ListGtkThemes(base::FilePath(dirs[i]).Append("themes"), themes);
-
-  return themes;
-}
-
-void GtkUi::SetSystemThemeByNameForTest(const std::string& theme_name) {
-  GValue theme = G_VALUE_INIT;
-  g_value_init(&theme, G_TYPE_STRING);
-  g_value_set_string(&theme, theme_name.c_str());
-  g_object_set_property(G_OBJECT(gtk_settings_get_default()), "gtk-theme-name",
-                        &theme);
-}
-
 ui::NativeTheme* GtkUi::GetNativeTheme() const {
   return native_theme_;
 }
diff --git a/ui/gtk/gtk_ui.h b/ui/gtk/gtk_ui.h
index 162fb07..5206acf 100644
--- a/ui/gtk/gtk_ui.h
+++ b/ui/gtk/gtk_ui.h
@@ -71,7 +71,7 @@
 
   // ui::ShellDialogLinux:
   ui::SelectFileDialog* CreateSelectFileDialog(
-      ui::SelectFileDialog::Listener* listener,
+      void* listener,
       std::unique_ptr<ui::SelectFilePolicy> policy) const override;
 
   // ui::LinuxUi:
@@ -97,8 +97,6 @@
   base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
   std::string GetCursorThemeName() override;
   int GetCursorThemeSize() override;
-  std::vector<std::string> GetAvailableSystemThemeNamesForTest() const override;
-  void SetSystemThemeByNameForTest(const std::string& theme_name) override;
   ui::NativeTheme* GetNativeTheme() const override;
 
   // ui::TextEditKeybindingDelegate:
diff --git a/ui/linux/BUILD.gn b/ui/linux/BUILD.gn
index ca135e1..0b23158 100644
--- a/ui/linux/BUILD.gn
+++ b/ui/linux/BUILD.gn
@@ -3,12 +3,12 @@
 # found in the LICENSE file.
 
 import("//build/config/linux/gtk/gtk.gni")
-import("//printing/buildflags/buildflags.gni")
 import("//ui/qt/qt.gni")
 
 component("linux_ui") {
   defines = [ "IS_LINUX_UI_IMPL" ]
   public = [
+    "cursor_theme_manager_observer.h",
     "device_scale_factor_observer.h",
     "linux_ui.h",
     "nav_button_provider.h",
@@ -24,15 +24,10 @@
     "//build:chromecast_buildflags",
     "//ui/base/ime/linux",
     "//ui/native_theme",
-    "//ui/shell_dialogs",
   ]
-  if (enable_basic_printing) {
-    deps += [ "//printing" ]
-  }
   public_deps = [
     "//printing/buildflags",
     "//skia",
-    "//ui/base/cursor:theme_manager",
   ]
 }
 
@@ -44,7 +39,10 @@
 
   public_deps = [ ":linux_ui" ]
 
-  deps = [ "//ui/base:buildflags" ]
+  deps = [
+    "//base",
+    "//ui/base:buildflags",
+  ]
   if (use_gtk) {
     # This is the only component that can interact with gtk.
     deps += [ "//ui/gtk" ]
diff --git a/ui/base/cursor/cursor_theme_manager_observer.h b/ui/linux/cursor_theme_manager_observer.h
similarity index 78%
rename from ui/base/cursor/cursor_theme_manager_observer.h
rename to ui/linux/cursor_theme_manager_observer.h
index 028a8bb..ab7e9f5 100644
--- a/ui/base/cursor/cursor_theme_manager_observer.h
+++ b/ui/linux/cursor_theme_manager_observer.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 UI_BASE_CURSOR_CURSOR_THEME_MANAGER_OBSERVER_H_
-#define UI_BASE_CURSOR_CURSOR_THEME_MANAGER_OBSERVER_H_
+#ifndef UI_LINUX_CURSOR_THEME_MANAGER_OBSERVER_H_
+#define UI_LINUX_CURSOR_THEME_MANAGER_OBSERVER_H_
 
 #include <string>
 
@@ -26,4 +26,4 @@
 
 }  // namespace ui
 
-#endif  // UI_BASE_CURSOR_CURSOR_THEME_MANAGER_OBSERVER_H_
+#endif  // UI_LINUX_CURSOR_THEME_MANAGER_OBSERVER_H_
diff --git a/ui/linux/linux_ui.cc b/ui/linux/linux_ui.cc
index 261dd9f..f2b5d11 100644
--- a/ui/linux/linux_ui.cc
+++ b/ui/linux/linux_ui.cc
@@ -11,6 +11,7 @@
 #include "base/environment.h"
 #include "base/nix/xdg_util.h"
 #include "build/build_config.h"
+#include "ui/linux/cursor_theme_manager_observer.h"
 #include "ui/native_theme/native_theme.h"
 
 namespace {
@@ -26,14 +27,7 @@
   g_linux_ui = instance.release();
 
   SkiaFontDelegate::SetInstance(g_linux_ui);
-#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
-  ShellDialogLinux::SetInstance(g_linux_ui);
-#endif
   ui::SetTextEditKeyBindingsDelegate(g_linux_ui);
-#if BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_PRINTING)
-  printing::PrintingContextLinuxDelegate::SetInstance(g_linux_ui);
-#endif
-  ui::CursorThemeManager::SetInstance(g_linux_ui);
   gfx::AnimationSettingsProviderLinux::SetInstance(g_linux_ui);
 
   // Do not set IME instance for ozone as we delegate creating the input method
@@ -77,6 +71,20 @@
   device_scale_factor_observer_list_.RemoveObserver(observer);
 }
 
+void LinuxUi::AddCursorThemeObserver(CursorThemeManagerObserver* observer) {
+  cursor_theme_observer_list_.AddObserver(observer);
+  std::string name = GetCursorThemeName();
+  if (!name.empty())
+    observer->OnCursorThemeNameChanged(name);
+  int size = GetCursorThemeSize();
+  if (size)
+    observer->OnCursorThemeSizeChanged(size);
+}
+
+void LinuxUi::RemoveCursorThemeObserver(CursorThemeManagerObserver* observer) {
+  cursor_theme_observer_list_.RemoveObserver(observer);
+}
+
 ui::NativeTheme* LinuxUi::GetNativeTheme(aura::Window* window) const {
   return GetNativeTheme(use_system_theme_callback_.is_null() ||
                         use_system_theme_callback_.Run(window));
diff --git a/ui/linux/linux_ui.h b/ui/linux/linux_ui.h
index bf0bdd18..0cd8a0e 100644
--- a/ui/linux/linux_ui.h
+++ b/ui/linux/linux_ui.h
@@ -13,24 +13,18 @@
 #include "base/command_line.h"
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
+#include "base/observer_list.h"
 #include "build/buildflag.h"
 #include "build/chromecast_buildflags.h"
 #include "printing/buildflags/buildflags.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "ui/base/cursor/cursor_theme_manager.h"
 #include "ui/base/ime/linux/linux_input_method_context_factory.h"
 #include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h"
 #include "ui/gfx/animation/animation_settings_provider_linux.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/gfx/skia_font_delegate.h"
 
-#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
-#include "ui/shell_dialogs/shell_dialog_linux.h"
-#endif
-
-#if BUILDFLAG(ENABLE_PRINTING)
-#include "printing/printing_context_linux.h"  // nogncheck
-#endif
-
 // The main entrypoint into Linux toolkit specific code. GTK/QT code should only
 // be executed behind this interface.
 
@@ -46,11 +40,19 @@
 class Image;
 }
 
+namespace printing {
+class PrintingContextLinux;
+class PrintDialogLinuxInterface;
+}  // namespace printing
+
 namespace ui {
 
+class CursorThemeManagerObserver;
 class DeviceScaleFactorObserver;
 class NativeTheme;
 class NavButtonProvider;
+class SelectFileDialog;
+class SelectFilePolicy;
 class WindowButtonOrderObserver;
 class WindowFrameProvider;
 
@@ -59,14 +61,7 @@
 class COMPONENT_EXPORT(LINUX_UI) LinuxUi
     : public ui::LinuxInputMethodContextFactory,
       public gfx::SkiaFontDelegate,
-#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
-      public ui::ShellDialogLinux,
-#endif
-#if BUILDFLAG(ENABLE_PRINTING)
-      public printing::PrintingContextLinuxDelegate,
-#endif
       public ui::TextEditKeyBindingsDelegateAuraLinux,
-      public ui::CursorThemeManager,
       public gfx::AnimationSettingsProviderLinux {
  public:
   using UseSystemThemeCallback =
@@ -118,6 +113,12 @@
   // factor.
   void RemoveDeviceScaleFactorObserver(DeviceScaleFactorObserver* observer);
 
+  // Adds |observer| and makes initial OnCursorThemNameChanged() and/or
+  // OnCursorThemeSizeChanged() calls if the respective settings were set.
+  void AddCursorThemeObserver(CursorThemeManagerObserver* observer);
+
+  void RemoveCursorThemeObserver(CursorThemeManagerObserver* observer);
+
   // Returns the NativeTheme that reflects the theme used by `window`.
   ui::NativeTheme* GetNativeTheme(aura::Window* window) const;
 
@@ -180,12 +181,28 @@
   // Returns a map of KeyboardEvent code to KeyboardEvent key values.
   virtual base::flat_map<std::string, std::string> GetKeyboardLayoutMap() = 0;
 
-  // Returns the names of available system themes. Used only in test.
-  virtual std::vector<std::string> GetAvailableSystemThemeNamesForTest()
-      const = 0;
+#if BUILDFLAG(ENABLE_PRINTING)
+  virtual printing::PrintDialogLinuxInterface* CreatePrintDialog(
+      printing::PrintingContextLinux* context) = 0;
 
-  // Set the system theme by name. Used only in test.
-  virtual void SetSystemThemeByNameForTest(const std::string& theme_name) = 0;
+  virtual gfx::Size GetPdfPaperSize(
+      printing::PrintingContextLinux* context) = 0;
+#endif
+
+  // Returns a native file selection dialog.  `listener` is of type
+  // SelectFileDialog::Listener.  TODO(thomasanderson): Move
+  // SelectFileDialog::Listener to SelectFileDialogListener so that it can be
+  // forward declared.
+  virtual SelectFileDialog* CreateSelectFileDialog(
+      void* listener,
+      std::unique_ptr<SelectFilePolicy> policy) const = 0;
+
+  // Returns the prefererd theme name for cursor loading.
+  virtual std::string GetCursorThemeName() = 0;
+
+  // Returns the preferred size for cursor bitmaps.  A value of 64 indicates
+  // that 64x64 px bitmaps are preferred.
+  virtual int GetCursorThemeSize() = 0;
 
  protected:
   struct CmdLineArgs {
@@ -218,6 +235,11 @@
     return device_scale_factor_observer_list_;
   }
 
+  const base::ObserverList<CursorThemeManagerObserver>&
+  cursor_theme_observers() {
+    return cursor_theme_observer_list_;
+  }
+
   virtual ui::NativeTheme* GetNativeTheme() const = 0;
 
  private:
@@ -233,6 +255,9 @@
   // Objects to notify when the device scale factor changes.
   base::ObserverList<DeviceScaleFactorObserver>::Unchecked
       device_scale_factor_observer_list_;
+
+  // Objects to notify when the cursor theme or size changes.
+  base::ObserverList<CursorThemeManagerObserver> cursor_theme_observer_list_;
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index c27fbc30..b386d96 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -269,7 +269,6 @@
     "//ui/base:data_exchange",
     "//ui/base:features",
     "//ui/base/cursor",
-    "//ui/base/cursor:theme_manager",
     "//ui/base/cursor/mojom:cursor_type",
     "//ui/base/dragdrop:types",
     "//ui/base/dragdrop/mojom",
@@ -295,6 +294,10 @@
     "//ui/platform_window/wm",
   ]
 
+  if (is_linux) {
+    deps += [ "//ui/linux:linux_ui" ]
+  }
+
   if (use_dbus) {
     sources += [
       "host/org_gnome_mutter_idle_monitor.cc",
diff --git a/ui/ozone/platform/wayland/DEPS b/ui/ozone/platform/wayland/DEPS
index ea0ca3d0..39c1c91 100644
--- a/ui/ozone/platform/wayland/DEPS
+++ b/ui/ozone/platform/wayland/DEPS
@@ -2,4 +2,5 @@
   "+mojo/public",
   "+third_party/wayland",
   "+ui/base",
+  "+ui/linux",
 ]
diff --git a/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc b/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc
index 43986b7..02e8a62 100644
--- a/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc
+++ b/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc
@@ -45,9 +45,9 @@
 WaylandCursorFactory::~WaylandCursorFactory() = default;
 
 void WaylandCursorFactory::ObserveThemeChanges() {
-  auto* cursor_theme_manager = CursorThemeManager::GetInstance();
-  DCHECK(cursor_theme_manager);
-  cursor_theme_observer_.Observe(cursor_theme_manager);
+  auto* linux_ui = LinuxUi::instance();
+  DCHECK(linux_ui);
+  cursor_theme_observer_.Observe(linux_ui);
 }
 
 scoped_refptr<PlatformCursor> WaylandCursorFactory::GetDefaultCursor(
diff --git a/ui/ozone/platform/wayland/host/wayland_cursor_factory.h b/ui/ozone/platform/wayland/host/wayland_cursor_factory.h
index bbd2a98..9542bf4d 100644
--- a/ui/ozone/platform/wayland/host/wayland_cursor_factory.h
+++ b/ui/ozone/platform/wayland/host/wayland_cursor_factory.h
@@ -12,8 +12,8 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/scoped_observation.h"
-#include "ui/base/cursor/cursor_theme_manager.h"
-#include "ui/base/cursor/cursor_theme_manager_observer.h"
+#include "ui/linux/cursor_theme_manager_observer.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/ozone/common/bitmap_cursor_factory.h"
 #include "ui/ozone/platform/wayland/common/wayland_object.h"
 #include "ui/ozone/platform/wayland/host/wayland_cursor.h"
@@ -73,7 +73,10 @@
 
   const raw_ptr<WaylandConnection> connection_;
 
-  base::ScopedObservation<CursorThemeManager, CursorThemeManagerObserver>
+  base::ScopedObservation<LinuxUi,
+                          CursorThemeManagerObserver,
+                          &LinuxUi::AddCursorThemeObserver,
+                          &LinuxUi::RemoveCursorThemeObserver>
       cursor_theme_observer_{this};
 
   // Name of the current theme.
diff --git a/ui/qt/qt_ui.cc b/ui/qt/qt_ui.cc
index 4200b42c..357d99db 100644
--- a/ui/qt/qt_ui.cc
+++ b/ui/qt/qt_ui.cc
@@ -32,6 +32,7 @@
 #include "ui/native_theme/native_theme_base.h"
 #include "ui/qt/qt_interface.h"
 #include "ui/shell_dialogs/select_file_policy.h"
+#include "ui/shell_dialogs/shell_dialog_linux.h"
 #include "ui/views/controls/button/label_button_border.h"
 
 namespace qt {
@@ -118,7 +119,9 @@
 QtUi::QtUi(std::unique_ptr<ui::LinuxUi> fallback_linux_ui)
     : fallback_linux_ui_(std::move(fallback_linux_ui)) {}
 
-QtUi::~QtUi() = default;
+QtUi::~QtUi() {
+  shell_dialog_linux::Finalize();
+}
 
 std::unique_ptr<ui::LinuxInputMethodContext> QtUi::CreateInputMethodContext(
     ui::LinuxInputMethodContextDelegate* delegate) const {
@@ -149,7 +152,7 @@
 }
 
 ui::SelectFileDialog* QtUi::CreateSelectFileDialog(
-    ui::SelectFileDialog::Listener* listener,
+    void* listener,
     std::unique_ptr<ui::SelectFilePolicy> policy) const {
   return fallback_linux_ui_ ? fallback_linux_ui_->CreateSelectFileDialog(
                                   listener, std::move(policy))
@@ -174,6 +177,7 @@
   ui::ColorProviderManager::Get().AppendColorProviderInitializer(
       base::BindRepeating(&QtUi::AddNativeColorMixer, base::Unretained(this)));
   FontChanged();
+  shell_dialog_linux::Initialize();
 
   return true;
 }
@@ -294,20 +298,6 @@
   return 0;
 }
 
-std::vector<std::string> QtUi::GetAvailableSystemThemeNamesForTest() const {
-  // In QT, themes are binary plugins that are loaded on start and can't be
-  // changed at runtime.  The style may change, but there's no common interface
-  // for doing this from a client.  Return a single empty theme here to
-  // represent the current theme.
-  return {std::string()};
-}
-
-void QtUi::SetSystemThemeByNameForTest(const std::string& theme_name) {
-  // Ensure we only get passed the "current theme" name that we returned from
-  // GetAvailableSystemThemeNamesForTest() above.
-  DCHECK(theme_name.empty());
-}
-
 ui::NativeTheme* QtUi::GetNativeTheme() const {
   return native_theme_.get();
 }
diff --git a/ui/qt/qt_ui.h b/ui/qt/qt_ui.h
index c45b2ae..5430ceaa 100644
--- a/ui/qt/qt_ui.h
+++ b/ui/qt/qt_ui.h
@@ -48,7 +48,7 @@
 
   // ui::ShellDialogLinux:
   ui::SelectFileDialog* CreateSelectFileDialog(
-      ui::SelectFileDialog::Listener* listener,
+      void* listener,
       std::unique_ptr<ui::SelectFilePolicy> policy) const override;
 
   // ui::LinuxUi:
@@ -74,8 +74,6 @@
   base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
   std::string GetCursorThemeName() override;
   int GetCursorThemeSize() override;
-  std::vector<std::string> GetAvailableSystemThemeNamesForTest() const override;
-  void SetSystemThemeByNameForTest(const std::string& theme_name) override;
   ui::NativeTheme* GetNativeTheme() const override;
 
   // ui::TextEditKeybindingDelegate:
diff --git a/ui/shell_dialogs/BUILD.gn b/ui/shell_dialogs/BUILD.gn
index ae6c1b07..72e7324 100644
--- a/ui/shell_dialogs/BUILD.gn
+++ b/ui/shell_dialogs/BUILD.gn
@@ -54,7 +54,10 @@
       "shell_dialog_linux.cc",
       "shell_dialog_linux.h",
     ]
-    deps += [ "//components/dbus/thread_linux" ]
+    deps += [
+      "//components/dbus/thread_linux",
+      "//ui/linux:linux_ui",
+    ]
     if (use_dbus) {
       defines += [ "USE_DBUS" ]
       sources += [
diff --git a/ui/shell_dialogs/DEPS b/ui/shell_dialogs/DEPS
index abbefa1a..9d19ead4 100644
--- a/ui/shell_dialogs/DEPS
+++ b/ui/shell_dialogs/DEPS
@@ -2,14 +2,15 @@
   "+chromeos/crosapi",
   "+chromeos/lacros",
   "+components/dbus/thread_linux",
-  "+dbus",
   "+components/remote_cocoa",
+  "+dbus",
   "+mojo/core/embedder",
   "+mojo/public/cpp/bindings",
   "+ui/android/window_android.h",
   "+ui/aura",
   "+ui/base",
   "+ui/gfx",
+  "+ui/linux",
   "+ui/platform_window",
   "+ui/strings/grit/ui_strings.h",
 ]
diff --git a/ui/shell_dialogs/select_file_dialog.h b/ui/shell_dialogs/select_file_dialog.h
index 8a417fc4..957ef752b 100644
--- a/ui/shell_dialogs/select_file_dialog.h
+++ b/ui/shell_dialogs/select_file_dialog.h
@@ -90,7 +90,7 @@
     virtual void FileSelectionCanceled(void* params) {}
 
    protected:
-    virtual ~Listener() {}
+    virtual ~Listener() = default;
   };
 
   // Sets the factory that creates SelectFileDialog objects, overriding default
diff --git a/ui/shell_dialogs/shell_dialog_linux.cc b/ui/shell_dialogs/shell_dialog_linux.cc
index 078fd3b..65ac31c3 100644
--- a/ui/shell_dialogs/shell_dialog_linux.cc
+++ b/ui/shell_dialogs/shell_dialog_linux.cc
@@ -8,6 +8,7 @@
 #include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "build/chromeos_buildflags.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/shell_dialogs/select_file_dialog_linux.h"
 #include "ui/shell_dialogs/select_file_dialog_linux_kde.h"
 #include "ui/shell_dialogs/select_file_policy.h"
@@ -16,12 +17,26 @@
 #include "ui/shell_dialogs/select_file_dialog_linux_portal.h"
 #endif
 
+namespace shell_dialog_linux {
+
+void Initialize() {
+#if defined(USE_DBUS)
+  ui::SelectFileDialogLinuxPortal::StartAvailabilityTestInBackground();
+#endif
+}
+
+void Finalize() {
+#if defined(USE_DBUS)
+  ui::SelectFileDialogLinuxPortal::DestroyPortalConnection();
+#endif
+}
+
+}  // namespace shell_dialog_linux
+
 namespace ui {
 
 namespace {
 
-ShellDialogLinux* g_shell_dialog_linux = nullptr;
-
 enum FileDialogChoice {
   kUnknown,
   kToolkit,
@@ -69,40 +84,18 @@
 
 }  // namespace
 
-ShellDialogLinux::ShellDialogLinux() = default;
-
-ShellDialogLinux::~ShellDialogLinux() {
-#if defined(USE_DBUS)
-  SelectFileDialogLinuxPortal::DestroyPortalConnection();
-#endif
-}
-
-void ShellDialogLinux::SetInstance(ShellDialogLinux* instance) {
-  g_shell_dialog_linux = instance;
-}
-
-const ShellDialogLinux* ShellDialogLinux::instance() {
-  return g_shell_dialog_linux;
-}
-
-void ShellDialogLinux::Initialize() {
-#if defined(USE_DBUS)
-  SelectFileDialogLinuxPortal::StartAvailabilityTestInBackground();
-#endif
-}
-
 SelectFileDialog* CreateSelectFileDialog(
     SelectFileDialog::Listener* listener,
     std::unique_ptr<SelectFilePolicy> policy) {
   if (dialog_choice_ == kUnknown)
     dialog_choice_ = GetFileDialogChoice();
 
-  const ShellDialogLinux* shell_dialogs = ShellDialogLinux::instance();
+  const LinuxUi* linux_ui = LinuxUi::instance();
   switch (dialog_choice_) {
     case kToolkit:
-      if (!shell_dialogs)
+      if (!linux_ui)
         break;
-      return shell_dialogs->CreateSelectFileDialog(listener, std::move(policy));
+      return linux_ui->CreateSelectFileDialog(listener, std::move(policy));
 #if defined(USE_DBUS)
     case kPortal:
       return new SelectFileDialogLinuxPortal(listener, std::move(policy));
diff --git a/ui/shell_dialogs/shell_dialog_linux.h b/ui/shell_dialogs/shell_dialog_linux.h
index 531944c..b9a64da 100644
--- a/ui/shell_dialogs/shell_dialog_linux.h
+++ b/ui/shell_dialogs/shell_dialog_linux.h
@@ -5,39 +5,17 @@
 #ifndef UI_SHELL_DIALOGS_SHELL_DIALOG_LINUX_H_
 #define UI_SHELL_DIALOGS_SHELL_DIALOG_LINUX_H_
 
-#include "ui/shell_dialogs/select_file_dialog.h"
 #include "ui/shell_dialogs/shell_dialogs_export.h"
 
-namespace ui {
+namespace shell_dialog_linux {
 
-// An interface that lets different Linux platforms override the
-// CreateSelectFileDialog function declared here to return native file dialogs.
-class SHELL_DIALOGS_EXPORT ShellDialogLinux {
- public:
-  ShellDialogLinux();
-  virtual ~ShellDialogLinux();
+// TODO(thomasanderson): Remove Initialize() and Finalize().
 
-  // Sets the dynamically loaded singleton that draws the desktop native
-  // UI. This pointer is not owned, and if this method is called a second time,
-  // the first instance is not deleted.
-  static void SetInstance(ShellDialogLinux* instance);
+// Should be called before the first call to CreateSelectFileDialog.
+SHELL_DIALOGS_EXPORT void Initialize();
 
-  // Returns a LinuxUI instance for the toolkit used in the user's desktop
-  // environment.
-  //
-  // Can return NULL, in case no toolkit has been set. (For example, if we're
-  // running with the "--ash" flag.)
-  static const ShellDialogLinux* instance();
+SHELL_DIALOGS_EXPORT void Finalize();
 
-  // Should be called before the first call to CreateSelectFileDialog.
-  void Initialize();
-
-  // Returns a native file selection dialog.
-  virtual SelectFileDialog* CreateSelectFileDialog(
-      SelectFileDialog::Listener* listener,
-      std::unique_ptr<SelectFilePolicy> policy) const = 0;
-};
-
-}  // namespace ui
+}  // namespace shell_dialog_linux
 
 #endif  // UI_SHELL_DIALOGS_SHELL_DIALOG_LINUX_H_
diff --git a/ui/views/OWNERS b/ui/views/OWNERS
index bd1791d..bbe15824 100644
--- a/ui/views/OWNERS
+++ b/ui/views/OWNERS
@@ -4,7 +4,9 @@
 # 5 minutes. This approach helps our team to load-balance incoming reviews.
 # Googlers can read more about this at go/gwsq-gerrit.
 
+elainechien@chromium.org
 ellyjones@chromium.org
+kerenzhu@chromium.org
 kylixrd@chromium.org
 msw@chromium.org
 pbos@chromium.org